Merge "Add LOCAL_LICENSE_KINDS to build/soong"
diff --git a/android/apex.go b/android/apex.go
index b87ff09..6bb0751 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -749,74 +749,69 @@
 	}
 	return list
 }(map[string]int{
-	"adbd":                  30,
-	"android.net.ipsec.ike": 30,
-	"androidx-constraintlayout_constraintlayout-solver": 30,
-	"androidx.annotation_annotation":                    28,
-	"androidx.arch.core_core-common":                    28,
-	"androidx.collection_collection":                    28,
-	"androidx.lifecycle_lifecycle-common":               28,
-	"apache-commons-compress":                           29,
-	"bouncycastle_ike_digests":                          30,
-	"brotli-java":                                       29,
-	"captiveportal-lib":                                 28,
-	"flatbuffer_headers":                                30,
-	"framework-permission":                              30,
-	"framework-statsd":                                  30,
-	"gemmlowp_headers":                                  30,
-	"ike-internals":                                     30,
-	"kotlinx-coroutines-android":                        28,
-	"kotlinx-coroutines-core":                           28,
-	"libadb_crypto":                                     30,
-	"libadb_pairing_auth":                               30,
-	"libadb_pairing_connection":                         30,
-	"libadb_pairing_server":                             30,
-	"libadb_protos":                                     30,
-	"libadb_tls_connection":                             30,
-	"libadbconnection_client":                           30,
-	"libadbconnection_server":                           30,
-	"libadbd_core":                                      30,
-	"libadbd_services":                                  30,
-	"libadbd":                                           30,
-	"libapp_processes_protos_lite":                      30,
-	"libasyncio":                                        30,
-	"libbrotli":                                         30,
-	"libbuildversion":                                   30,
-	"libcrypto_static":                                  30,
-	"libcrypto_utils":                                   30,
-	"libdiagnose_usb":                                   30,
-	"libeigen":                                          30,
-	"liblz4":                                            30,
-	"libmdnssd":                                         30,
-	"libneuralnetworks_common":                          30,
-	"libneuralnetworks_headers":                         30,
-	"libneuralnetworks":                                 30,
-	"libprocpartition":                                  30,
-	"libprotobuf-java-lite":                             30,
-	"libprotoutil":                                      30,
-	"libqemu_pipe":                                      30,
-	"libstats_jni":                                      30,
-	"libstatslog_statsd":                                30,
-	"libstatsmetadata":                                  30,
-	"libstatspull":                                      30,
-	"libstatssocket":                                    30,
-	"libsync":                                           30,
-	"libtextclassifier_hash_headers":                    30,
-	"libtextclassifier_hash_static":                     30,
-	"libtflite_kernel_utils":                            30,
-	"libwatchdog":                                       29,
-	"libzstd":                                           30,
-	"metrics-constants-protos":                          28,
-	"net-utils-framework-common":                        29,
-	"permissioncontroller-statsd":                       28,
-	"philox_random_headers":                             30,
-	"philox_random":                                     30,
-	"service-permission":                                30,
-	"service-statsd":                                    30,
-	"statsd-aidl-ndk_platform":                          30,
-	"statsd":                                            30,
-	"tensorflow_headers":                                30,
-	"xz-java":                                           29,
+	"adbd":                           30,
+	"android.net.ipsec.ike":          30,
+	"apache-commons-compress":        29,
+	"bouncycastle_ike_digests":       30,
+	"brotli-java":                    29,
+	"captiveportal-lib":              28,
+	"flatbuffer_headers":             30,
+	"framework-permission":           30,
+	"framework-statsd":               30,
+	"gemmlowp_headers":               30,
+	"ike-internals":                  30,
+	"kotlinx-coroutines-android":     28,
+	"kotlinx-coroutines-core":        28,
+	"libadb_crypto":                  30,
+	"libadb_pairing_auth":            30,
+	"libadb_pairing_connection":      30,
+	"libadb_pairing_server":          30,
+	"libadb_protos":                  30,
+	"libadb_tls_connection":          30,
+	"libadbconnection_client":        30,
+	"libadbconnection_server":        30,
+	"libadbd_core":                   30,
+	"libadbd_services":               30,
+	"libadbd":                        30,
+	"libapp_processes_protos_lite":   30,
+	"libasyncio":                     30,
+	"libbrotli":                      30,
+	"libbuildversion":                30,
+	"libcrypto_static":               30,
+	"libcrypto_utils":                30,
+	"libdiagnose_usb":                30,
+	"libeigen":                       30,
+	"liblz4":                         30,
+	"libmdnssd":                      30,
+	"libneuralnetworks_common":       30,
+	"libneuralnetworks_headers":      30,
+	"libneuralnetworks":              30,
+	"libprocpartition":               30,
+	"libprotobuf-java-lite":          30,
+	"libprotoutil":                   30,
+	"libqemu_pipe":                   30,
+	"libstats_jni":                   30,
+	"libstatslog_statsd":             30,
+	"libstatsmetadata":               30,
+	"libstatspull":                   30,
+	"libstatssocket":                 30,
+	"libsync":                        30,
+	"libtextclassifier_hash_headers": 30,
+	"libtextclassifier_hash_static":  30,
+	"libtflite_kernel_utils":         30,
+	"libwatchdog":                    29,
+	"libzstd":                        30,
+	"metrics-constants-protos":       28,
+	"net-utils-framework-common":     29,
+	"permissioncontroller-statsd":    28,
+	"philox_random_headers":          30,
+	"philox_random":                  30,
+	"service-permission":             30,
+	"service-statsd":                 30,
+	"statsd-aidl-ndk_platform":       30,
+	"statsd":                         30,
+	"tensorflow_headers":             30,
+	"xz-java":                        29,
 })
 
 // Function called while walking an APEX's payload dependencies.
diff --git a/android/api_levels.go b/android/api_levels.go
index 1b53f3f..2f6a9d2 100644
--- a/android/api_levels.go
+++ b/android/api_levels.go
@@ -24,6 +24,8 @@
 	RegisterSingletonType("api_levels", ApiLevelsSingleton)
 }
 
+const previewAPILevelBase = 9000
+
 // An API level, which may be a finalized (numbered) API, a preview (codenamed)
 // API, or the future API level (10000). Can be parsed from a string with
 // ApiLevelFromUser or ApiLevelOrPanic.
@@ -57,6 +59,21 @@
 	}
 }
 
+// FinalOrPreviewInt distinguishes preview versions from "current" (future).
+// This is for "native" stubs and should be in sync with ndkstubgen/getApiLevelsMap().
+// - "current" -> future (10000)
+// - preview codenames -> preview base (9000) + index
+// - otherwise -> cast to int
+func (this ApiLevel) FinalOrPreviewInt() int {
+	if this.IsCurrent() {
+		return this.number
+	}
+	if this.IsPreview() {
+		return previewAPILevelBase + this.number
+	}
+	return this.number
+}
+
 // Returns the canonical name for this API level. For a finalized API level
 // this will be the API number as a string. For a preview API level this
 // will be the codename, or "current".
@@ -282,7 +299,6 @@
 
 func getApiLevelsMap(config Config) map[string]int {
 	return config.Once(apiLevelsMapKey, func() interface{} {
-		baseApiLevel := 9000
 		apiLevelsMap := map[string]int{
 			"G":     9,
 			"I":     14,
@@ -302,7 +318,7 @@
 			"R":     30,
 		}
 		for i, codename := range config.PlatformVersionActiveCodenames() {
-			apiLevelsMap[codename] = baseApiLevel + i
+			apiLevelsMap[codename] = previewAPILevelBase + i
 		}
 
 		return apiLevelsMap
diff --git a/android/arch.go b/android/arch.go
index baee9be..b277381 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -436,7 +436,7 @@
 	// blueprint.BottomUpMutatorContext because android.BottomUpMutatorContext
 	// filters out non-Soong modules.  Now that we've handled them, create a
 	// normal android.BottomUpMutatorContext.
-	mctx := bottomUpMutatorContextFactory(bpctx, module, false)
+	mctx := bottomUpMutatorContextFactory(bpctx, module, false, false)
 
 	base := module.base()
 
@@ -576,7 +576,7 @@
 	// blueprint.BottomUpMutatorContext because android.BottomUpMutatorContext
 	// filters out non-Soong modules.  Now that we've handled them, create a
 	// normal android.BottomUpMutatorContext.
-	mctx := bottomUpMutatorContextFactory(bpctx, module, false)
+	mctx := bottomUpMutatorContextFactory(bpctx, module, false, false)
 
 	base := module.base()
 
@@ -647,10 +647,11 @@
 	}
 
 	// Recovery is always the primary architecture, filter out any other architectures.
+	// Common arch is also allowed
 	if image == RecoveryVariation {
 		primaryArch := mctx.Config().DevicePrimaryArchType()
-		targets = filterToArch(targets, primaryArch)
-		multiTargets = filterToArch(multiTargets, primaryArch)
+		targets = filterToArch(targets, primaryArch, Common)
+		multiTargets = filterToArch(multiTargets, primaryArch, Common)
 	}
 
 	// If there are no supported targets disable the module.
@@ -719,10 +720,17 @@
 }
 
 // filterToArch takes a list of Targets and an ArchType, and returns a modified list that contains
-// only Targets that have the specified ArchType.
-func filterToArch(targets []Target, arch ArchType) []Target {
+// only Targets that have the specified ArchTypes.
+func filterToArch(targets []Target, archs ...ArchType) []Target {
 	for i := 0; i < len(targets); i++ {
-		if targets[i].Arch.ArchType != arch {
+		found := false
+		for _, arch := range archs {
+			if targets[i].Arch.ArchType == arch {
+				found = true
+				break
+			}
+		}
+		if !found {
 			targets = append(targets[:i], targets[i+1:]...)
 			i--
 		}
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index 4a25119..a5c4bed 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -234,6 +234,13 @@
 	cmdFlags = append(cmdFlags, labels...)
 	cmdFlags = append(cmdFlags, "--package_path=%workspace%/"+context.intermediatesDir())
 	cmdFlags = append(cmdFlags, "--profile="+shared.BazelMetricsFilename(context, runName))
+	// Set default platforms to canonicalized values for mixed builds requests. If these are set
+	// in the bazelrc, they will have values that are non-canonicalized, and thus be invalid.
+	// The actual platform values here may be overridden by configuration transitions from the buildroot.
+	cmdFlags = append(cmdFlags,
+		fmt.Sprintf("--platforms=%s", canonicalizeLabel("//build/bazel/platforms:generic_x86_64")))
+	cmdFlags = append(cmdFlags,
+		fmt.Sprintf("--extra_toolchains=%s", canonicalizeLabel("//prebuilts/clang/host/linux-x86:all")))
 	cmdFlags = append(cmdFlags, extraFlags...)
 
 	bazelCmd := exec.Command(context.bazelPath, cmdFlags...)
@@ -460,7 +467,6 @@
 		return err
 	}
 
-	fmt.Printf("Build statements %s", context.buildStatements)
 	// Clear requests.
 	context.requests = map[cqueryKey]bool{}
 	return nil
diff --git a/android/config.go b/android/config.go
index 8090889..50e39d7 100644
--- a/android/config.go
+++ b/android/config.go
@@ -1413,6 +1413,14 @@
 	return c.config.productVariables.VendorSnapshotModules
 }
 
+func (c *deviceConfig) DirectedRecoverySnapshot() bool {
+	return c.config.productVariables.DirectedRecoverySnapshot
+}
+
+func (c *deviceConfig) RecoverySnapshotModules() map[string]bool {
+	return c.config.productVariables.RecoverySnapshotModules
+}
+
 // The ConfiguredJarList struct provides methods for handling a list of (apex, jar) pairs.
 // Such lists are used in the build system for things like bootclasspath jars or system server jars.
 // The apex part is either an apex name, or a special names "platform" or "system_ext". Jar is a
@@ -1624,21 +1632,33 @@
 func splitConfiguredJarPair(str string) (string, string, error) {
 	pair := strings.SplitN(str, ":", 2)
 	if len(pair) == 2 {
-		return pair[0], pair[1], nil
+		apex := pair[0]
+		jar := pair[1]
+		if apex == "" {
+			return apex, jar, fmt.Errorf("invalid apex '%s' in <apex>:<jar> pair '%s', expected format: <apex>:<jar>", apex, str)
+		}
+		return apex, jar, nil
 	} else {
 		return "error-apex", "error-jar", fmt.Errorf("malformed (apex, jar) pair: '%s', expected format: <apex>:<jar>", str)
 	}
 }
 
-// CreateTestConfiguredJarList is a function to create ConfiguredJarList for
-// tests.
+// CreateTestConfiguredJarList is a function to create ConfiguredJarList for tests.
 func CreateTestConfiguredJarList(list []string) ConfiguredJarList {
-	apexes, jars, err := splitListOfPairsIntoPairOfLists(list)
+	// Create the ConfiguredJarList in as similar way as it is created at runtime by marshalling to
+	// a json list of strings and then unmarshalling into a ConfiguredJarList instance.
+	b, err := json.Marshal(list)
 	if err != nil {
 		panic(err)
 	}
 
-	return ConfiguredJarList{apexes, jars}
+	var jarList ConfiguredJarList
+	err = json.Unmarshal(b, &jarList)
+	if err != nil {
+		panic(err)
+	}
+
+	return jarList
 }
 
 // EmptyConfiguredJarList returns an empty jar list.
diff --git a/android/config_test.go b/android/config_test.go
index 7bfc800..a11115d 100644
--- a/android/config_test.go
+++ b/android/config_test.go
@@ -100,6 +100,22 @@
 		assertStringEquals(t, "apex1:jarA", list1.String())
 	})
 
+	t.Run("create invalid - missing apex", func(t *testing.T) {
+		defer func() {
+			err := recover().(error)
+			assertStringEquals(t, "malformed (apex, jar) pair: 'jarA', expected format: <apex>:<jar>", err.Error())
+		}()
+		CreateTestConfiguredJarList([]string{"jarA"})
+	})
+
+	t.Run("create invalid - empty apex", func(t *testing.T) {
+		defer func() {
+			err := recover().(error)
+			assertStringEquals(t, "invalid apex '' in <apex>:<jar> pair ':jarA', expected format: <apex>:<jar>", err.Error())
+		}()
+		CreateTestConfiguredJarList([]string{":jarA"})
+	})
+
 	list2 := list1.Append("apex2", "jarB")
 	t.Run("append", func(t *testing.T) {
 		assertStringEquals(t, "apex1:jarA,apex2:jarB", list2.String())
diff --git a/android/filegroup.go b/android/filegroup.go
index 3d1bbc5..674a196 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -17,8 +17,6 @@
 import (
 	"android/soong/bazel"
 	"strings"
-
-	"github.com/google/blueprint/proptools"
 )
 
 func init() {
@@ -28,8 +26,7 @@
 
 // https://docs.bazel.build/versions/master/be/general.html#filegroup
 type bazelFilegroupAttributes struct {
-	Name *string
-	Srcs []string
+	Srcs bazel.LabelList
 }
 
 type bazelFilegroup struct {
@@ -50,17 +47,19 @@
 
 func (bfg *bazelFilegroup) GenerateAndroidBuildActions(ctx ModuleContext) {}
 
-// TODO: Create helper functions to avoid this boilerplate.
 func FilegroupBp2Build(ctx TopDownMutatorContext) {
-	if m, ok := ctx.Module().(*fileGroup); ok {
-		name := "__bp2build__" + m.base().BaseModuleName()
-		ctx.CreateModule(BazelFileGroupFactory, &bazelFilegroupAttributes{
-			Name: proptools.StringPtr(name),
-			Srcs: m.properties.Srcs,
-		}, &bazel.BazelTargetModuleProperties{
-			Rule_class: "filegroup",
-		})
+	fg, ok := ctx.Module().(*fileGroup)
+	if !ok || !fg.properties.Bazel_module.Bp2build_available {
+		return
 	}
+
+	attrs := &bazelFilegroupAttributes{
+		Srcs: BazelLabelForModuleSrcExcludes(ctx, fg.properties.Srcs, fg.properties.Exclude_srcs),
+	}
+
+	props := bazel.NewBazelTargetModuleProperties(fg.Name(), "filegroup", "")
+
+	ctx.CreateBazelTargetModule(BazelFileGroupFactory, props, attrs)
 }
 
 type fileGroupProperties struct {
diff --git a/android/module.go b/android/module.go
index dcc2b84..bf74cad 100644
--- a/android/module.go
+++ b/android/module.go
@@ -2220,10 +2220,17 @@
 	}
 	var deps []dep
 	b.VisitDirectDepsBlueprint(func(module blueprint.Module) {
-		if aModule, _ := module.(Module); aModule != nil && aModule.base().BaseModuleName() == name {
-			returnedTag := b.bp.OtherModuleDependencyTag(aModule)
+		if aModule, _ := module.(Module); aModule != nil {
+			if aModule.base().BaseModuleName() == name {
+				returnedTag := b.bp.OtherModuleDependencyTag(aModule)
+				if tag == nil || returnedTag == tag {
+					deps = append(deps, dep{aModule, returnedTag})
+				}
+			}
+		} else if b.bp.OtherModuleName(module) == name {
+			returnedTag := b.bp.OtherModuleDependencyTag(module)
 			if tag == nil || returnedTag == tag {
-				deps = append(deps, dep{aModule, returnedTag})
+				deps = append(deps, dep{module, returnedTag})
 			}
 		}
 	})
@@ -2367,6 +2374,16 @@
 	return b.bp.FinalModule().(Module)
 }
 
+// IsMetaDependencyTag returns true for cross-cutting metadata dependencies.
+func IsMetaDependencyTag(tag blueprint.DependencyTag) bool {
+	if tag == licenseKindTag {
+		return true
+	} else if tag == licensesTag {
+		return true
+	}
+	return false
+}
+
 // A regexp for removing boilerplate from BaseDependencyTag from the string representation of
 // a dependency tag.
 var tagCleaner = regexp.MustCompile(`\QBaseDependencyTag:{}\E(, )?`)
diff --git a/android/mutator.go b/android/mutator.go
index 6b19dc5..c387193 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -15,7 +15,10 @@
 package android
 
 import (
+	"android/soong/bazel"
+	"fmt"
 	"reflect"
+	"strings"
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
@@ -45,14 +48,30 @@
 }
 
 // RegisterMutatorsForBazelConversion is a alternate registration pipeline for bp2build. Exported for testing.
-func RegisterMutatorsForBazelConversion(ctx *blueprint.Context, bp2buildMutators []RegisterMutatorFunc) {
-	mctx := &registerMutatorsContext{}
-
-	sharedMutators := []RegisterMutatorFunc{
-		RegisterDefaultsPreArchMutators,
+func RegisterMutatorsForBazelConversion(ctx *blueprint.Context, preArchMutators, depsMutators, bp2buildMutators []RegisterMutatorFunc) {
+	mctx := &registerMutatorsContext{
+		bazelConversionMode: true,
 	}
 
-	for _, f := range sharedMutators {
+	bp2buildPreArchMutators = append([]RegisterMutatorFunc{
+		RegisterNamespaceMutator,
+		RegisterDefaultsPreArchMutators,
+		// TODO(b/165114590): this is required to resolve deps that are only prebuilts, but we should
+		// evaluate the impact on conversion.
+		RegisterPrebuiltsPreArchMutators,
+	},
+		preArchMutators...)
+
+	for _, f := range bp2buildPreArchMutators {
+		f(mctx)
+	}
+
+	bp2buildDepsMutators = append([]RegisterMutatorFunc{
+		registerDepsMutatorBp2Build,
+		registerPathDepsMutator,
+	}, depsMutators...)
+
+	for _, f := range bp2buildDepsMutators {
 		f(mctx)
 	}
 
@@ -77,7 +96,7 @@
 
 	register(preDeps)
 
-	mctx.BottomUp("deps", depsMutator).Parallel()
+	register([]RegisterMutatorFunc{registerDepsMutator})
 
 	register(postDeps)
 
@@ -88,8 +107,9 @@
 }
 
 type registerMutatorsContext struct {
-	mutators   []*mutator
-	finalPhase bool
+	mutators            []*mutator
+	finalPhase          bool
+	bazelConversionMode bool
 }
 
 type RegisterMutatorsContext interface {
@@ -211,6 +231,8 @@
 	finalDeps = append(finalDeps, f)
 }
 
+var bp2buildPreArchMutators = []RegisterMutatorFunc{}
+var bp2buildDepsMutators = []RegisterMutatorFunc{}
 var bp2buildMutators = []RegisterMutatorFunc{}
 
 // RegisterBp2BuildMutator registers specially crafted mutators for
@@ -219,13 +241,24 @@
 //
 // TODO(b/178068862): bring this into TestContext.
 func RegisterBp2BuildMutator(moduleType string, m func(TopDownMutatorContext)) {
-	mutatorName := moduleType + "_bp2build"
 	f := func(ctx RegisterMutatorsContext) {
-		ctx.TopDown(mutatorName, m)
+		ctx.TopDown(moduleType, m)
 	}
 	bp2buildMutators = append(bp2buildMutators, f)
 }
 
+// PreArchBp2BuildMutators adds mutators to be register for converting Android Blueprint modules
+// into Bazel BUILD targets that should run prior to deps and conversion.
+func PreArchBp2BuildMutators(f RegisterMutatorFunc) {
+	bp2buildPreArchMutators = append(bp2buildPreArchMutators, f)
+}
+
+// DepsBp2BuildMutators adds mutators to be register for converting Android Blueprint modules into
+// Bazel BUILD targets that should run prior to conversion to resolve dependencies.
+func DepsBp2BuildMutators(f RegisterMutatorFunc) {
+	bp2buildDepsMutators = append(bp2buildDepsMutators, f)
+}
+
 type BaseMutatorContext interface {
 	BaseModuleContext
 
@@ -245,6 +278,12 @@
 	// CreateModule creates a new module by calling the factory method for the specified moduleType, and applies
 	// the specified property structs to it as if the properties were set in a blueprint file.
 	CreateModule(ModuleFactory, ...interface{}) Module
+
+	// CreateBazelTargetModule creates a BazelTargetModule by calling the
+	// factory method, just like in CreateModule, but also requires
+	// BazelTargetModuleProperties containing additional metadata for the
+	// bp2build codegenerator.
+	CreateBazelTargetModule(ModuleFactory, bazel.BazelTargetModuleProperties, interface{}) BazelTargetModule
 }
 
 type topDownMutatorContext struct {
@@ -370,32 +409,38 @@
 	// variant of the current module.  The value should not be modified after being passed to
 	// SetVariationProvider.
 	SetVariationProvider(module blueprint.Module, provider blueprint.ProviderKey, value interface{})
+
+	// BazelConversionMode returns whether this mutator is being run as part of Bazel Conversion.
+	BazelConversionMode() bool
 }
 
 type bottomUpMutatorContext struct {
 	bp blueprint.BottomUpMutatorContext
 	baseModuleContext
-	finalPhase bool
+	finalPhase          bool
+	bazelConversionMode bool
 }
 
 func bottomUpMutatorContextFactory(ctx blueprint.BottomUpMutatorContext, a Module,
-	finalPhase bool) BottomUpMutatorContext {
+	finalPhase, bazelConversionMode bool) BottomUpMutatorContext {
 
 	return &bottomUpMutatorContext{
-		bp:                ctx,
-		baseModuleContext: a.base().baseModuleContextFactory(ctx),
-		finalPhase:        finalPhase,
+		bp:                  ctx,
+		baseModuleContext:   a.base().baseModuleContextFactory(ctx),
+		finalPhase:          finalPhase,
+		bazelConversionMode: bazelConversionMode,
 	}
 }
 
 func (x *registerMutatorsContext) BottomUp(name string, m BottomUpMutator) MutatorHandle {
 	finalPhase := x.finalPhase
+	bazelConversionMode := x.bazelConversionMode
 	f := func(ctx blueprint.BottomUpMutatorContext) {
 		if a, ok := ctx.Module().(Module); ok {
-			m(bottomUpMutatorContextFactory(ctx, a, finalPhase))
+			m(bottomUpMutatorContextFactory(ctx, a, finalPhase, bazelConversionMode))
 		}
 	}
-	mutator := &mutator{name: name, bottomUpMutator: f}
+	mutator := &mutator{name: x.mutatorName(name), bottomUpMutator: f}
 	x.mutators = append(x.mutators, mutator)
 	return mutator
 }
@@ -406,6 +451,13 @@
 	return mutator
 }
 
+func (x *registerMutatorsContext) mutatorName(name string) string {
+	if x.bazelConversionMode {
+		return name + "_bp2build"
+	}
+	return name
+}
+
 func (x *registerMutatorsContext) TopDown(name string, m TopDownMutator) MutatorHandle {
 	f := func(ctx blueprint.TopDownMutatorContext) {
 		if a, ok := ctx.Module().(Module); ok {
@@ -416,7 +468,7 @@
 			m(actx)
 		}
 	}
-	mutator := &mutator{name: name, topDownMutator: f}
+	mutator := &mutator{name: x.mutatorName(name), topDownMutator: f}
 	x.mutators = append(x.mutators, mutator)
 	return mutator
 }
@@ -449,6 +501,31 @@
 	}
 }
 
+func registerDepsMutator(ctx RegisterMutatorsContext) {
+	ctx.BottomUp("deps", depsMutator).Parallel()
+}
+
+func registerDepsMutatorBp2Build(ctx RegisterMutatorsContext) {
+	// TODO(b/179313531): Consider a separate mutator that only runs depsMutator for modules that are
+	// being converted to build targets.
+	ctx.BottomUp("deps", depsMutator).Parallel()
+}
+
+func (t *topDownMutatorContext) CreateBazelTargetModule(
+	factory ModuleFactory,
+	bazelProps bazel.BazelTargetModuleProperties,
+	attrs interface{}) BazelTargetModule {
+	if !strings.HasPrefix(*bazelProps.Name, bazel.BazelTargetModuleNamePrefix) {
+		panic(fmt.Errorf(
+			"bp2build error: the bazel target module name must start with '%s': %s",
+			bazel.BazelTargetModuleNamePrefix,
+			*bazelProps.Name,
+		))
+	}
+
+	return t.CreateModule(factory, &bazelProps, attrs).(BazelTargetModule)
+}
+
 func (t *topDownMutatorContext) AppendProperties(props ...interface{}) {
 	for _, p := range props {
 		err := proptools.AppendMatchingProperties(t.Module().base().customizableProperties,
@@ -576,12 +653,28 @@
 
 func (b *bottomUpMutatorContext) AddVariationDependencies(variations []blueprint.Variation, tag blueprint.DependencyTag,
 	names ...string) []blueprint.Module {
+	if b.bazelConversionMode {
+		_, noSelfDeps := RemoveFromList(b.ModuleName(), names)
+		if len(noSelfDeps) == 0 {
+			return []blueprint.Module(nil)
+		}
+		// In Bazel conversion mode, mutators should not have created any variants. So, when adding a
+		// dependency, the variations would not exist and the dependency could not be added, by
+		// specifying no variations, we will allow adding the dependency to succeed.
+		return b.bp.AddFarVariationDependencies(nil, tag, noSelfDeps...)
+	}
 
 	return b.bp.AddVariationDependencies(variations, tag, names...)
 }
 
 func (b *bottomUpMutatorContext) AddFarVariationDependencies(variations []blueprint.Variation,
 	tag blueprint.DependencyTag, names ...string) []blueprint.Module {
+	if b.bazelConversionMode {
+		// In Bazel conversion mode, mutators should not have created any variants. So, when adding a
+		// dependency, the variations would not exist and the dependency could not be added, by
+		// specifying no variations, we will allow adding the dependency to succeed.
+		return b.bp.AddFarVariationDependencies(nil, tag, names...)
+	}
 
 	return b.bp.AddFarVariationDependencies(variations, tag, names...)
 }
@@ -609,3 +702,7 @@
 func (b *bottomUpMutatorContext) SetVariationProvider(module blueprint.Module, provider blueprint.ProviderKey, value interface{}) {
 	b.bp.SetVariationProvider(module, provider, value)
 }
+
+func (b *bottomUpMutatorContext) BazelConversionMode() bool {
+	return b.bazelConversionMode
+}
diff --git a/android/packaging.go b/android/packaging.go
index df2533f..9b901ce 100644
--- a/android/packaging.go
+++ b/android/packaging.go
@@ -66,7 +66,7 @@
 	// returns zip entries in it. This is expected to be called in GenerateAndroidBuildActions,
 	// followed by a build rule that unzips it and creates the final output (img, zip, tar.gz,
 	// etc.) from the extracted files
-	CopyDepsToZip(ctx ModuleContext, zipOut OutputPath) []string
+	CopyDepsToZip(ctx ModuleContext, zipOut WritablePath) []string
 }
 
 // PackagingBase provides basic functionality for packaging dependencies. A module is expected to
@@ -180,7 +180,7 @@
 }
 
 // See PackageModule.CopyDepsToZip
-func (p *PackagingBase) CopyDepsToZip(ctx ModuleContext, zipOut OutputPath) (entries []string) {
+func (p *PackagingBase) CopyDepsToZip(ctx ModuleContext, zipOut WritablePath) (entries []string) {
 	m := make(map[string]PackagingSpec)
 	ctx.WalkDeps(func(child Module, parent Module) bool {
 		if !IsInstallDepNeeded(ctx.OtherModuleDependencyTag(child)) {
@@ -196,7 +196,7 @@
 
 	builder := NewRuleBuilder(pctx, ctx)
 
-	dir := PathForModuleOut(ctx, ".zip").OutputPath
+	dir := PathForModuleOut(ctx, ".zip")
 	builder.Command().Text("rm").Flag("-rf").Text(dir.String())
 	builder.Command().Text("mkdir").Flag("-p").Text(dir.String())
 
diff --git a/android/packaging_test.go b/android/packaging_test.go
index dc259dd..2c99b97 100644
--- a/android/packaging_test.go
+++ b/android/packaging_test.go
@@ -80,7 +80,7 @@
 }
 
 func (m *packageTestModule) GenerateAndroidBuildActions(ctx ModuleContext) {
-	zipFile := PathForModuleOut(ctx, "myzip.zip").OutputPath
+	zipFile := PathForModuleOut(ctx, "myzip.zip")
 	m.entries = m.CopyDepsToZip(ctx, zipFile)
 }
 
diff --git a/android/paths.go b/android/paths.go
index b5a1401..44221be 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -15,6 +15,7 @@
 package android
 
 import (
+	"android/soong/bazel"
 	"fmt"
 	"io/ioutil"
 	"os"
@@ -331,6 +332,115 @@
 	return ret
 }
 
+// A subset of the ModuleContext methods which are sufficient to resolve references to paths/deps in
+// order to form a Bazel-compatible label for conversion.
+type BazelConversionPathContext interface {
+	EarlyModulePathContext
+
+	GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag)
+	OtherModuleName(m blueprint.Module) string
+	OtherModuleDir(m blueprint.Module) string
+}
+
+// BazelLabelForModuleDeps returns a Bazel-compatible label for the requested modules which
+// correspond to dependencies on the module within the given ctx.
+func BazelLabelForModuleDeps(ctx BazelConversionPathContext, modules []string) bazel.LabelList {
+	var labels bazel.LabelList
+	for _, module := range modules {
+		bpText := module
+		if m := SrcIsModule(module); m == "" {
+			module = ":" + module
+		}
+		if m, t := SrcIsModuleWithTag(module); m != "" {
+			l := getOtherModuleLabel(ctx, m, t)
+			l.Bp_text = bpText
+			labels.Includes = append(labels.Includes, l)
+		} else {
+			ctx.ModuleErrorf("%q, is not a module reference", module)
+		}
+	}
+	return labels
+}
+
+// BazelLabelForModuleSrc returns bazel.LabelList with paths rooted from the module's local source
+// directory. It expands globs, and resolves references to modules using the ":name" syntax to
+// bazel-compatible labels.  Properties passed as the paths or excludes argument must have been
+// annotated with struct tag `android:"path"` so that dependencies on other modules will have
+// already been handled by the path_properties mutator.
+func BazelLabelForModuleSrc(ctx BazelConversionPathContext, paths []string) bazel.LabelList {
+	return BazelLabelForModuleSrcExcludes(ctx, paths, []string(nil))
+}
+
+// BazelLabelForModuleSrcExcludes returns bazel.LabelList with paths rooted from the module's local
+// source directory, excluding labels included in the excludes argument. It expands globs, and
+// resolves references to modules using the ":name" syntax to bazel-compatible labels. Properties
+// passed as the paths or excludes argument must have been annotated with struct tag
+// `android:"path"` so that dependencies on other modules will have already been handled by the
+// path_properties mutator.
+func BazelLabelForModuleSrcExcludes(ctx BazelConversionPathContext, paths, excludes []string) bazel.LabelList {
+	excludeLabels := expandSrcsForBazel(ctx, excludes, []string(nil))
+	excluded := make([]string, 0, len(excludeLabels.Includes))
+	for _, e := range excludeLabels.Includes {
+		excluded = append(excluded, e.Label)
+	}
+	labels := expandSrcsForBazel(ctx, paths, excluded)
+	labels.Excludes = excludeLabels.Includes
+	return labels
+}
+
+// expandSrcsForBazel returns bazel.LabelList with paths rooted from the module's local
+// source directory, excluding labels included in the excludes argument. It expands globs, and
+// resolves references to modules using the ":name" syntax to bazel-compatible labels.  Properties
+// passed as the paths or excludes argument must have been annotated with struct tag
+// `android:"path"` so that dependencies on other modules will have already been handled by the
+// path_properties mutator.
+func expandSrcsForBazel(ctx BazelConversionPathContext, paths, expandedExcludes []string) bazel.LabelList {
+	labels := bazel.LabelList{
+		Includes: []bazel.Label{},
+	}
+	for _, p := range paths {
+		if m, tag := SrcIsModuleWithTag(p); m != "" {
+			l := getOtherModuleLabel(ctx, m, tag)
+			if !InList(l.Label, expandedExcludes) {
+				l.Bp_text = fmt.Sprintf(":%s", m)
+				labels.Includes = append(labels.Includes, l)
+			}
+		} else {
+			var expandedPaths []bazel.Label
+			if pathtools.IsGlob(p) {
+				globbedPaths := GlobFiles(ctx, pathForModuleSrc(ctx, p).String(), expandedExcludes)
+				globbedPaths = PathsWithModuleSrcSubDir(ctx, globbedPaths, "")
+				for _, path := range globbedPaths {
+					s := path.Rel()
+					expandedPaths = append(expandedPaths, bazel.Label{Label: s})
+				}
+			} else {
+				if !InList(p, expandedExcludes) {
+					expandedPaths = append(expandedPaths, bazel.Label{Label: p})
+				}
+			}
+			labels.Includes = append(labels.Includes, expandedPaths...)
+		}
+	}
+	return labels
+}
+
+// getOtherModuleLabel returns a bazel.Label for the given dependency/tag combination for the
+// module. The label will be relative to the current directory if appropriate. The dependency must
+// already be resolved by either deps mutator or path deps mutator.
+func getOtherModuleLabel(ctx BazelConversionPathContext, dep, tag string) bazel.Label {
+	m, _ := ctx.GetDirectDep(dep)
+	// TODO(b/165114590): Convert tag (":name{.tag}") to corresponding Bazel implicit output targets.
+	otherModuleName := ctx.OtherModuleName(m)
+	var label bazel.Label
+	if otherDir, dir := ctx.OtherModuleDir(m), ctx.ModuleDir(); otherDir != dir {
+		label.Label = fmt.Sprintf("//%s:%s", otherDir, otherModuleName)
+	} else {
+		label.Label = fmt.Sprintf(":%s", otherModuleName)
+	}
+	return label
+}
+
 // OutputPaths is a slice of OutputPath objects, with helpers to operate on the collection.
 type OutputPaths []OutputPath
 
@@ -1011,8 +1121,13 @@
 	return p.config.buildDir
 }
 
+func (p OutputPath) objPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleObjPath {
+	return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
+}
+
 var _ Path = OutputPath{}
 var _ WritablePath = OutputPath{}
+var _ objPathProvider = OutputPath{}
 
 // toolDepPath is a Path representing a dependency of the build tool.
 type toolDepPath struct {
diff --git a/android/register.go b/android/register.go
index 02fc97e..18c743f 100644
--- a/android/register.go
+++ b/android/register.go
@@ -115,7 +115,7 @@
 		ctx.RegisterSingletonType(t.name, SingletonFactoryAdaptor(ctx, t.factory))
 	}
 
-	RegisterMutatorsForBazelConversion(ctx.Context, bp2buildMutators)
+	RegisterMutatorsForBazelConversion(ctx.Context, bp2buildPreArchMutators, bp2buildDepsMutators, bp2buildMutators)
 }
 
 // Register the pipeline of singletons, module types, and mutators for
diff --git a/android/sdk.go b/android/sdk.go
index bab0ed8..f2cdc88 100644
--- a/android/sdk.go
+++ b/android/sdk.go
@@ -177,12 +177,6 @@
 	// to the zip
 	CopyToSnapshot(src Path, dest string)
 
-	// Return 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
-
 	// Unzip the supplied zip into the snapshot relative directory destDir.
 	UnzipToSnapshot(zipPath Path, destDir string)
 
diff --git a/android/testing.go b/android/testing.go
index 5640c29..de338bf 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -56,9 +56,9 @@
 
 type TestContext struct {
 	*Context
-	preArch, preDeps, postDeps, finalDeps []RegisterMutatorFunc
-	bp2buildMutators                      []RegisterMutatorFunc
-	NameResolver                          *NameResolver
+	preArch, preDeps, postDeps, finalDeps           []RegisterMutatorFunc
+	bp2buildPreArch, bp2buildDeps, bp2buildMutators []RegisterMutatorFunc
+	NameResolver                                    *NameResolver
 }
 
 func (ctx *TestContext) PreArchMutators(f RegisterMutatorFunc) {
@@ -85,13 +85,24 @@
 // RegisterBp2BuildMutator registers a BazelTargetModule mutator for converting a module
 // type to the equivalent Bazel target.
 func (ctx *TestContext) RegisterBp2BuildMutator(moduleType string, m func(TopDownMutatorContext)) {
-	mutatorName := moduleType + "_bp2build"
 	f := func(ctx RegisterMutatorsContext) {
-		ctx.TopDown(mutatorName, m)
+		ctx.TopDown(moduleType, m)
 	}
 	ctx.bp2buildMutators = append(ctx.bp2buildMutators, f)
 }
 
+// PreArchBp2BuildMutators adds mutators to be register for converting Android Blueprint modules
+// into Bazel BUILD targets that should run prior to deps and conversion.
+func (ctx *TestContext) PreArchBp2BuildMutators(f RegisterMutatorFunc) {
+	ctx.bp2buildPreArch = append(ctx.bp2buildPreArch, f)
+}
+
+// DepsBp2BuildMutators adds mutators to be register for converting Android Blueprint modules into
+// Bazel BUILD targets that should run prior to conversion to resolve dependencies.
+func (ctx *TestContext) DepsBp2BuildMutators(f RegisterMutatorFunc) {
+	ctx.bp2buildDeps = append(ctx.bp2buildDeps, f)
+}
+
 func (ctx *TestContext) Register() {
 	registerMutators(ctx.Context.Context, ctx.preArch, ctx.preDeps, ctx.postDeps, ctx.finalDeps)
 
@@ -100,7 +111,7 @@
 
 // RegisterForBazelConversion prepares a test context for bp2build conversion.
 func (ctx *TestContext) RegisterForBazelConversion() {
-	RegisterMutatorsForBazelConversion(ctx.Context.Context, ctx.bp2buildMutators)
+	RegisterMutatorsForBazelConversion(ctx.Context.Context, ctx.bp2buildPreArch, ctx.bp2buildDeps, ctx.bp2buildMutators)
 }
 
 func (ctx *TestContext) ParseFileList(rootDir string, filePaths []string) (deps []string, errs []error) {
diff --git a/android/variable.go b/android/variable.go
index 9b3ed17..e76d683 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -121,10 +121,6 @@
 			Cppflags []string
 		}
 
-		Use_lmkd_stats_log struct {
-			Cflags []string
-		}
-
 		Arc struct {
 			Cflags            []string `android:"arch_variant"`
 			Exclude_srcs      []string `android:"arch_variant"`
@@ -240,7 +236,6 @@
 	Treble_linker_namespaces     *bool `json:",omitempty"`
 	Enforce_vintf_manifest       *bool `json:",omitempty"`
 	Uml                          *bool `json:",omitempty"`
-	Use_lmkd_stats_log           *bool `json:",omitempty"`
 	Arc                          *bool `json:",omitempty"`
 	MinimizeJavaDebugInfo        *bool `json:",omitempty"`
 
@@ -312,6 +307,9 @@
 	DirectedVendorSnapshot bool            `json:",omitempty"`
 	VendorSnapshotModules  map[string]bool `json:",omitempty"`
 
+	DirectedRecoverySnapshot bool            `json:",omitempty"`
+	RecoverySnapshotModules  map[string]bool `json:",omitempty"`
+
 	BoardVendorSepolicyDirs      []string `json:",omitempty"`
 	BoardOdmSepolicyDirs         []string `json:",omitempty"`
 	BoardReqdMaskPolicy          []string `json:",omitempty"`
diff --git a/apex/allowed_deps.txt b/apex/allowed_deps.txt
index 6dd49e5..5d00e06 100644
--- a/apex/allowed_deps.txt
+++ b/apex/allowed_deps.txt
@@ -52,6 +52,7 @@
 android.hidl.safe_union@1.0(minSdkVersion:29)
 android.hidl.token@1.0(minSdkVersion:29)
 android.hidl.token@1.0-utils(minSdkVersion:29)
+android.net.ipsec.ike(minSdkVersion:30)
 android.net.ipsec.ike(minSdkVersion:current)
 android.net.ipsec.ike.xml(minSdkVersion:(no version))
 androidx-constraintlayout_constraintlayout(minSdkVersion:14)
@@ -238,6 +239,7 @@
 libbase(minSdkVersion:29)
 libbase_headers(minSdkVersion:29)
 libbinder_headers(minSdkVersion:29)
+libbinder_headers_platform_shared(minSdkVersion:29)
 libbinderthreadstateutils(minSdkVersion:29)
 libbluetooth-types-header(minSdkVersion:29)
 libbrotli(minSdkVersion:(no version))
@@ -499,6 +501,7 @@
 neuralnetworks_utils_hal_1_3(minSdkVersion:30)
 neuralnetworks_utils_hal_common(minSdkVersion:30)
 neuralnetworks_utils_hal_service(minSdkVersion:30)
+no_op(minSdkVersion:current)
 note_memtag_heap_async(minSdkVersion:16)
 note_memtag_heap_sync(minSdkVersion:16)
 PermissionController(minSdkVersion:28)
diff --git a/apex/apex.go b/apex/apex.go
index c897042..cfeac72 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -54,6 +54,8 @@
 func RegisterPreDepsMutators(ctx android.RegisterMutatorsContext) {
 	ctx.TopDown("apex_vndk", apexVndkMutator).Parallel()
 	ctx.BottomUp("apex_vndk_deps", apexVndkDepsMutator).Parallel()
+	ctx.BottomUp("prebuilt_apex_select_source", prebuiltSelectSourceMutator).Parallel()
+	ctx.BottomUp("deapexer_select_source", deapexerSelectSourceMutator).Parallel()
 }
 
 func RegisterPostDepsMutators(ctx android.RegisterMutatorsContext) {
@@ -2346,7 +2348,6 @@
 		"libFraunhoferAAC",
 		"libaudio-a2dp-hw-utils",
 		"libaudio-hearing-aid-hw-utils",
-		"libbinder_headers",
 		"libbluetooth",
 		"libbluetooth-types",
 		"libbluetooth-types-header",
@@ -2477,7 +2478,6 @@
 		"libaudiopolicy",
 		"libaudioutils",
 		"libaudioutils_fixedfft",
-		"libbinder_headers",
 		"libbluetooth-types-header",
 		"libbufferhub",
 		"libbufferhub_headers",
@@ -2593,7 +2593,6 @@
 		"libavcenc",
 		"libavservices_minijail",
 		"libavservices_minijail",
-		"libbinder_headers",
 		"libbinderthreadstateutils",
 		"libbluetooth-types-header",
 		"libbufferhub_headers",
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 7f5be7e..83eb56a 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -994,6 +994,80 @@
 	})
 }
 
+func TestApex_PlatformUsesLatestStubFromApex(t *testing.T) {
+	t.Parallel()
+	//   myapex (Z)
+	//      mylib -----------------.
+	//                             |
+	//   otherapex (29)            |
+	//      libstub's versions: 29 Z current
+	//                                  |
+	//   <platform>                     |
+	//      libplatform ----------------'
+	ctx, _ := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			native_shared_libs: ["mylib"],
+			min_sdk_version: "Z", // non-final
+		}
+
+		cc_library {
+			name: "mylib",
+			srcs: ["mylib.cpp"],
+			shared_libs: ["libstub"],
+			apex_available: ["myapex"],
+			min_sdk_version: "Z",
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		apex {
+			name: "otherapex",
+			key: "myapex.key",
+			native_shared_libs: ["libstub"],
+			min_sdk_version: "29",
+		}
+
+		cc_library {
+			name: "libstub",
+			srcs: ["mylib.cpp"],
+			stubs: {
+				versions: ["29", "Z", "current"],
+			},
+			apex_available: ["otherapex"],
+			min_sdk_version: "29",
+		}
+
+		// platform module depending on libstub from otherapex should use the latest stub("current")
+		cc_library {
+			name: "libplatform",
+			srcs: ["mylib.cpp"],
+			shared_libs: ["libstub"],
+		}
+	`, func(fs map[string][]byte, config android.Config) {
+		config.TestProductVariables.Platform_sdk_codename = proptools.StringPtr("Z")
+		config.TestProductVariables.Platform_sdk_final = proptools.BoolPtr(false)
+		config.TestProductVariables.Platform_version_active_codenames = []string{"Z"}
+	})
+
+	// Ensure that mylib from myapex is built against "min_sdk_version" stub ("Z"), which is non-final
+	mylibCflags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static_apex10000").Rule("cc").Args["cFlags"]
+	ensureContains(t, mylibCflags, "-D__LIBSTUB_API__=9000 ")
+	mylibLdflags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_apex10000").Rule("ld").Args["libFlags"]
+	ensureContains(t, mylibLdflags, "libstub/android_arm64_armv8-a_shared_Z/libstub.so ")
+
+	// Ensure that libplatform is built against latest stub ("current") of mylib3 from the apex
+	libplatformCflags := ctx.ModuleForTests("libplatform", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
+	ensureContains(t, libplatformCflags, "-D__LIBSTUB_API__=10000 ") // "current" maps to 10000
+	libplatformLdflags := ctx.ModuleForTests("libplatform", "android_arm64_armv8-a_shared").Rule("ld").Args["libFlags"]
+	ensureContains(t, libplatformLdflags, "libstub/android_arm64_armv8-a_shared_current/libstub.so ")
+}
+
 func TestApexWithExplicitStubsDependency(t *testing.T) {
 	ctx, _ := testApex(t, `
 		apex {
@@ -4288,7 +4362,7 @@
 		// Empty transformation.
 	}
 
-	checkDexJarBuildPath := func(ctx *android.TestContext, name string) {
+	checkDexJarBuildPath := func(t *testing.T, ctx *android.TestContext, name string) {
 		// Make sure the import has been given the correct path to the dex jar.
 		p := ctx.ModuleForTests(name, "android_common_myapex").Module().(java.Dependency)
 		dexJarBuildPath := p.DexJarBuildPath()
@@ -4297,7 +4371,7 @@
 		}
 	}
 
-	ensureNoSourceVariant := func(ctx *android.TestContext) {
+	ensureNoSourceVariant := func(t *testing.T, ctx *android.TestContext) {
 		// Make sure that an apex variant is not created for the source module.
 		if expected, actual := []string{"android_common"}, ctx.ModuleVariantsForTests("libfoo"); !reflect.DeepEqual(expected, actual) {
 			t.Errorf("invalid set of variants for %q: expected %q, found %q", "libfoo", expected, actual)
@@ -4328,7 +4402,7 @@
 		// Make sure that dexpreopt can access dex implementation files from the prebuilt.
 		ctx := testDexpreoptWithApexes(t, bp, "", transform)
 
-		checkDexJarBuildPath(ctx, "libfoo")
+		checkDexJarBuildPath(t, ctx, "libfoo")
 	})
 
 	t.Run("prebuilt with source preferred", func(t *testing.T) {
@@ -4360,8 +4434,8 @@
 		// Make sure that dexpreopt can access dex implementation files from the prebuilt.
 		ctx := testDexpreoptWithApexes(t, bp, "", transform)
 
-		checkDexJarBuildPath(ctx, "prebuilt_libfoo")
-		ensureNoSourceVariant(ctx)
+		checkDexJarBuildPath(t, ctx, "prebuilt_libfoo")
+		ensureNoSourceVariant(t, ctx)
 	})
 
 	t.Run("prebuilt preferred with source", func(t *testing.T) {
@@ -4393,8 +4467,8 @@
 		// Make sure that dexpreopt can access dex implementation files from the prebuilt.
 		ctx := testDexpreoptWithApexes(t, bp, "", transform)
 
-		checkDexJarBuildPath(ctx, "prebuilt_libfoo")
-		ensureNoSourceVariant(ctx)
+		checkDexJarBuildPath(t, ctx, "prebuilt_libfoo")
+		ensureNoSourceVariant(t, ctx)
 	})
 }
 
@@ -4403,7 +4477,7 @@
 		config.BootJars = android.CreateTestConfiguredJarList([]string{"myapex:libfoo"})
 	}
 
-	checkBootDexJarPath := func(ctx *android.TestContext, bootDexJarPath string) {
+	checkBootDexJarPath := func(t *testing.T, ctx *android.TestContext, bootDexJarPath string) {
 		s := ctx.SingletonForTests("dex_bootjars")
 		foundLibfooJar := false
 		for _, output := range s.AllOutputs() {
@@ -4421,6 +4495,12 @@
 		}
 	}
 
+	checkHiddenAPIIndexInputs := func(t *testing.T, ctx *android.TestContext, expectedInputs string) {
+		hiddenAPIIndex := ctx.SingletonForTests("hiddenapi_index")
+		indexRule := hiddenAPIIndex.Rule("singleton-merged-hiddenapi-index")
+		java.CheckHiddenAPIRuleInputs(t, expectedInputs, indexRule)
+	}
+
 	t.Run("prebuilt only", func(t *testing.T) {
 		bp := `
 		prebuilt_apex {
@@ -4444,7 +4524,12 @@
 	`
 
 		ctx := testDexpreoptWithApexes(t, bp, "", transform)
-		checkBootDexJarPath(ctx, ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
+		checkBootDexJarPath(t, ctx, ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
+
+		// Make sure that the dex file from the prebuilt_apex contributes to the hiddenapi index file.
+		checkHiddenAPIIndexInputs(t, ctx, `
+.intermediates/libfoo/android_common_myapex/hiddenapi/index.csv
+`)
 	})
 
 	t.Run("prebuilt with source library preferred", func(t *testing.T) {
@@ -4513,7 +4598,12 @@
 	`
 
 		ctx := testDexpreoptWithApexes(t, bp, "", transform)
-		checkBootDexJarPath(ctx, ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
+		checkBootDexJarPath(t, ctx, ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
+
+		// Make sure that the dex file from the prebuilt_apex contributes to the hiddenapi index file.
+		checkHiddenAPIIndexInputs(t, ctx, `
+.intermediates/prebuilt_libfoo/android_common_myapex/hiddenapi/index.csv
+`)
 	})
 
 	t.Run("prebuilt with source apex preferred", func(t *testing.T) {
@@ -4557,7 +4647,12 @@
 	`
 
 		ctx := testDexpreoptWithApexes(t, bp, "", transform)
-		checkBootDexJarPath(ctx, ".intermediates/libfoo/android_common_apex10000/aligned/libfoo.jar")
+		checkBootDexJarPath(t, ctx, ".intermediates/libfoo/android_common_apex10000/hiddenapi/libfoo.jar")
+
+		// Make sure that the dex file from the prebuilt_apex contributes to the hiddenapi index file.
+		checkHiddenAPIIndexInputs(t, ctx, `
+.intermediates/libfoo/android_common_apex10000/hiddenapi/index.csv
+`)
 	})
 
 	t.Run("prebuilt preferred with source apex disabled", func(t *testing.T) {
@@ -4603,7 +4698,12 @@
 	`
 
 		ctx := testDexpreoptWithApexes(t, bp, "", transform)
-		checkBootDexJarPath(ctx, ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
+		checkBootDexJarPath(t, ctx, ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
+
+		// Make sure that the dex file from the prebuilt_apex contributes to the hiddenapi index file.
+		checkHiddenAPIIndexInputs(t, ctx, `
+.intermediates/prebuilt_libfoo/android_common_prebuilt_myapex/hiddenapi/index.csv
+`)
 	})
 }
 
@@ -6230,6 +6330,7 @@
 	android.RegisterPrebuiltMutators(ctx)
 	cc.RegisterRequiredBuildComponentsForTest(ctx)
 	java.RegisterRequiredBuildComponentsForTest(ctx)
+	java.RegisterHiddenApiSingletonComponents(ctx)
 	ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators)
 	ctx.PreDepsMutators(RegisterPreDepsMutators)
 	ctx.PostDepsMutators(RegisterPostDepsMutators)
@@ -6241,6 +6342,11 @@
 	transformDexpreoptConfig(dexpreoptConfig)
 	dexpreopt.SetTestGlobalConfig(config, dexpreoptConfig)
 
+	// Make sure that any changes to these dexpreopt properties are mirrored in the corresponding
+	// product variables.
+	config.TestProductVariables.BootJars = dexpreoptConfig.BootJars
+	config.TestProductVariables.UpdatableBootJars = dexpreoptConfig.UpdatableBootJars
+
 	_, errs := ctx.ParseBlueprintsFiles("Android.bp")
 	android.FailIfErrored(t, errs)
 
diff --git a/apex/builder.go b/apex/builder.go
index 16ca74c..2663a67 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -700,15 +700,20 @@
 		})
 		a.apisUsedByModuleFile = apisUsedbyOutputFile
 
+		var libNames []string
+		for _, f := range a.filesInfo {
+			if f.class == nativeSharedLib {
+				libNames = append(libNames, f.stem())
+			}
+		}
 		apisBackedbyOutputFile := android.PathForModuleOut(ctx, a.Name()+"_backing.txt")
 		ndkLibraryList := android.PathForSource(ctx, "system/core/rootdir/etc/public.libraries.android.txt")
 		rule := android.NewRuleBuilder(pctx, ctx)
 		rule.Command().
 			Tool(android.PathForSource(ctx, "build/soong/scripts/gen_ndk_backedby_apex.sh")).
-			Text(imageDir.String()).
-			Implicits(implicitInputs).
 			Output(apisBackedbyOutputFile).
-			Input(ndkLibraryList)
+			Input(ndkLibraryList).
+			Flags(libNames)
 		rule.Build("ndk_backedby_list", "Generate API libraries backed by Apex")
 		a.apisBackedByModuleFile = apisBackedbyOutputFile
 
diff --git a/apex/deapexer.go b/apex/deapexer.go
index 651cadf..8f4a285 100644
--- a/apex/deapexer.go
+++ b/apex/deapexer.go
@@ -78,12 +78,17 @@
 	return p.prebuilt.Name(p.ModuleBase.Name())
 }
 
-func (p *Deapexer) DepsMutator(ctx android.BottomUpMutatorContext) {
-	if err := p.apexFileProperties.selectSource(ctx); err != nil {
-		ctx.ModuleErrorf("%s", err)
+func deapexerSelectSourceMutator(ctx android.BottomUpMutatorContext) {
+	p, ok := ctx.Module().(*Deapexer)
+	if !ok {
 		return
 	}
+	if err := p.apexFileProperties.selectSource(ctx); err != nil {
+		ctx.ModuleErrorf("%s", err)
+	}
+}
 
+func (p *Deapexer) DepsMutator(ctx android.BottomUpMutatorContext) {
 	// Add dependencies from the java modules to which this exports files from the `.apex` file onto
 	// this module so that they can access the `DeapexerInfo` object that this provides.
 	for _, lib := range p.properties.Exported_java_libs {
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index c72a9eb..041afb3 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -250,17 +250,46 @@
 	return name
 }
 
-func (p *Prebuilt) DepsMutator(ctx android.BottomUpMutatorContext) {
-	if err := p.properties.selectSource(ctx); err != nil {
-		ctx.ModuleErrorf("%s", err)
+func prebuiltSelectSourceMutator(ctx android.BottomUpMutatorContext) {
+	p, ok := ctx.Module().(*Prebuilt)
+	if !ok {
 		return
 	}
+	if err := p.properties.selectSource(ctx); err != nil {
+		ctx.ModuleErrorf("%s", err)
+	}
+}
 
+type exportedDependencyTag struct {
+	blueprint.BaseDependencyTag
+	name string
+}
+
+// Mark this tag so dependencies that use it are excluded from visibility enforcement.
+//
+// This does allow any prebuilt_apex to reference any module which does open up a small window for
+// restricted visibility modules to be referenced from the wrong prebuilt_apex. However, doing so
+// avoids opening up a much bigger window by widening the visibility of modules that need files
+// provided by the prebuilt_apex to include all the possible locations they may be defined, which
+// could include everything below vendor/.
+//
+// A prebuilt_apex that references a module via this tag will have to contain the appropriate files
+// corresponding to that module, otherwise it will fail when attempting to retrieve the files from
+// the .apex file. It will also have to be included in the module's apex_available property too.
+// That makes it highly unlikely that a prebuilt_apex would reference a restricted module
+// incorrectly.
+func (t exportedDependencyTag) ExcludeFromVisibilityEnforcement() {}
+
+var (
+	exportedJavaLibTag = exportedDependencyTag{name: "exported_java_lib"}
+)
+
+func (p *Prebuilt) DepsMutator(ctx android.BottomUpMutatorContext) {
 	// Add dependencies onto the java modules that represent the java libraries that are provided by
 	// and exported from this prebuilt apex.
 	for _, lib := range p.properties.Exported_java_libs {
 		dep := prebuiltApexExportedModuleName(ctx, lib)
-		ctx.AddFarVariationDependencies(ctx.Config().AndroidCommonTarget.Variations(), javaLibTag, dep)
+		ctx.AddFarVariationDependencies(ctx.Config().AndroidCommonTarget.Variations(), exportedJavaLibTag, dep)
 	}
 }
 
@@ -300,7 +329,7 @@
 	var dependencies []android.ApexModule
 	mctx.VisitDirectDeps(func(m android.Module) {
 		tag := mctx.OtherModuleDependencyTag(m)
-		if tag == javaLibTag {
+		if tag == exportedJavaLibTag {
 			depName := mctx.OtherModuleName(m)
 
 			// It is an error if the other module is not a prebuilt.
diff --git a/bazel/properties.go b/bazel/properties.go
index 79956e1..8055306 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -14,9 +14,17 @@
 
 package bazel
 
+import (
+	"fmt"
+	"strings"
+)
+
 type bazelModuleProperties struct {
 	// The label of the Bazel target replacing this Soong module.
 	Label string
+
+	// If true, bp2build will generate the converted Bazel target for this module.
+	Bp2build_available bool
 }
 
 // Properties contains common module properties for Bazel migration purposes.
@@ -29,9 +37,51 @@
 // BazelTargetModuleProperties contain properties and metadata used for
 // Blueprint to BUILD file conversion.
 type BazelTargetModuleProperties struct {
+	Name *string
+
 	// The Bazel rule class for this target.
 	Rule_class string
 
 	// The target label for the bzl file containing the definition of the rule class.
 	Bzl_load_location string
 }
+
+const BazelTargetModuleNamePrefix = "__bp2build__"
+
+func NewBazelTargetModuleProperties(name string, ruleClass string, bzlLoadLocation string) BazelTargetModuleProperties {
+	if strings.HasPrefix(name, BazelTargetModuleNamePrefix) {
+		panic(fmt.Errorf(
+			"The %s name prefix is added automatically, do not set it manually: %s",
+			BazelTargetModuleNamePrefix,
+			name))
+	}
+	name = BazelTargetModuleNamePrefix + name
+	return BazelTargetModuleProperties{
+		Name:              &name,
+		Rule_class:        ruleClass,
+		Bzl_load_location: bzlLoadLocation,
+	}
+}
+
+// Label is used to represent a Bazel compatible Label. Also stores the original bp text to support
+// string replacement.
+type Label struct {
+	Bp_text string
+	Label   string
+}
+
+// LabelList is used to represent a list of Bazel labels.
+type LabelList struct {
+	Includes []Label
+	Excludes []Label
+}
+
+// Append appends the fields of other labelList to the corresponding fields of ll.
+func (ll *LabelList) Append(other LabelList) {
+	if len(ll.Includes) > 0 || len(other.Includes) > 0 {
+		ll.Includes = append(ll.Includes, other.Includes...)
+	}
+	if len(ll.Excludes) > 0 || len(other.Excludes) > 0 {
+		ll.Excludes = append(other.Excludes, other.Excludes...)
+	}
+}
diff --git a/bp2build/Android.bp b/bp2build/Android.bp
index d4c52b5..fdac88d 100644
--- a/bp2build/Android.bp
+++ b/bp2build/Android.bp
@@ -16,6 +16,7 @@
         "soong-android",
         "soong-bazel",
         "soong-genrule",
+        "soong-sh",
     ],
     testSrcs: [
         "build_conversion_test.go",
diff --git a/bp2build/bp2build.go b/bp2build/bp2build.go
index a414f04..b89d0a0 100644
--- a/bp2build/bp2build.go
+++ b/bp2build/bp2build.go
@@ -28,7 +28,7 @@
 
 	ruleShims := CreateRuleShims(android.ModuleTypeFactories())
 
-	buildToTargets := GenerateSoongModuleTargets(ctx.Context(), ctx.mode)
+	buildToTargets := GenerateBazelTargets(ctx.Context(), ctx.mode)
 
 	filesToWrite := CreateBazelFiles(ruleShims, buildToTargets, ctx.mode)
 	for _, f := range filesToWrite {
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index 1af1d60..7ffcfa4 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -16,6 +16,7 @@
 
 import (
 	"android/soong/android"
+	"android/soong/bazel"
 	"fmt"
 	"reflect"
 	"strconv"
@@ -159,7 +160,7 @@
 	return attributes
 }
 
-func GenerateSoongModuleTargets(ctx bpToBuildContext, codegenMode CodegenMode) map[string]BazelTargets {
+func GenerateBazelTargets(ctx bpToBuildContext, codegenMode CodegenMode) map[string]BazelTargets {
 	buildFileToTargets := make(map[string]BazelTargets)
 	ctx.VisitAllModules(func(m blueprint.Module) {
 		dir := ctx.ModuleDir(m)
@@ -172,12 +173,18 @@
 			}
 			t = generateBazelTarget(ctx, m)
 		case QueryView:
+			// Blocklist certain module types from being generated.
+			if canonicalizeModuleType(ctx.ModuleType(m)) == "package" {
+				// package module name contain slashes, and thus cannot
+				// be mapped cleanly to a bazel label.
+				return
+			}
 			t = generateSoongModuleTarget(ctx, m)
 		default:
 			panic(fmt.Errorf("Unknown code-generation mode: %s", codegenMode))
 		}
 
-		buildFileToTargets[ctx.ModuleDir(m)] = append(buildFileToTargets[dir], t)
+		buildFileToTargets[dir] = append(buildFileToTargets[dir], t)
 	})
 	return buildFileToTargets
 }
@@ -212,6 +219,8 @@
 	// Delete it from being generated in the BUILD file.
 	delete(props.Attrs, "bzl_load_location")
 
+	delete(props.Attrs, "bp2build_available")
+
 	// Return the Bazel target with rule class and attributes, ready to be
 	// code-generated.
 	attributes := propsToAttributes(props.Attrs)
@@ -349,6 +358,13 @@
 		ret += makeIndent(indent)
 		ret += "]"
 	case reflect.Struct:
+		if labels, ok := propertyValue.Interface().(bazel.LabelList); ok {
+			// TODO(b/165114590): convert glob syntax
+			return prettyPrint(reflect.ValueOf(labels.Includes), indent)
+		} else if label, ok := propertyValue.Interface().(bazel.Label); ok {
+			return fmt.Sprintf("%q", label.Label), nil
+		}
+
 		ret = "{\n"
 		// Sort and print the struct props by the key.
 		structProps := extractStructProperties(propertyValue, indent)
@@ -453,7 +469,7 @@
 }
 
 func targetNameForBp2Build(c bpToBuildContext, logicModule blueprint.Module) string {
-	return strings.Replace(c.ModuleName(logicModule), "__bp2build__", "", 1)
+	return strings.Replace(c.ModuleName(logicModule), bazel.BazelTargetModuleNamePrefix, "", 1)
 }
 
 func targetNameWithVariant(c bpToBuildContext, logicModule blueprint.Module) string {
diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go
index a01a7cd..df554a0 100644
--- a/bp2build/build_conversion_test.go
+++ b/bp2build/build_conversion_test.go
@@ -17,6 +17,8 @@
 import (
 	"android/soong/android"
 	"android/soong/genrule"
+	"android/soong/sh"
+	"strings"
 	"testing"
 )
 
@@ -201,7 +203,7 @@
 		_, errs = ctx.PrepareBuildActions(config)
 		android.FailIfErrored(t, errs)
 
-		bazelTargets := GenerateSoongModuleTargets(ctx.Context.Context, QueryView)[dir]
+		bazelTargets := GenerateBazelTargets(ctx.Context.Context, QueryView)[dir]
 		if actualCount, expectedCount := len(bazelTargets), 1; actualCount != expectedCount {
 			t.Fatalf("Expected %d bazel target, got %d", expectedCount, actualCount)
 		}
@@ -227,6 +229,7 @@
 	name: "foo",
     string_list_prop: ["a", "b"],
     string_prop: "a",
+    bazel_module: { bp2build_available: true },
 }`,
 			expectedBazelTarget: `custom(
     name = "foo",
@@ -248,22 +251,26 @@
 		ctx.RegisterForBazelConversion()
 
 		_, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
-		android.FailIfErrored(t, errs)
+		if Errored(t, "", errs) {
+			continue
+		}
 		_, errs = ctx.ResolveDependencies(config)
-		android.FailIfErrored(t, errs)
-
-		bazelTargets := GenerateSoongModuleTargets(ctx.Context.Context, Bp2Build)[dir]
-		if actualCount, expectedCount := len(bazelTargets), 1; actualCount != expectedCount {
-			t.Fatalf("Expected %d bazel target, got %d", expectedCount, actualCount)
+		if Errored(t, "", errs) {
+			continue
 		}
 
-		actualBazelTarget := bazelTargets[0]
-		if actualBazelTarget.content != testCase.expectedBazelTarget {
-			t.Errorf(
-				"Expected generated Bazel target to be '%s', got '%s'",
-				testCase.expectedBazelTarget,
-				actualBazelTarget.content,
-			)
+		bazelTargets := GenerateBazelTargets(ctx.Context.Context, Bp2Build)[dir]
+		if actualCount, expectedCount := len(bazelTargets), 1; actualCount != expectedCount {
+			t.Errorf("Expected %d bazel target, got %d", expectedCount, actualCount)
+		} else {
+			actualBazelTarget := bazelTargets[0]
+			if actualBazelTarget.content != testCase.expectedBazelTarget {
+				t.Errorf(
+					"Expected generated Bazel target to be '%s', got '%s'",
+					testCase.expectedBazelTarget,
+					actualBazelTarget.content,
+				)
+			}
 		}
 	}
 }
@@ -351,6 +358,12 @@
 					ruleClass: "genrule",
 					// Note: no bzlLoadLocation for native rules
 				},
+				BazelTarget{
+					name:      "sh_binary_target",
+					ruleClass: "sh_binary",
+					// Note: no bzlLoadLocation for native rules
+					// TODO(ruperts): Could open source the existing, experimental Starlark sh_ rules?
+				},
 			},
 			expectedLoadStatements: `load("//build/bazel/rules:cc.bzl", "cc_binary")
 load("//build/bazel/rules:java.bzl", "java_binary")`,
@@ -377,6 +390,7 @@
 		{
 			bp: `custom {
     name: "bar",
+    bazel_module: { bp2build_available: true  },
 }`,
 			expectedBazelTarget: `my_library(
     name = "bar",
@@ -408,7 +422,7 @@
 		_, errs = ctx.ResolveDependencies(config)
 		android.FailIfErrored(t, errs)
 
-		bazelTargets := GenerateSoongModuleTargets(ctx.Context.Context, Bp2Build)[dir]
+		bazelTargets := GenerateBazelTargets(ctx.Context.Context, Bp2Build)[dir]
 		if actualCount := len(bazelTargets); actualCount != testCase.expectedBazelTargetCount {
 			t.Fatalf("Expected %d bazel target, got %d", testCase.expectedBazelTargetCount, actualCount)
 		}
@@ -434,13 +448,32 @@
 }
 
 func TestModuleTypeBp2Build(t *testing.T) {
+	otherGenruleBp := map[string]string{
+		"other/Android.bp": `genrule {
+    name: "foo.tool",
+    out: ["foo_tool.out"],
+    srcs: ["foo_tool.in"],
+    cmd: "cp $(in) $(out)",
+}
+genrule {
+    name: "other.tool",
+    out: ["other_tool.out"],
+    srcs: ["other_tool.in"],
+    cmd: "cp $(in) $(out)",
+}`,
+	}
+
 	testCases := []struct {
+		description                        string
 		moduleTypeUnderTest                string
 		moduleTypeUnderTestFactory         android.ModuleFactory
 		moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext)
+		preArchMutators                    []android.RegisterMutatorFunc
+		depsMutators                       []android.RegisterMutatorFunc
 		bp                                 string
-		expectedBazelTarget                string
-		description                        string
+		expectedBazelTargets               []string
+		fs                                 map[string]string
+		dir                                string
 	}{
 		{
 			description:                        "filegroup with no srcs",
@@ -448,14 +481,17 @@
 			moduleTypeUnderTestFactory:         android.FileGroupFactory,
 			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
 			bp: `filegroup {
-	name: "foo",
-	srcs: [],
+    name: "fg_foo",
+    srcs: [],
+    bazel_module: { bp2build_available: true },
 }`,
-			expectedBazelTarget: `filegroup(
-    name = "foo",
+			expectedBazelTargets: []string{
+				`filegroup(
+    name = "fg_foo",
     srcs = [
     ],
 )`,
+			},
 		},
 		{
 			description:                        "filegroup with srcs",
@@ -463,30 +499,148 @@
 			moduleTypeUnderTestFactory:         android.FileGroupFactory,
 			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
 			bp: `filegroup {
-	name: "foo",
-	srcs: ["a", "b"],
+    name: "fg_foo",
+    srcs: ["a", "b"],
+    bazel_module: { bp2build_available: true },
 }`,
-			expectedBazelTarget: `filegroup(
-    name = "foo",
+			expectedBazelTargets: []string{`filegroup(
+    name = "fg_foo",
     srcs = [
         "a",
         "b",
     ],
 )`,
+			},
+		},
+		{
+			description:                        "filegroup with excludes srcs",
+			moduleTypeUnderTest:                "filegroup",
+			moduleTypeUnderTestFactory:         android.FileGroupFactory,
+			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+			bp: `filegroup {
+    name: "fg_foo",
+    srcs: ["a", "b"],
+    exclude_srcs: ["a"],
+    bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{`filegroup(
+    name = "fg_foo",
+    srcs = [
+        "b",
+    ],
+)`,
+			},
+		},
+		{
+			description:                        "filegroup with glob",
+			moduleTypeUnderTest:                "filegroup",
+			moduleTypeUnderTestFactory:         android.FileGroupFactory,
+			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+			bp: `filegroup {
+    name: "foo",
+    srcs: ["**/*.txt"],
+    bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{`filegroup(
+    name = "foo",
+    srcs = [
+        "other/a.txt",
+        "other/b.txt",
+        "other/subdir/a.txt",
+    ],
+)`,
+			},
+			fs: map[string]string{
+				"other/a.txt":        "",
+				"other/b.txt":        "",
+				"other/subdir/a.txt": "",
+				"other/file":         "",
+			},
+		},
+		{
+			description:                        "filegroup with glob in subdir",
+			moduleTypeUnderTest:                "filegroup",
+			moduleTypeUnderTestFactory:         android.FileGroupFactory,
+			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+			bp: `filegroup {
+    name: "foo",
+    srcs: ["a.txt"],
+    bazel_module: { bp2build_available: true },
+}`,
+			dir: "other",
+			expectedBazelTargets: []string{`filegroup(
+    name = "fg_foo",
+    srcs = [
+        "a.txt",
+        "b.txt",
+        "subdir/a.txt",
+    ],
+)`,
+			},
+			fs: map[string]string{
+				"other/Android.bp": `filegroup {
+    name: "fg_foo",
+    srcs: ["**/*.txt"],
+    bazel_module: { bp2build_available: true },
+}`,
+				"other/a.txt":        "",
+				"other/b.txt":        "",
+				"other/subdir/a.txt": "",
+				"other/file":         "",
+			},
+		},
+		{
+			description:                        "depends_on_other_dir_module",
+			moduleTypeUnderTest:                "filegroup",
+			moduleTypeUnderTestFactory:         android.FileGroupFactory,
+			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+			bp: `filegroup {
+    name: "foobar",
+    srcs: [
+      ":foo",
+        "c",
+    ],
+    bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{`filegroup(
+    name = "foobar",
+    srcs = [
+        "//other:foo",
+        "c",
+    ],
+)`,
+			},
+			fs: map[string]string{
+				"other/Android.bp": `filegroup {
+    name: "foo",
+    srcs: ["a", "b"],
+}`,
+			},
 		},
 		{
 			description:                        "genrule with command line variable replacements",
 			moduleTypeUnderTest:                "genrule",
 			moduleTypeUnderTestFactory:         genrule.GenRuleFactory,
 			moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
+			depsMutators:                       []android.RegisterMutatorFunc{genrule.RegisterGenruleBp2BuildDeps},
 			bp: `genrule {
+    name: "foo.tool",
+    out: ["foo_tool.out"],
+    srcs: ["foo_tool.in"],
+    cmd: "cp $(in) $(out)",
+    bazel_module: { bp2build_available: true },
+}
+
+genrule {
     name: "foo",
     out: ["foo.out"],
     srcs: ["foo.in"],
     tools: [":foo.tool"],
     cmd: "$(location :foo.tool) --genDir=$(genDir) arg $(in) $(out)",
+    bazel_module: { bp2build_available: true },
 }`,
-			expectedBazelTarget: `genrule(
+			expectedBazelTargets: []string{
+				`genrule(
     name = "foo",
     cmd = "$(location :foo.tool) --genDir=$(GENDIR) arg $(SRCS) $(OUTS)",
     outs = [
@@ -499,20 +653,41 @@
         ":foo.tool",
     ],
 )`,
+				`genrule(
+    name = "foo.tool",
+    cmd = "cp $(SRCS) $(OUTS)",
+    outs = [
+        "foo_tool.out",
+    ],
+    srcs = [
+        "foo_tool.in",
+    ],
+)`,
+			},
 		},
 		{
 			description:                        "genrule using $(locations :label)",
 			moduleTypeUnderTest:                "genrule",
 			moduleTypeUnderTestFactory:         genrule.GenRuleFactory,
 			moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
+			depsMutators:                       []android.RegisterMutatorFunc{genrule.RegisterGenruleBp2BuildDeps},
 			bp: `genrule {
+    name: "foo.tools",
+    out: ["foo_tool.out", "foo_tool2.out"],
+    srcs: ["foo_tool.in"],
+    cmd: "cp $(in) $(out)",
+    bazel_module: { bp2build_available: true },
+}
+
+genrule {
     name: "foo",
     out: ["foo.out"],
     srcs: ["foo.in"],
     tools: [":foo.tools"],
     cmd: "$(locations :foo.tools) -s $(out) $(in)",
+    bazel_module: { bp2build_available: true },
 }`,
-			expectedBazelTarget: `genrule(
+			expectedBazelTargets: []string{`genrule(
     name = "foo",
     cmd = "$(locations :foo.tools) -s $(OUTS) $(SRCS)",
     outs = [
@@ -525,22 +700,96 @@
         ":foo.tools",
     ],
 )`,
+				`genrule(
+    name = "foo.tools",
+    cmd = "cp $(SRCS) $(OUTS)",
+    outs = [
+        "foo_tool.out",
+        "foo_tool2.out",
+    ],
+    srcs = [
+        "foo_tool.in",
+    ],
+)`,
+			},
+		},
+		{
+			description:                        "genrule using $(locations //absolute:label)",
+			moduleTypeUnderTest:                "genrule",
+			moduleTypeUnderTestFactory:         genrule.GenRuleFactory,
+			moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
+			depsMutators:                       []android.RegisterMutatorFunc{genrule.RegisterGenruleBp2BuildDeps},
+			bp: `genrule {
+    name: "foo",
+    out: ["foo.out"],
+    srcs: ["foo.in"],
+    tool_files: [":foo.tool"],
+    cmd: "$(locations :foo.tool) -s $(out) $(in)",
+    bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{`genrule(
+    name = "foo",
+    cmd = "$(locations //other:foo.tool) -s $(OUTS) $(SRCS)",
+    outs = [
+        "foo.out",
+    ],
+    srcs = [
+        "foo.in",
+    ],
+    tools = [
+        "//other:foo.tool",
+    ],
+)`,
+			},
+			fs: otherGenruleBp,
+		},
+		{
+			description:                        "genrule srcs using $(locations //absolute:label)",
+			moduleTypeUnderTest:                "genrule",
+			moduleTypeUnderTestFactory:         genrule.GenRuleFactory,
+			moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
+			depsMutators:                       []android.RegisterMutatorFunc{genrule.RegisterGenruleBp2BuildDeps},
+			bp: `genrule {
+    name: "foo",
+    out: ["foo.out"],
+    srcs: [":other.tool"],
+    tool_files: [":foo.tool"],
+    cmd: "$(locations :foo.tool) -s $(out) $(location :other.tool)",
+    bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{`genrule(
+    name = "foo",
+    cmd = "$(locations //other:foo.tool) -s $(OUTS) $(location //other:other.tool)",
+    outs = [
+        "foo.out",
+    ],
+    srcs = [
+        "//other:other.tool",
+    ],
+    tools = [
+        "//other:foo.tool",
+    ],
+)`,
+			},
+			fs: otherGenruleBp,
 		},
 		{
 			description:                        "genrule using $(location) label should substitute first tool label automatically",
 			moduleTypeUnderTest:                "genrule",
 			moduleTypeUnderTestFactory:         genrule.GenRuleFactory,
 			moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
+			depsMutators:                       []android.RegisterMutatorFunc{genrule.RegisterGenruleBp2BuildDeps},
 			bp: `genrule {
     name: "foo",
     out: ["foo.out"],
     srcs: ["foo.in"],
     tool_files: [":foo.tool", ":other.tool"],
     cmd: "$(location) -s $(out) $(in)",
+    bazel_module: { bp2build_available: true },
 }`,
-			expectedBazelTarget: `genrule(
+			expectedBazelTargets: []string{`genrule(
     name = "foo",
-    cmd = "$(location :foo.tool) -s $(OUTS) $(SRCS)",
+    cmd = "$(location //other:foo.tool) -s $(OUTS) $(SRCS)",
     outs = [
         "foo.out",
     ],
@@ -548,26 +797,30 @@
         "foo.in",
     ],
     tools = [
-        ":foo.tool",
-        ":other.tool",
+        "//other:foo.tool",
+        "//other:other.tool",
     ],
 )`,
+			},
+			fs: otherGenruleBp,
 		},
 		{
 			description:                        "genrule using $(locations) label should substitute first tool label automatically",
 			moduleTypeUnderTest:                "genrule",
 			moduleTypeUnderTestFactory:         genrule.GenRuleFactory,
 			moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
+			depsMutators:                       []android.RegisterMutatorFunc{genrule.RegisterGenruleBp2BuildDeps},
 			bp: `genrule {
     name: "foo",
     out: ["foo.out"],
     srcs: ["foo.in"],
     tools: [":foo.tool", ":other.tool"],
     cmd: "$(locations) -s $(out) $(in)",
+    bazel_module: { bp2build_available: true },
 }`,
-			expectedBazelTarget: `genrule(
+			expectedBazelTargets: []string{`genrule(
     name = "foo",
-    cmd = "$(locations :foo.tool) -s $(OUTS) $(SRCS)",
+    cmd = "$(locations //other:foo.tool) -s $(OUTS) $(SRCS)",
     outs = [
         "foo.out",
     ],
@@ -575,23 +828,27 @@
         "foo.in",
     ],
     tools = [
-        ":foo.tool",
-        ":other.tool",
+        "//other:foo.tool",
+        "//other:other.tool",
     ],
 )`,
+			},
+			fs: otherGenruleBp,
 		},
 		{
 			description:                        "genrule without tools or tool_files can convert successfully",
 			moduleTypeUnderTest:                "genrule",
 			moduleTypeUnderTestFactory:         genrule.GenRuleFactory,
 			moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
+			depsMutators:                       []android.RegisterMutatorFunc{genrule.RegisterGenruleBp2BuildDeps},
 			bp: `genrule {
     name: "foo",
     out: ["foo.out"],
     srcs: ["foo.in"],
     cmd: "cp $(in) $(out)",
+    bazel_module: { bp2build_available: true },
 }`,
-			expectedBazelTarget: `genrule(
+			expectedBazelTargets: []string{`genrule(
     name = "foo",
     cmd = "cp $(SRCS) $(OUTS)",
     outs = [
@@ -601,39 +858,90 @@
         "foo.in",
     ],
 )`,
+			},
+		},
+		{
+			description:                        "sh_binary test",
+			moduleTypeUnderTest:                "sh_binary",
+			moduleTypeUnderTestFactory:         sh.ShBinaryFactory,
+			moduleTypeUnderTestBp2BuildMutator: sh.ShBinaryBp2Build,
+			bp: `sh_binary {
+    name: "foo",
+    src: "foo.sh",
+    bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{`sh_binary(
+    name = "foo",
+    srcs = [
+        "foo.sh",
+    ],
+)`},
 		},
 	}
 
 	dir := "."
 	for _, testCase := range testCases {
-		config := android.TestConfig(buildDir, nil, testCase.bp, nil)
+		fs := make(map[string][]byte)
+		toParse := []string{
+			"Android.bp",
+		}
+		for f, content := range testCase.fs {
+			if strings.HasSuffix(f, "Android.bp") {
+				toParse = append(toParse, f)
+			}
+			fs[f] = []byte(content)
+		}
+		config := android.TestConfig(buildDir, nil, testCase.bp, fs)
 		ctx := android.NewTestContext(config)
 		ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
+		for _, m := range testCase.depsMutators {
+			ctx.DepsBp2BuildMutators(m)
+		}
 		ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
 		ctx.RegisterForBazelConversion()
 
-		_, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
-		android.FailIfErrored(t, errs)
+		_, errs := ctx.ParseFileList(dir, toParse)
+		if Errored(t, testCase.description, errs) {
+			continue
+		}
 		_, errs = ctx.ResolveDependencies(config)
-		android.FailIfErrored(t, errs)
-
-		bazelTargets := GenerateSoongModuleTargets(ctx.Context.Context, Bp2Build)[dir]
-		if actualCount, expectedCount := len(bazelTargets), 1; actualCount != expectedCount {
-			t.Fatalf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount)
+		if Errored(t, testCase.description, errs) {
+			continue
 		}
 
-		actualBazelTarget := bazelTargets[0]
-		if actualBazelTarget.content != testCase.expectedBazelTarget {
-			t.Errorf(
-				"%s: Expected generated Bazel target to be '%s', got '%s'",
-				testCase.description,
-				testCase.expectedBazelTarget,
-				actualBazelTarget.content,
-			)
+		checkDir := dir
+		if testCase.dir != "" {
+			checkDir = testCase.dir
+		}
+		bazelTargets := GenerateBazelTargets(ctx.Context.Context, Bp2Build)[checkDir]
+		if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
+			t.Errorf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount)
+		} else {
+			for i, target := range bazelTargets {
+				if w, g := testCase.expectedBazelTargets[i], target.content; w != g {
+					t.Errorf(
+						"%s: Expected generated Bazel target to be '%s', got '%s'",
+						testCase.description,
+						w,
+						g,
+					)
+				}
+			}
 		}
 	}
 }
 
+func Errored(t *testing.T, desc string, errs []error) bool {
+	t.Helper()
+	if len(errs) > 0 {
+		for _, err := range errs {
+			t.Errorf("%s: %s", desc, err)
+		}
+		return true
+	}
+	return false
+}
+
 type bp2buildMutator = func(android.TopDownMutatorContext)
 
 func TestBp2BuildInlinesDefaults(t *testing.T) {
@@ -661,6 +969,7 @@
     out: ["out"],
     srcs: ["in1"],
     defaults: ["gen_defaults"],
+    bazel_module: { bp2build_available: true },
 }
 `,
 			expectedBazelTarget: `genrule(
@@ -695,6 +1004,7 @@
     srcs: ["in1"],
     defaults: ["gen_defaults"],
     cmd: "do-something $(in) $(out)",
+    bazel_module: { bp2build_available: true },
 }
 `,
 			expectedBazelTarget: `genrule(
@@ -733,6 +1043,7 @@
     name: "gen",
     out: ["out"],
     defaults: ["gen_defaults1", "gen_defaults2"],
+    bazel_module: { bp2build_available: true },
 }
 `,
 			expectedBazelTarget: `genrule(
@@ -779,6 +1090,7 @@
     name: "gen",
     out: ["out"],
     defaults: ["gen_defaults1"],
+    bazel_module: { bp2build_available: true },
 }
 `,
 			expectedBazelTarget: `genrule(
@@ -815,7 +1127,7 @@
 		_, errs = ctx.ResolveDependencies(config)
 		android.FailIfErrored(t, errs)
 
-		bazelTargets := GenerateSoongModuleTargets(ctx.Context.Context, Bp2Build)[dir]
+		bazelTargets := GenerateBazelTargets(ctx.Context.Context, Bp2Build)[dir]
 		if actualCount := len(bazelTargets); actualCount != 1 {
 			t.Fatalf("%s: Expected 1 bazel target, got %d", testCase.description, actualCount)
 		}
@@ -831,3 +1143,80 @@
 		}
 	}
 }
+
+func TestAllowlistingBp2buildTargets(t *testing.T) {
+	testCases := []struct {
+		moduleTypeUnderTest                string
+		moduleTypeUnderTestFactory         android.ModuleFactory
+		moduleTypeUnderTestBp2BuildMutator bp2buildMutator
+		bp                                 string
+		expectedCount                      int
+		description                        string
+	}{
+		{
+			description:                        "explicitly unavailable",
+			moduleTypeUnderTest:                "filegroup",
+			moduleTypeUnderTestFactory:         android.FileGroupFactory,
+			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+			bp: `filegroup {
+    name: "foo",
+    srcs: ["a", "b"],
+    bazel_module: { bp2build_available: false },
+}`,
+			expectedCount: 0,
+		},
+		{
+			description:                        "implicitly unavailable",
+			moduleTypeUnderTest:                "filegroup",
+			moduleTypeUnderTestFactory:         android.FileGroupFactory,
+			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+			bp: `filegroup {
+    name: "foo",
+    srcs: ["a", "b"],
+}`,
+			expectedCount: 0,
+		},
+		{
+			description:                        "explicitly available",
+			moduleTypeUnderTest:                "filegroup",
+			moduleTypeUnderTestFactory:         android.FileGroupFactory,
+			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+			bp: `filegroup {
+    name: "foo",
+    srcs: ["a", "b"],
+    bazel_module: { bp2build_available: true },
+}`,
+			expectedCount: 1,
+		},
+		{
+			description:                        "generates more than 1 target if needed",
+			moduleTypeUnderTest:                "custom",
+			moduleTypeUnderTestFactory:         customModuleFactory,
+			moduleTypeUnderTestBp2BuildMutator: customBp2BuildMutatorFromStarlark,
+			bp: `custom {
+    name: "foo",
+    bazel_module: { bp2build_available: true },
+}`,
+			expectedCount: 3,
+		},
+	}
+
+	dir := "."
+	for _, testCase := range testCases {
+		config := android.TestConfig(buildDir, nil, testCase.bp, nil)
+		ctx := android.NewTestContext(config)
+		ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
+		ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
+		ctx.RegisterForBazelConversion()
+
+		_, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
+		android.FailIfErrored(t, errs)
+		_, errs = ctx.ResolveDependencies(config)
+		android.FailIfErrored(t, errs)
+
+		bazelTargets := GenerateBazelTargets(ctx.Context.Context, Bp2Build)[dir]
+		if actualCount := len(bazelTargets); actualCount != testCase.expectedCount {
+			t.Fatalf("%s: Expected %d bazel target, got %d", testCase.description, testCase.expectedCount, actualCount)
+		}
+	}
+}
diff --git a/bp2build/bzl_conversion_test.go b/bp2build/bzl_conversion_test.go
index f2a4058..30c1a5b 100644
--- a/bp2build/bzl_conversion_test.go
+++ b/bp2build/bzl_conversion_test.go
@@ -86,6 +86,10 @@
         "soong_module_name": attr.string(mandatory = True),
         "soong_module_variant": attr.string(),
         "soong_module_deps": attr.label_list(providers = [SoongModuleInfo]),
+        # bazel_module start
+#         "label": attr.string(),
+#         "bp2build_available": attr.bool(),
+        # bazel_module end
         "bool_prop": attr.bool(),
         "bool_ptr_prop": attr.bool(),
         "int64_ptr_prop": attr.int(),
diff --git a/bp2build/conversion.go b/bp2build/conversion.go
index 62cd8d4..081e082 100644
--- a/bp2build/conversion.go
+++ b/bp2build/conversion.go
@@ -50,14 +50,19 @@
 		sort.Slice(targets, func(i, j int) bool { return targets[i].name < targets[j].name })
 		content := soongModuleLoad
 		if mode == Bp2Build {
-			content = targets.LoadStatements()
+			content = `# This file was automatically generated by bp2build for the Bazel migration project.
+# Feel free to edit or test it, but do *not* check it into your version control system.`
+			content += "\n\n"
+			content += "package(default_visibility = [\"//visibility:public\"])"
+			content += "\n\n"
+			content += targets.LoadStatements()
 		}
 		if content != "" {
 			// If there are load statements, add a couple of newlines.
 			content += "\n\n"
 		}
 		content += targets.String()
-		files = append(files, newFile(dir, "BUILD.bazel", content))
+		files = append(files, newFile(dir, "BUILD", content))
 	}
 	return files
 }
diff --git a/bp2build/testing.go b/bp2build/testing.go
index 5e6481b..2e59999 100644
--- a/bp2build/testing.go
+++ b/bp2build/testing.go
@@ -3,8 +3,6 @@
 import (
 	"android/soong/android"
 	"android/soong/bazel"
-
-	"github.com/google/blueprint/proptools"
 )
 
 type nestedProps struct {
@@ -29,6 +27,8 @@
 	android.ModuleBase
 
 	props customProps
+
+	bazelProps bazel.Properties
 }
 
 // OutputFiles is needed because some instances of this module use dist with a
@@ -44,6 +44,7 @@
 func customModuleFactoryBase() android.Module {
 	module := &customModule{}
 	module.AddProperties(&module.props)
+	module.AddProperties(&module.bazelProps)
 	return module
 }
 
@@ -105,7 +106,6 @@
 }
 
 type customBazelModuleAttributes struct {
-	Name             *string
 	String_prop      string
 	String_list_prop []string
 }
@@ -127,14 +127,18 @@
 
 func customBp2BuildMutator(ctx android.TopDownMutatorContext) {
 	if m, ok := ctx.Module().(*customModule); ok {
-		name := "__bp2build__" + m.Name()
-		ctx.CreateModule(customBazelModuleFactory, &customBazelModuleAttributes{
-			Name:             proptools.StringPtr(name),
+		if !m.bazelProps.Bazel_module.Bp2build_available {
+			return
+		}
+
+		attrs := &customBazelModuleAttributes{
 			String_prop:      m.props.String_prop,
 			String_list_prop: m.props.String_list_prop,
-		}, &bazel.BazelTargetModuleProperties{
-			Rule_class: "custom",
-		})
+		}
+
+		props := bazel.NewBazelTargetModuleProperties(m.Name(), "custom", "")
+
+		ctx.CreateBazelTargetModule(customBazelModuleFactory, props, attrs)
 	}
 }
 
@@ -142,24 +146,32 @@
 // module to target.
 func customBp2BuildMutatorFromStarlark(ctx android.TopDownMutatorContext) {
 	if m, ok := ctx.Module().(*customModule); ok {
-		baseName := "__bp2build__" + m.Name()
-		ctx.CreateModule(customBazelModuleFactory, &customBazelModuleAttributes{
-			Name: proptools.StringPtr(baseName),
-		}, &bazel.BazelTargetModuleProperties{
-			Rule_class:        "my_library",
-			Bzl_load_location: "//build/bazel/rules:rules.bzl",
-		})
-		ctx.CreateModule(customBazelModuleFactory, &customBazelModuleAttributes{
-			Name: proptools.StringPtr(baseName + "_proto_library_deps"),
-		}, &bazel.BazelTargetModuleProperties{
-			Rule_class:        "proto_library",
-			Bzl_load_location: "//build/bazel/rules:proto.bzl",
-		})
-		ctx.CreateModule(customBazelModuleFactory, &customBazelModuleAttributes{
-			Name: proptools.StringPtr(baseName + "_my_proto_library_deps"),
-		}, &bazel.BazelTargetModuleProperties{
-			Rule_class:        "my_proto_library",
-			Bzl_load_location: "//build/bazel/rules:proto.bzl",
-		})
+		if !m.bazelProps.Bazel_module.Bp2build_available {
+			return
+		}
+
+		baseName := m.Name()
+		attrs := &customBazelModuleAttributes{}
+
+		myLibraryProps := bazel.NewBazelTargetModuleProperties(
+			baseName,
+			"my_library",
+			"//build/bazel/rules:rules.bzl",
+		)
+		ctx.CreateBazelTargetModule(customBazelModuleFactory, myLibraryProps, attrs)
+
+		protoLibraryProps := bazel.NewBazelTargetModuleProperties(
+			baseName+"_proto_library_deps",
+			"proto_library",
+			"//build/bazel/rules:proto.bzl",
+		)
+		ctx.CreateBazelTargetModule(customBazelModuleFactory, protoLibraryProps, attrs)
+
+		myProtoLibraryProps := bazel.NewBazelTargetModuleProperties(
+			baseName+"_my_proto_library_deps",
+			"my_proto_library",
+			"//build/bazel/rules:proto.bzl",
+		)
+		ctx.CreateBazelTargetModule(customBazelModuleFactory, myProtoLibraryProps, attrs)
 	}
 }
diff --git a/cc/androidmk.go b/cc/androidmk.go
index ddb81d9..8652c10 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -26,9 +26,9 @@
 var (
 	nativeBridgeSuffix  = ".native_bridge"
 	productSuffix       = ".product"
-	vendorSuffix        = ".vendor"
+	VendorSuffix        = ".vendor"
 	ramdiskSuffix       = ".ramdisk"
-	vendorRamdiskSuffix = ".vendor_ramdisk"
+	VendorRamdiskSuffix = ".vendor_ramdisk"
 	recoverySuffix      = ".recovery"
 	sdkSuffix           = ".sdk"
 )
diff --git a/cc/binary.go b/cc/binary.go
index 71c865b..999b82c 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -182,7 +182,7 @@
 		}
 	}
 
-	if !binary.static() && inList("libc", deps.StaticLibs) {
+	if !binary.static() && inList("libc", deps.StaticLibs) && !ctx.BazelConversionMode() {
 		ctx.ModuleErrorf("statically linking libc to dynamic executable, please remove libc\n" +
 			"from static libs or set static_executable: true")
 	}
diff --git a/cc/cc.go b/cc/cc.go
index d282b6e..7f59158 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -1535,11 +1535,16 @@
 	var vndkVersion string
 	var nameSuffix string
 	if c.InProduct() {
+		if c.ProductSpecific() {
+			// If the module is product specific with 'product_specific: true',
+			// do not add a name suffix because it is a base module.
+			return ""
+		}
 		vndkVersion = ctx.DeviceConfig().ProductVndkVersion()
 		nameSuffix = productSuffix
 	} else {
 		vndkVersion = ctx.DeviceConfig().VndkVersion()
-		nameSuffix = vendorSuffix
+		nameSuffix = VendorSuffix
 	}
 	if vndkVersion == "current" {
 		vndkVersion = ctx.DeviceConfig().PlatformVndkVersion()
@@ -1586,11 +1591,11 @@
 	} else if _, ok := c.linker.(*vndkPrebuiltLibraryDecorator); ok {
 		// .vendor suffix is added for backward compatibility with VNDK snapshot whose names with
 		// such suffixes are already hard-coded in prebuilts/vndk/.../Android.bp.
-		c.Properties.SubName += vendorSuffix
+		c.Properties.SubName += VendorSuffix
 	} else if c.InRamdisk() && !c.OnlyInRamdisk() {
 		c.Properties.SubName += ramdiskSuffix
 	} else if c.InVendorRamdisk() && !c.OnlyInVendorRamdisk() {
-		c.Properties.SubName += vendorRamdiskSuffix
+		c.Properties.SubName += VendorRamdiskSuffix
 	} else if c.InRecovery() && !c.OnlyInRecovery() {
 		c.Properties.SubName += recoverySuffix
 	} else if c.IsSdkVariant() && (c.Properties.SdkAndPlatformVariantVisibleToMake || c.SplitPerApiLevel()) {
@@ -1741,7 +1746,7 @@
 
 func (c *Module) toolchain(ctx android.BaseModuleContext) config.Toolchain {
 	if c.cachedToolchain == nil {
-		c.cachedToolchain = config.FindToolchain(ctx.Os(), ctx.Arch())
+		c.cachedToolchain = config.FindToolchainWithContext(ctx)
 	}
 	return c.cachedToolchain
 }
@@ -1833,6 +1838,12 @@
 	deps.HeaderLibs = android.LastUniqueStrings(deps.HeaderLibs)
 	deps.RuntimeLibs = android.LastUniqueStrings(deps.RuntimeLibs)
 
+	// In Bazel conversion mode, we dependency and build validations will occur in Bazel, so there is
+	// no need to do so in Soong.
+	if ctx.BazelConversionMode() {
+		return deps
+	}
+
 	for _, lib := range deps.ReexportSharedLibHeaders {
 		if !inList(lib, deps.SharedLibs) {
 			ctx.PropertyErrorf("export_shared_lib_headers", "Shared library not in shared_libs: '%s'", lib)
@@ -2891,12 +2902,12 @@
 	ccDepModule, _ := ccDep.(*Module)
 	isLLndk := ccDepModule != nil && ccDepModule.IsLlndk()
 	isVendorPublicLib := inList(libName, *vendorPublicLibraries)
-	bothVendorAndCoreVariantsExist := ccDep.HasVendorVariant() || isLLndk
+	nonSystemVariantsExist := ccDep.HasNonSystemVariants() || isLLndk
 
-	if c, ok := ccDep.(*Module); ok {
+	if ccDepModule != nil {
 		// Use base module name for snapshots when exporting to Makefile.
-		if snapshotPrebuilt, ok := c.linker.(snapshotInterface); ok {
-			baseName := c.BaseModuleName()
+		if snapshotPrebuilt, ok := ccDepModule.linker.(snapshotInterface); ok {
+			baseName := ccDepModule.BaseModuleName()
 
 			return baseName + snapshotPrebuilt.snapshotAndroidMkSuffix()
 		}
@@ -2907,16 +2918,16 @@
 		// The vendor module is a no-vendor-variant VNDK library.  Depend on the
 		// core module instead.
 		return libName
-	} else if c.UseVndk() && bothVendorAndCoreVariantsExist {
-		// The vendor module in Make will have been renamed to not conflict with the core
-		// module, so update the dependency name here accordingly.
-		return libName + c.getNameSuffixWithVndkVersion(ctx)
+	} else if ccDep.UseVndk() && nonSystemVariantsExist && ccDepModule != nil {
+		// The vendor and product modules in Make will have been renamed to not conflict with the
+		// core module, so update the dependency name here accordingly.
+		return libName + ccDepModule.Properties.SubName
 	} else if (ctx.Platform() || ctx.ProductSpecific()) && isVendorPublicLib {
 		return libName + vendorPublicLibrarySuffix
 	} else if ccDep.InRamdisk() && !ccDep.OnlyInRamdisk() {
 		return libName + ramdiskSuffix
 	} else if ccDep.InVendorRamdisk() && !ccDep.OnlyInVendorRamdisk() {
-		return libName + vendorRamdiskSuffix
+		return libName + VendorRamdiskSuffix
 	} else if ccDep.InRecovery() && !ccDep.OnlyInRecovery() {
 		return libName + recoverySuffix
 	} else if ccDep.Target().NativeBridge == android.NativeBridgeEnabled {
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 7288cc4..b1c1b2e 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -266,7 +266,7 @@
 	}
 }
 
-func checkSnapshotIncludeExclude(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, moduleName, snapshotFilename, subDir, variant string, include bool) {
+func checkSnapshotIncludeExclude(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, moduleName, snapshotFilename, subDir, variant string, include bool, fake bool) {
 	t.Helper()
 	mod, ok := ctx.ModuleForTests(moduleName, variant).Module().(android.OutputFileProducer)
 	if !ok {
@@ -282,8 +282,14 @@
 
 	if include {
 		out := singleton.Output(snapshotPath)
-		if out.Input.String() != outputFiles[0].String() {
-			t.Errorf("The input of snapshot %q must be %q, but %q", moduleName, out.Input.String(), outputFiles[0])
+		if fake {
+			if out.Rule == nil {
+				t.Errorf("Missing rule for module %q output file %q", moduleName, outputFiles[0])
+			}
+		} else {
+			if out.Input.String() != outputFiles[0].String() {
+				t.Errorf("The input of snapshot %q must be %q, but %q", moduleName, out.Input.String(), outputFiles[0])
+			}
 		}
 	} else {
 		out := singleton.MaybeOutput(snapshotPath)
@@ -294,11 +300,15 @@
 }
 
 func checkSnapshot(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, moduleName, snapshotFilename, subDir, variant string) {
-	checkSnapshotIncludeExclude(t, ctx, singleton, moduleName, snapshotFilename, subDir, variant, true)
+	checkSnapshotIncludeExclude(t, ctx, singleton, moduleName, snapshotFilename, subDir, variant, true, false)
 }
 
 func checkSnapshotExclude(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, moduleName, snapshotFilename, subDir, variant string) {
-	checkSnapshotIncludeExclude(t, ctx, singleton, moduleName, snapshotFilename, subDir, variant, false)
+	checkSnapshotIncludeExclude(t, ctx, singleton, moduleName, snapshotFilename, subDir, variant, false, false)
+}
+
+func checkSnapshotRule(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, moduleName, snapshotFilename, subDir, variant string) {
+	checkSnapshotIncludeExclude(t, ctx, singleton, moduleName, snapshotFilename, subDir, variant, true, true)
 }
 
 func checkWriteFileOutput(t *testing.T, params android.TestingBuildParams, expected []string) {
@@ -2712,6 +2722,14 @@
 		system_shared_libs : [],
 	}
 	cc_library {
+		name: "libproduct_vendor",
+		product_specific: true,
+		vendor_available: true,
+		no_libcrt : true,
+		nocrt : true,
+		system_shared_libs : [],
+	}
+	cc_library {
 		name: "libcore",
 		runtime_libs: ["liball_available"],
 		no_libcrt : true,
@@ -2728,7 +2746,7 @@
 	cc_library {
 		name: "libvendor2",
 		vendor: true,
-		runtime_libs: ["liball_available", "libvendor1"],
+		runtime_libs: ["liball_available", "libvendor1", "libproduct_vendor"],
 		no_libcrt : true,
 		nocrt : true,
 		system_shared_libs : [],
@@ -2751,7 +2769,7 @@
 	cc_library {
 		name: "libproduct2",
 		product_specific: true,
-		runtime_libs: ["liball_available", "libproduct1"],
+		runtime_libs: ["liball_available", "libproduct1", "libproduct_vendor"],
 		no_libcrt : true,
 		nocrt : true,
 		system_shared_libs : [],
@@ -2781,7 +2799,7 @@
 	checkRuntimeLibs(t, []string{"liball_available.vendor"}, module)
 
 	module = ctx.ModuleForTests("libvendor2", variant).Module().(*Module)
-	checkRuntimeLibs(t, []string{"liball_available.vendor", "libvendor1"}, module)
+	checkRuntimeLibs(t, []string{"liball_available.vendor", "libvendor1", "libproduct_vendor.vendor"}, module)
 
 	// runtime_libs for product variants have '.product' suffixes if the modules have both core
 	// and product variants.
@@ -2791,7 +2809,7 @@
 	checkRuntimeLibs(t, []string{"liball_available.product"}, module)
 
 	module = ctx.ModuleForTests("libproduct2", variant).Module().(*Module)
-	checkRuntimeLibs(t, []string{"liball_available.product", "libproduct1"}, module)
+	checkRuntimeLibs(t, []string{"liball_available.product", "libproduct1", "libproduct_vendor"}, module)
 }
 
 func TestExcludeRuntimeLibs(t *testing.T) {
@@ -2817,10 +2835,10 @@
 	checkRuntimeLibs(t, []string{"liball_available"}, module)
 
 	module = ctx.ModuleForTests("libvendor2", variant).Module().(*Module)
-	checkRuntimeLibs(t, []string{"liball_available", "libvendor1"}, module)
+	checkRuntimeLibs(t, []string{"liball_available", "libvendor1", "libproduct_vendor"}, module)
 
 	module = ctx.ModuleForTests("libproduct2", variant).Module().(*Module)
-	checkRuntimeLibs(t, []string{"liball_available", "libproduct1"}, module)
+	checkRuntimeLibs(t, []string{"liball_available", "libproduct1", "libproduct_vendor"}, module)
 }
 
 func checkStaticLibs(t *testing.T, expected []string, module *Module) {
diff --git a/cc/config/clang.go b/cc/config/clang.go
index 35dd10f..71c7626 100644
--- a/cc/config/clang.go
+++ b/cc/config/clang.go
@@ -142,6 +142,9 @@
 		// Nested and array designated initialization is nice to have.
 		"-Wno-c99-designator",
 
+		// Warnings from clang-12
+		"-Wno-gnu-folding-constant",
+
 		// Calls to the APIs that are newer than the min sdk version of the caller should be
 		// guarded with __builtin_available.
 		"-Wunguarded-availability",
diff --git a/cc/config/toolchain.go b/cc/config/toolchain.go
index db9092d..59c0422 100644
--- a/cc/config/toolchain.go
+++ b/cc/config/toolchain.go
@@ -32,12 +32,42 @@
 	toolchainFactories[os][arch] = factory
 }
 
+type toolchainContext interface {
+	Os() android.OsType
+	Arch() android.Arch
+}
+
+type conversionContext interface {
+	BazelConversionMode() bool
+}
+
+func FindToolchainWithContext(ctx toolchainContext) Toolchain {
+	t, err := findToolchain(ctx.Os(), ctx.Arch())
+	if err != nil {
+		if c, ok := ctx.(conversionContext); ok && c.BazelConversionMode() {
+			// TODO(b/179123288): determine conversion for toolchain
+			return &toolchainX86_64{}
+		} else {
+			panic(err)
+		}
+	}
+	return t
+}
+
 func FindToolchain(os android.OsType, arch android.Arch) Toolchain {
+	t, err := findToolchain(os, arch)
+	if err != nil {
+		panic(err)
+	}
+	return t
+}
+
+func findToolchain(os android.OsType, arch android.Arch) (Toolchain, error) {
 	factory := toolchainFactories[os][arch.ArchType]
 	if factory == nil {
-		panic(fmt.Errorf("Toolchain not found for %s arch %q", os.String(), arch.String()))
+		return nil, fmt.Errorf("Toolchain not found for %s arch %q", os.String(), arch.String())
 	}
-	return factory(arch)
+	return factory(arch), nil
 }
 
 type Toolchain interface {
diff --git a/cc/config/vndk.go b/cc/config/vndk.go
index 45c18c2..425e349 100644
--- a/cc/config/vndk.go
+++ b/cc/config/vndk.go
@@ -41,6 +41,9 @@
 	"android.hardware.power-V1-ndk_platform",
 	"android.hardware.power-ndk_platform",
 	"android.hardware.rebootescrow-V1-ndk_platform",
+	"android.hardware.power.stats-V1-ndk_platform",
+	"android.hardware.power.stats-ndk_platform",
+	"android.hardware.power.stats-unstable-ndk_platform",
 	"android.hardware.rebootescrow-ndk_platform",
 	"android.hardware.security.keymint-V1-ndk_platform",
 	"android.hardware.security.keymint-ndk_platform",
diff --git a/cc/library.go b/cc/library.go
index 29a3c69..65533bc 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -19,6 +19,7 @@
 	"io"
 	"path/filepath"
 	"regexp"
+	"strconv"
 	"strings"
 	"sync"
 
@@ -647,6 +648,11 @@
 		return objs
 	}
 	if library.buildStubs() {
+		symbolFile := String(library.Properties.Stubs.Symbol_file)
+		if symbolFile != "" && !strings.HasSuffix(symbolFile, ".map.txt") {
+			ctx.PropertyErrorf("symbol_file", "%q doesn't have .map.txt suffix", symbolFile)
+			return Objects{}
+		}
 		objs, versionScript := compileStubLibrary(ctx, flags, String(library.Properties.Stubs.Symbol_file), library.MutatedProperties.StubsVersion, "--apex")
 		library.versionScriptPath = android.OptionalPathForPath(versionScript)
 		return objs
@@ -1362,8 +1368,11 @@
 func (library *libraryDecorator) exportVersioningMacroIfNeeded(ctx android.BaseModuleContext) {
 	if library.buildStubs() && library.stubsVersion() != "" && !library.skipAPIDefine {
 		name := versioningMacroName(ctx.Module().(*Module).ImplementationModuleName(ctx))
-		ver := library.stubsVersion()
-		library.reexportFlags("-D" + name + "=" + ver)
+		apiLevel, err := android.ApiLevelFromUser(ctx, library.stubsVersion())
+		if err != nil {
+			ctx.ModuleErrorf("Can't export version macro: %s", err.Error())
+		}
+		library.reexportFlags("-D" + name + "=" + strconv.Itoa(apiLevel.FinalOrPreviewInt()))
 	}
 }
 
diff --git a/cc/linkable.go b/cc/linkable.go
index ab5a552..58919a0 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -106,6 +106,8 @@
 	IsVndkExt() bool
 	IsVndkPrivate() bool
 	HasVendorVariant() bool
+	HasProductVariant() bool
+	HasNonSystemVariants() bool
 	InProduct() bool
 
 	SdkVersion() string
diff --git a/cc/linker.go b/cc/linker.go
index ff07224..6d0d416 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -328,7 +328,9 @@
 		}
 
 		deps.SystemSharedLibs = linker.Properties.System_shared_libs
-		if deps.SystemSharedLibs == nil {
+		// In Bazel conversion mode, variations have not been specified, so SystemSharedLibs may
+		// inaccuarately appear unset, which can cause issues with circular dependencies.
+		if deps.SystemSharedLibs == nil && !ctx.BazelConversionMode() {
 			// Provide a default system_shared_libs if it is unspecified. Note: If an
 			// empty list [] is specified, it implies that the module declines the
 			// default system_shared_libs.
diff --git a/cc/snapshot_prebuilt.go b/cc/snapshot_prebuilt.go
index ffaed8e..62daafd 100644
--- a/cc/snapshot_prebuilt.go
+++ b/cc/snapshot_prebuilt.go
@@ -144,7 +144,7 @@
 }
 
 func (vendorSnapshotImage) moduleNameSuffix() string {
-	return vendorSuffix
+	return VendorSuffix
 }
 
 func (recoverySnapshotImage) init(ctx android.RegistrationContext) {
@@ -195,8 +195,12 @@
 }
 
 func (recoverySnapshotImage) excludeFromDirectedSnapshot(cfg android.DeviceConfig, name string) bool {
-	// directed recovery snapshot is not implemented yet
-	return false
+	// If we're using full snapshot, not directed snapshot, capture every module
+	if !cfg.DirectedRecoverySnapshot() {
+		return false
+	}
+	// Else, checks if name is in RECOVERY_SNAPSHOT_MODULES.
+	return !cfg.RecoverySnapshotModules()[name]
 }
 
 func (recoverySnapshotImage) imageVariantName(cfg android.DeviceConfig) string {
diff --git a/cc/test.go b/cc/test.go
index f715a8d..17ac534 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -19,6 +19,8 @@
 	"strconv"
 	"strings"
 
+	"github.com/google/blueprint/proptools"
+
 	"android/soong/android"
 	"android/soong/tradefed"
 )
@@ -230,6 +232,7 @@
 type testDecorator struct {
 	Properties TestProperties
 	linker     *baseLinker
+	hod        android.HostOrDeviceSupported
 }
 
 func (test *testDecorator) gtest() bool {
@@ -429,6 +432,10 @@
 		ctx.PropertyErrorf("no_named_install_directory", "Module install directory may only be disabled if relative_install_path is set")
 	}
 
+	// TODO(179092189): Clean up to use Ctx.Host() when generalizing to cc_test
+	if test.testDecorator.hod == android.HostSupported && test.gtest() && test.Properties.Test_options.Unit_test == nil {
+		test.Properties.Test_options.Unit_test = proptools.BoolPtr(true)
+	}
 	test.binaryDecorator.baseInstaller.install(ctx, file)
 }
 
@@ -440,6 +447,7 @@
 	test := &testBinary{
 		testDecorator: testDecorator{
 			linker: binary.baseLinker,
+			hod:    hod,
 		},
 		binaryDecorator: binary,
 		baseCompiler:    NewBaseCompiler(),
diff --git a/cc/testing.go b/cc/testing.go
index 3a5bd17..45e5312 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -43,6 +43,7 @@
 			name: "libatomic",
 			defaults: ["linux_bionic_supported"],
 			vendor_available: true,
+			vendor_ramdisk_available: true,
 			product_available: true,
 			recovery_available: true,
 			native_bridge_supported: true,
@@ -52,6 +53,7 @@
 		toolchain_library {
 			name: "libcompiler_rt-extras",
 			vendor_available: true,
+			vendor_ramdisk_available: true,
 			product_available: true,
 			recovery_available: true,
 			src: "",
@@ -60,6 +62,7 @@
 		toolchain_library {
 			name: "libclang_rt.builtins-arm-android",
 			vendor_available: true,
+			vendor_ramdisk_available: true,
 			product_available: true,
 			recovery_available: true,
 			native_bridge_supported: true,
@@ -69,6 +72,7 @@
 		toolchain_library {
 			name: "libclang_rt.builtins-aarch64-android",
 			vendor_available: true,
+			vendor_ramdisk_available: true,
 			product_available: true,
 			recovery_available: true,
 			native_bridge_supported: true,
@@ -93,6 +97,7 @@
 		toolchain_library {
 			name: "libclang_rt.builtins-i686-android",
 			vendor_available: true,
+			vendor_ramdisk_available: true,
 			product_available: true,
 			recovery_available: true,
 			native_bridge_supported: true,
@@ -103,6 +108,7 @@
 			name: "libclang_rt.builtins-x86_64-android",
 			defaults: ["linux_bionic_supported"],
 			vendor_available: true,
+			vendor_ramdisk_available: true,
 			product_available: true,
 			recovery_available: true,
 			native_bridge_supported: true,
@@ -113,6 +119,7 @@
 			name: "libunwind",
 			defaults: ["linux_bionic_supported"],
 			vendor_available: true,
+			vendor_ramdisk_available: true,
 			product_available: true,
 			recovery_available: true,
 			native_bridge_supported: true,
@@ -238,6 +245,7 @@
 		cc_library {
 			name: "libprofile-extras",
 			vendor_available: true,
+			vendor_ramdisk_available: true,
 			product_available: true,
 			recovery_available: true,
 			native_coverage: false,
@@ -248,6 +256,7 @@
 		cc_library {
 			name: "libprofile-clang-extras",
 			vendor_available: true,
+			vendor_ramdisk_available: true,
 			product_available: true,
 			recovery_available: true,
 			native_coverage: false,
@@ -319,6 +328,7 @@
 			system_shared_libs: [],
 			stl: "none",
 			vendor_available: true,
+			vendor_ramdisk_available: true,
 			product_available: true,
 			recovery_available: true,
 			host_supported: true,
@@ -356,6 +366,7 @@
 			stl: "none",
 			host_supported: false,
 			vendor_available: true,
+			vendor_ramdisk_available: true,
 			product_available: true,
 			recovery_available: true,
 			min_sdk_version: "29",
@@ -380,6 +391,7 @@
 			defaults: ["linux_bionic_supported"],
 			recovery_available: true,
 			vendor_available: true,
+			vendor_ramdisk_available: true,
 			product_available: true,
 			native_bridge_supported: true,
 			stl: "none",
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index 35fc1c1..7077b71 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -239,10 +239,6 @@
 	if _, ok := m.linker.(*llndkHeadersDecorator); ok {
 		return false
 	}
-	// If we are using directed snapshot AND we have to exclude this module, skip this
-	if image.excludeFromDirectedSnapshot(cfg, m.BaseModuleName()) {
-		return false
-	}
 
 	// Libraries
 	if l, ok := m.linker.(snapshotLibraryInterface); ok {
@@ -371,18 +367,19 @@
 
 	var headers android.Paths
 
-	copyFile := copyFileRule
-	if c.fake {
-		// All prebuilt binaries and headers are installed by copyFile function. This makes a fake
-		// snapshot just touch prebuilts and headers, rather than installing real files.
-		copyFile = func(ctx android.SingletonContext, path android.Path, out string) android.OutputPath {
+	copyFile := func(ctx android.SingletonContext, path android.Path, out string, fake bool) android.OutputPath {
+		if fake {
+			// All prebuilt binaries and headers are installed by copyFile function. This makes a fake
+			// snapshot just touch prebuilts and headers, rather than installing real files.
 			return writeStringToFileRule(ctx, "", out)
+		} else {
+			return copyFileRule(ctx, path, out)
 		}
 	}
 
 	// installSnapshot function copies prebuilt file (.so, .a, or executable) and json flag file.
 	// For executables, init_rc and vintf_fragments files are also copied.
-	installSnapshot := func(m *Module) android.Paths {
+	installSnapshot := func(m *Module, fake bool) android.Paths {
 		targetArch := "arch-" + m.Target().Arch.ArchType.String()
 		if m.Target().Arch.ArchVariant != "" {
 			targetArch += "-" + m.Target().Arch.ArchVariant
@@ -419,7 +416,7 @@
 			out := filepath.Join(configsDir, path.Base())
 			if !installedConfigs[out] {
 				installedConfigs[out] = true
-				ret = append(ret, copyFile(ctx, path, out))
+				ret = append(ret, copyFile(ctx, path, out, fake))
 			}
 		}
 
@@ -470,7 +467,7 @@
 					prop.ModuleName += ".cfi"
 				}
 				snapshotLibOut := filepath.Join(snapshotArchDir, targetArch, libType, stem)
-				ret = append(ret, copyFile(ctx, libPath, snapshotLibOut))
+				ret = append(ret, copyFile(ctx, libPath, snapshotLibOut, fake))
 			} else {
 				stem = ctx.ModuleName(m)
 			}
@@ -484,7 +481,7 @@
 			// install bin
 			binPath := m.outputFile.Path()
 			snapshotBinOut := filepath.Join(snapshotArchDir, targetArch, "binary", binPath.Base())
-			ret = append(ret, copyFile(ctx, binPath, snapshotBinOut))
+			ret = append(ret, copyFile(ctx, binPath, snapshotBinOut, fake))
 			propOut = snapshotBinOut + ".json"
 		} else if m.object() {
 			// object files aren't installed to the device, so their names can conflict.
@@ -492,7 +489,7 @@
 			objPath := m.outputFile.Path()
 			snapshotObjOut := filepath.Join(snapshotArchDir, targetArch, "object",
 				ctx.ModuleName(m)+filepath.Ext(objPath.Base()))
-			ret = append(ret, copyFile(ctx, objPath, snapshotObjOut))
+			ret = append(ret, copyFile(ctx, objPath, snapshotObjOut, fake))
 			propOut = snapshotObjOut + ".json"
 		} else {
 			ctx.Errorf("unknown module %q in vendor snapshot", m.String())
@@ -532,9 +529,17 @@
 			return
 		}
 
-		// installSnapshot installs prebuilts and json flag files
-		snapshotOutputs = append(snapshotOutputs, installSnapshot(m)...)
+		// If we are using directed snapshot and a module is not included in the
+		// list, we will still include the module as if it was a fake module.
+		// The reason is that soong needs all the dependencies to be present, even
+		// if they are not using during the build.
+		installAsFake := c.fake
+		if c.image.excludeFromDirectedSnapshot(ctx.DeviceConfig(), m.BaseModuleName()) {
+			installAsFake = true
+		}
 
+		// installSnapshot installs prebuilts and json flag files
+		snapshotOutputs = append(snapshotOutputs, installSnapshot(m, installAsFake)...)
 		// just gather headers and notice files here, because they are to be deduplicated
 		if l, ok := m.linker.(snapshotLibraryInterface); ok {
 			headers = append(headers, l.snapshotHeaders()...)
@@ -553,7 +558,7 @@
 
 	// install all headers after removing duplicates
 	for _, header := range android.FirstUniquePaths(headers) {
-		snapshotOutputs = append(snapshotOutputs, copyFile(ctx, header, filepath.Join(includeDir, header.String())))
+		snapshotOutputs = append(snapshotOutputs, copyFile(ctx, header, filepath.Join(includeDir, header.String()), c.fake))
 	}
 
 	// All artifacts are ready. Sort them to normalize ninja and then zip.
diff --git a/cc/vendor_snapshot_test.go b/cc/vendor_snapshot_test.go
index 499d7ae..7e283fb 100644
--- a/cc/vendor_snapshot_test.go
+++ b/cc/vendor_snapshot_test.go
@@ -228,7 +228,6 @@
 	snapshotSingleton := ctx.SingletonForTests("vendor-snapshot")
 
 	var includeJsonFiles []string
-	var excludeJsonFiles []string
 
 	for _, arch := range [][]string{
 		[]string{"arm64", "armv8-a"},
@@ -248,9 +247,10 @@
 		checkSnapshot(t, ctx, snapshotSingleton, "prebuilt_libfoo", "libfoo.so", sharedDir, sharedVariant)
 		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libfoo.so.json"))
 
-		// Excluded modules
-		checkSnapshotExclude(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.so", sharedDir, sharedVariant)
-		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libvendor_available.so.json"))
+		// Excluded modules. Modules not included in the directed vendor snapshot
+		// are still include as fake modules.
+		checkSnapshotRule(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.so", sharedDir, sharedVariant)
+		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libvendor_available.so.json"))
 	}
 
 	// Verify that each json file for an included module has a rule.
@@ -259,13 +259,6 @@
 			t.Errorf("include json file %q not found", jsonFile)
 		}
 	}
-
-	// Verify that each json file for an excluded module has no rule.
-	for _, jsonFile := range excludeJsonFiles {
-		if snapshotSingleton.MaybeOutput(jsonFile).Rule != nil {
-			t.Errorf("exclude json file %q found", jsonFile)
-		}
-	}
 }
 
 func TestVendorSnapshotUse(t *testing.T) {
@@ -1058,3 +1051,85 @@
 		}
 	}
 }
+
+func TestRecoverySnapshotDirected(t *testing.T) {
+	bp := `
+	cc_library_shared {
+		name: "librecovery",
+		recovery: true,
+		nocrt: true,
+	}
+
+	cc_library_shared {
+		name: "librecovery_available",
+		recovery_available: true,
+		nocrt: true,
+	}
+
+	genrule {
+		name: "libfoo_gen",
+		cmd: "",
+		out: ["libfoo.so"],
+	}
+
+	cc_prebuilt_library_shared {
+		name: "libfoo",
+		recovery: true,
+		prefer: true,
+		srcs: [":libfoo_gen"],
+	}
+
+	cc_library_shared {
+		name: "libfoo",
+		recovery: true,
+		nocrt: true,
+	}
+`
+	config := TestConfig(buildDir, android.Android, nil, bp, nil)
+	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
+	config.TestProductVariables.RecoverySnapshotVersion = StringPtr("current")
+	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+	config.TestProductVariables.DirectedRecoverySnapshot = true
+	config.TestProductVariables.RecoverySnapshotModules = make(map[string]bool)
+	config.TestProductVariables.RecoverySnapshotModules["librecovery"] = true
+	config.TestProductVariables.RecoverySnapshotModules["libfoo"] = true
+	ctx := testCcWithConfig(t, config)
+
+	// Check recovery snapshot output.
+
+	snapshotDir := "recovery-snapshot"
+	snapshotVariantPath := filepath.Join(buildDir, snapshotDir, "arm64")
+	snapshotSingleton := ctx.SingletonForTests("recovery-snapshot")
+
+	var includeJsonFiles []string
+
+	for _, arch := range [][]string{
+		[]string{"arm64", "armv8-a"},
+	} {
+		archType := arch[0]
+		archVariant := arch[1]
+		archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
+
+		sharedVariant := fmt.Sprintf("android_recovery_%s_%s_shared", archType, archVariant)
+		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
+
+		// Included modules
+		checkSnapshot(t, ctx, snapshotSingleton, "librecovery", "librecovery.so", sharedDir, sharedVariant)
+		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "librecovery.so.json"))
+		// Check that snapshot captures "prefer: true" prebuilt
+		checkSnapshot(t, ctx, snapshotSingleton, "prebuilt_libfoo", "libfoo.so", sharedDir, sharedVariant)
+		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libfoo.so.json"))
+
+		// Excluded modules. Modules not included in the directed recovery snapshot
+		// are still include as fake modules.
+		checkSnapshotRule(t, ctx, snapshotSingleton, "librecovery_available", "librecovery_available.so", sharedDir, sharedVariant)
+		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "librecovery_available.so.json"))
+	}
+
+	// Verify that each json file for an included module has a rule.
+	for _, jsonFile := range includeJsonFiles {
+		if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
+			t.Errorf("include json file %q not found", jsonFile)
+		}
+	}
+}
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 9dcb5f3..c17e23d 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -100,7 +100,7 @@
 	if bazelConversionRequested(configuration) {
 		// Run the alternate pipeline of bp2build mutators and singleton to convert Blueprint to BUILD files
 		// before everything else.
-		runBp2Build(configuration, extraNinjaDeps)
+		runBp2Build(srcDir, configuration)
 		// Short-circuit and return.
 		return
 	}
@@ -162,7 +162,7 @@
 // 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) {
+func runBp2Build(srcDir string, configuration android.Config) {
 	// Register an alternate set of singletons and mutators for bazel
 	// conversion for Bazel conversion.
 	bp2buildCtx := android.NewContext(configuration)
@@ -172,7 +172,20 @@
 	configuration.SetStopBefore(bootstrap.StopBeforePrepareBuildActions)
 	bp2buildCtx.SetNameInterface(newNameResolver(configuration))
 
-	// Run the loading and analysis pipeline.
+	// The bp2build process is a purely functional process that only depends on
+	// Android.bp files. It must not depend on the values of per-build product
+	// configurations or variables, since those will generate different BUILD
+	// files based on how the user has configured their tree.
+	bp2buildCtx.SetModuleListFile(bootstrap.ModuleListFile)
+	extraNinjaDeps, err := bp2buildCtx.ListModulePaths(srcDir)
+	if err != nil {
+		panic(err)
+	}
+	extraNinjaDepsString := strings.Join(extraNinjaDeps, " \\\n ")
+
+	// 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.
 	bootstrap.Main(bp2buildCtx.Context, configuration, extraNinjaDeps...)
 
 	// Run the code-generation phase to convert BazelTargetModules to BUILD files.
@@ -195,7 +208,6 @@
 	ninjaFileName := "build.ninja"
 	ninjaFile := android.PathForOutput(codegenContext, ninjaFileName)
 	ninjaFileD := android.PathForOutput(codegenContext, ninjaFileName+".d")
-	extraNinjaDepsString := strings.Join(extraNinjaDeps, " \\\n ")
 	// A workaround to create the 'nothing' ninja target so `m nothing` works,
 	// since bp2build runs without Kati, and the 'nothing' target is declared in
 	// a Makefile.
diff --git a/cmd/soong_build/queryview.go b/cmd/soong_build/queryview.go
index 5d61d0c..dc0b323 100644
--- a/cmd/soong_build/queryview.go
+++ b/cmd/soong_build/queryview.go
@@ -24,7 +24,7 @@
 
 func createBazelQueryView(ctx *android.Context, bazelQueryViewDir string) error {
 	ruleShims := bp2build.CreateRuleShims(android.ModuleTypeFactories())
-	buildToTargets := bp2build.GenerateSoongModuleTargets(*ctx, bp2build.QueryView)
+	buildToTargets := bp2build.GenerateBazelTargets(*ctx, bp2build.QueryView)
 
 	filesToWrite := bp2build.CreateBazelFiles(ruleShims, buildToTargets, bp2build.QueryView)
 	for _, f := range filesToWrite {
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index 5092ad0..7658154 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -48,9 +48,12 @@
 	// Hash and signing algorithm for avbtool. Default is SHA256_RSA4096.
 	Avb_algorithm *string
 
-	// Type of the filesystem. Currently, ext4 and compressed_cpio are supported. Default is
-	// ext4.
+	// Type of the filesystem. Currently, ext4, cpio, and compressed_cpio are supported. Default
+	// is ext4.
 	Type *string
+
+	// file_contexts file to make image. Currently, only ext4 is supported.
+	File_contexts *string `android:"path"`
 }
 
 // android_filesystem packages a set of modules and their transitive dependencies into a filesystem
@@ -80,6 +83,7 @@
 const (
 	ext4Type fsType = iota
 	compressedCpioType
+	cpioType // uncompressed
 	unknown
 )
 
@@ -90,6 +94,8 @@
 		return ext4Type
 	case "compressed_cpio":
 		return compressedCpioType
+	case "cpio":
+		return cpioType
 	default:
 		ctx.PropertyErrorf("type", "%q not supported", typeStr)
 		return unknown
@@ -107,7 +113,9 @@
 	case ext4Type:
 		f.output = f.buildImageUsingBuildImage(ctx)
 	case compressedCpioType:
-		f.output = f.buildCompressedCpioImage(ctx)
+		f.output = f.buildCpioImage(ctx, true)
+	case cpioType:
+		f.output = f.buildCpioImage(ctx, false)
 	default:
 		return
 	}
@@ -142,6 +150,16 @@
 	return output
 }
 
+func (f *filesystem) buildFileContexts(ctx android.ModuleContext) android.OutputPath {
+	builder := android.NewRuleBuilder(pctx, ctx)
+	fcBin := android.PathForModuleOut(ctx, "file_contexts.bin")
+	builder.Command().BuiltTool("sefcontext_compile").
+		FlagWithOutput("-o ", fcBin).
+		Input(android.PathForModuleSrc(ctx, proptools.String(f.properties.File_contexts)))
+	builder.Build("build_filesystem_file_contexts", fmt.Sprintf("Creating filesystem file contexts for %s", f.BaseModuleName()))
+	return fcBin.OutputPath
+}
+
 func (f *filesystem) buildPropFile(ctx android.ModuleContext) (propFile android.OutputPath, toolDeps android.Paths) {
 	type prop struct {
 		name  string
@@ -188,6 +206,10 @@
 		addStr("partition_name", f.Name())
 	}
 
+	if proptools.String(f.properties.File_contexts) != "" {
+		addPath("selinux_fc", f.buildFileContexts(ctx))
+	}
+
 	propFile = android.PathForModuleOut(ctx, "prop").OutputPath
 	builder := android.NewRuleBuilder(pctx, ctx)
 	builder.Command().Text("rm").Flag("-rf").Output(propFile)
@@ -201,12 +223,16 @@
 	return propFile, deps
 }
 
-func (f *filesystem) buildCompressedCpioImage(ctx android.ModuleContext) android.OutputPath {
+func (f *filesystem) buildCpioImage(ctx android.ModuleContext, compressed bool) android.OutputPath {
 	if proptools.Bool(f.properties.Use_avb) {
 		ctx.PropertyErrorf("use_avb", "signing compresed cpio image using avbtool is not supported."+
 			"Consider adding this to bootimg module and signing the entire boot image.")
 	}
 
+	if proptools.String(f.properties.File_contexts) != "" {
+		ctx.PropertyErrorf("file_contexts", "file_contexts is not supported for compressed cpio image.")
+	}
+
 	zipFile := android.PathForModuleOut(ctx, "temp.zip").OutputPath
 	f.CopyDepsToZip(ctx, zipFile)
 
@@ -218,18 +244,22 @@
 		Input(zipFile)
 
 	output := android.PathForModuleOut(ctx, f.installFileName()).OutputPath
-	builder.Command().
+	cmd := builder.Command().
 		BuiltTool("mkbootfs").
-		Text(rootDir.String()). // input directory
-		Text("|").
-		BuiltTool("lz4").
-		Flag("--favor-decSpeed"). // for faster boot
-		Flag("-12").              // maximum compression level
-		Flag("-l").               // legacy format for kernel
-		Text(">").Output(output)
+		Text(rootDir.String()) // input directory
+	if compressed {
+		cmd.Text("|").
+			BuiltTool("lz4").
+			Flag("--favor-decSpeed"). // for faster boot
+			Flag("-12").              // maximum compression level
+			Flag("-l").               // legacy format for kernel
+			Text(">").Output(output)
+	} else {
+		cmd.Text(">").Output(output)
+	}
 
 	// rootDir is not deleted. Might be useful for quick inspection.
-	builder.Build("build_compressed_cpio_image", fmt.Sprintf("Creating filesystem %s", f.BaseModuleName()))
+	builder.Build("build_cpio_image", fmt.Sprintf("Creating filesystem %s", f.BaseModuleName()))
 
 	return output
 }
@@ -250,6 +280,16 @@
 	}}
 }
 
+var _ android.OutputFileProducer = (*filesystem)(nil)
+
+// Implements android.OutputFileProducer
+func (f *filesystem) OutputFiles(tag string) (android.Paths, error) {
+	if tag == "" {
+		return []android.Path{f.output}, nil
+	}
+	return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+}
+
 // Filesystem is the public interface for the filesystem struct. Currently, it's only for the apex
 // package to have access to the output file.
 type Filesystem interface {
diff --git a/genrule/genrule.go b/genrule/genrule.go
index ddfb459..9fa6c48 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -47,9 +47,14 @@
 		ctx.BottomUp("genrule_tool_deps", toolDepsMutator).Parallel()
 	})
 
+	android.DepsBp2BuildMutators(RegisterGenruleBp2BuildDeps)
 	android.RegisterBp2BuildMutator("genrule", GenruleBp2Build)
 }
 
+func RegisterGenruleBp2BuildDeps(ctx android.RegisterMutatorsContext) {
+	ctx.BottomUp("genrule_tool_deps", toolDepsMutator)
+}
+
 var (
 	pctx = android.NewPackageContext("android/soong/genrule")
 
@@ -775,10 +780,9 @@
 }
 
 type bazelGenruleAttributes struct {
-	Name  *string
-	Srcs  []string
+	Srcs  bazel.LabelList
 	Outs  []string
-	Tools []string
+	Tools bazel.LabelList
 	Cmd   string
 }
 
@@ -795,45 +799,64 @@
 }
 
 func GenruleBp2Build(ctx android.TopDownMutatorContext) {
-	if m, ok := ctx.Module().(*Module); ok {
-		name := "__bp2build__" + m.Name()
-		// Bazel only has the "tools" attribute.
-		tools := append(m.properties.Tools, m.properties.Tool_files...)
-
-		// Replace in and out variables with $< and $@
-		var cmd string
-		if m.properties.Cmd != nil {
-			cmd = strings.Replace(*m.properties.Cmd, "$(in)", "$(SRCS)", -1)
-			cmd = strings.Replace(cmd, "$(out)", "$(OUTS)", -1)
-			cmd = strings.Replace(cmd, "$(genDir)", "$(GENDIR)", -1)
-			if len(tools) > 0 {
-				cmd = strings.Replace(cmd, "$(location)", fmt.Sprintf("$(location %s)", tools[0]), -1)
-				cmd = strings.Replace(cmd, "$(locations)", fmt.Sprintf("$(locations %s)", tools[0]), -1)
-			}
-		}
-
-		// The Out prop is not in an immediately accessible field
-		// in the Module struct, so use GetProperties and cast it
-		// to the known struct prop.
-		var outs []string
-		for _, propIntf := range m.GetProperties() {
-			if props, ok := propIntf.(*genRuleProperties); ok {
-				outs = props.Out
-				break
-			}
-		}
-
-		// Create the BazelTargetModule.
-		ctx.CreateModule(BazelGenruleFactory, &bazelGenruleAttributes{
-			Name:  proptools.StringPtr(name),
-			Srcs:  m.properties.Srcs,
-			Outs:  outs,
-			Cmd:   cmd,
-			Tools: tools,
-		}, &bazel.BazelTargetModuleProperties{
-			Rule_class: "genrule",
-		})
+	m, ok := ctx.Module().(*Module)
+	if !ok || !m.properties.Bazel_module.Bp2build_available {
+		return
 	}
+
+	// Bazel only has the "tools" attribute.
+	tools := android.BazelLabelForModuleDeps(ctx, m.properties.Tools)
+	tool_files := android.BazelLabelForModuleSrc(ctx, m.properties.Tool_files)
+	tools.Append(tool_files)
+
+	srcs := android.BazelLabelForModuleSrc(ctx, m.properties.Srcs)
+
+	var allReplacements bazel.LabelList
+	allReplacements.Append(tools)
+	allReplacements.Append(srcs)
+
+	// Replace in and out variables with $< and $@
+	var cmd string
+	if m.properties.Cmd != nil {
+		cmd = strings.Replace(*m.properties.Cmd, "$(in)", "$(SRCS)", -1)
+		cmd = strings.Replace(cmd, "$(out)", "$(OUTS)", -1)
+		cmd = strings.Replace(cmd, "$(genDir)", "$(GENDIR)", -1)
+		if len(tools.Includes) > 0 {
+			cmd = strings.Replace(cmd, "$(location)", fmt.Sprintf("$(location %s)", tools.Includes[0].Label), -1)
+			cmd = strings.Replace(cmd, "$(locations)", fmt.Sprintf("$(locations %s)", tools.Includes[0].Label), -1)
+		}
+		for _, l := range allReplacements.Includes {
+			bpLoc := fmt.Sprintf("$(location %s)", l.Bp_text)
+			bpLocs := fmt.Sprintf("$(locations %s)", l.Bp_text)
+			bazelLoc := fmt.Sprintf("$(location %s)", l.Label)
+			bazelLocs := fmt.Sprintf("$(locations %s)", l.Label)
+			cmd = strings.Replace(cmd, bpLoc, bazelLoc, -1)
+			cmd = strings.Replace(cmd, bpLocs, bazelLocs, -1)
+		}
+	}
+
+	// The Out prop is not in an immediately accessible field
+	// in the Module struct, so use GetProperties and cast it
+	// to the known struct prop.
+	var outs []string
+	for _, propIntf := range m.GetProperties() {
+		if props, ok := propIntf.(*genRuleProperties); ok {
+			outs = props.Out
+			break
+		}
+	}
+
+	attrs := &bazelGenruleAttributes{
+		Srcs:  srcs,
+		Outs:  outs,
+		Cmd:   cmd,
+		Tools: tools,
+	}
+
+	props := bazel.NewBazelTargetModuleProperties(m.Name(), "genrule", "")
+
+	// Create the BazelTargetModule.
+	ctx.CreateBazelTargetModule(BazelGenruleFactory, props, attrs)
 }
 
 func (m *bazelGenrule) Name() string {
diff --git a/java/androidmk.go b/java/androidmk.go
index 21f3012..6e7c437 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -646,6 +646,9 @@
 					entries.SetString("LOCAL_SOONG_BUILT_INSTALLED", a.dexpreopter.builtInstalled)
 				}
 				entries.AddStrings("LOCAL_INSTALLED_MODULE_STEM", a.installPath.Rel())
+				if Bool(a.properties.Export_package_resources) {
+					entries.SetPath("LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE", a.outputFile)
+				}
 			},
 		},
 	}}
diff --git a/java/app_import.go b/java/app_import.go
index 6f21bfb..59eb10a 100644
--- a/java/app_import.go
+++ b/java/app_import.go
@@ -92,6 +92,10 @@
 
 	// Optional name for the installed app. If unspecified, it is derived from the module name.
 	Filename *string
+
+	// If set, create package-export.apk, which other packages can
+	// use to get PRODUCT-agnostic resource data like IDs and type definitions.
+	Export_package_resources *bool
 }
 
 func (a *AndroidAppImport) IsInstallable() bool {
@@ -142,13 +146,17 @@
 	}
 }
 
+func (a *AndroidAppImport) isPrebuiltFrameworkRes() bool {
+	return a.Name() == "prebuilt_framework-res"
+}
+
 func (a *AndroidAppImport) DepsMutator(ctx android.BottomUpMutatorContext) {
 	cert := android.SrcIsModule(String(a.properties.Certificate))
 	if cert != "" {
 		ctx.AddDependency(ctx.Module(), certificateTag, cert)
 	}
 
-	a.usesLibrary.deps(ctx, true)
+	a.usesLibrary.deps(ctx, !a.isPrebuiltFrameworkRes())
 }
 
 func (a *AndroidAppImport) uncompressEmbeddedJniLibs(
@@ -247,7 +255,12 @@
 	a.uncompressEmbeddedJniLibs(ctx, srcApk, jnisUncompressed.OutputPath)
 
 	var installDir android.InstallPath
-	if Bool(a.properties.Privileged) {
+
+	if a.isPrebuiltFrameworkRes() {
+		// framework-res.apk is installed as system/framework/framework-res.apk
+		installDir = android.PathForModuleInstall(ctx, "framework")
+		a.preprocessed = true
+	} else if Bool(a.properties.Privileged) {
 		installDir = android.PathForModuleInstall(ctx, "priv-app", a.BaseModuleName())
 	} else if ctx.InstallInTestcases() {
 		installDir = android.PathForModuleInstall(ctx, a.BaseModuleName(), ctx.DeviceConfig().DeviceArch())
@@ -275,7 +288,15 @@
 	// TODO: Handle EXTERNAL
 
 	// Sign or align the package if package has not been preprocessed
-	if a.preprocessed {
+
+	if a.isPrebuiltFrameworkRes() {
+		a.outputFile = srcApk
+		certificates = processMainCert(a.ModuleBase, String(a.properties.Certificate), certificates, ctx)
+		if len(certificates) != 1 {
+			ctx.ModuleErrorf("Unexpected number of certificates were extracted: %q", certificates)
+		}
+		a.certificate = certificates[0]
+	} else if a.preprocessed {
 		a.outputFile = srcApk
 		a.certificate = PresignedCertificate
 	} else if !Bool(a.properties.Presigned) {
diff --git a/java/app_import_test.go b/java/app_import_test.go
index 344d23b..d7f69eb 100644
--- a/java/app_import_test.go
+++ b/java/app_import_test.go
@@ -393,6 +393,69 @@
 	}
 }
 
+func TestAndroidAppImport_frameworkRes(t *testing.T) {
+	ctx, config := testJava(t, `
+		android_app_import {
+			name: "framework-res",
+			certificate: "platform",
+			apk: "package-res.apk",
+			prefer: true,
+			export_package_resources: true,
+			// Disable dexpreopt and verify_uses_libraries check as the app
+			// contains no Java code to be dexpreopted.
+			enforce_uses_libs: false,
+			dex_preopt: {
+				enabled: false,
+			},
+		}
+		`)
+
+	mod := ctx.ModuleForTests("prebuilt_framework-res", "android_common").Module()
+	a := mod.(*AndroidAppImport)
+
+	if !a.preprocessed {
+		t.Errorf("prebuilt framework-res is not preprocessed")
+	}
+
+	expectedInstallPath := buildDir + "/target/product/test_device/system/framework/framework-res.apk"
+
+	if a.dexpreopter.installPath.String() != expectedInstallPath {
+		t.Errorf("prebuilt framework-res installed to incorrect location, actual: %s, expected: %s", a.dexpreopter.installPath, expectedInstallPath)
+
+	}
+
+	entries := android.AndroidMkEntriesForTest(t, config, "", mod)[0]
+
+	expectedPath := "."
+	// From apk property above, in the root of the source tree.
+	expectedPrebuiltModuleFile := "package-res.apk"
+	// Verify that the apk is preprocessed: The export package is the same
+	// as the prebuilt.
+	expectedSoongResourceExportPackage := expectedPrebuiltModuleFile
+
+	actualPath := entries.EntryMap["LOCAL_PATH"]
+	actualPrebuiltModuleFile := entries.EntryMap["LOCAL_PREBUILT_MODULE_FILE"]
+	actualSoongResourceExportPackage := entries.EntryMap["LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE"]
+
+	if len(actualPath) != 1 {
+		t.Errorf("LOCAL_PATH incorrect len %d", len(actualPath))
+	} else if actualPath[0] != expectedPath {
+		t.Errorf("LOCAL_PATH mismatch, actual: %s, expected: %s", actualPath[0], expectedPath)
+	}
+
+	if len(actualPrebuiltModuleFile) != 1 {
+		t.Errorf("LOCAL_PREBUILT_MODULE_FILE incorrect len %d", len(actualPrebuiltModuleFile))
+	} else if actualPrebuiltModuleFile[0] != expectedPrebuiltModuleFile {
+		t.Errorf("LOCAL_PREBUILT_MODULE_FILE mismatch, actual: %s, expected: %s", actualPrebuiltModuleFile[0], expectedPrebuiltModuleFile)
+	}
+
+	if len(actualSoongResourceExportPackage) != 1 {
+		t.Errorf("LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE incorrect len %d", len(actualSoongResourceExportPackage))
+	} else if actualSoongResourceExportPackage[0] != expectedSoongResourceExportPackage {
+		t.Errorf("LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE mismatch, actual: %s, expected: %s", actualSoongResourceExportPackage[0], expectedSoongResourceExportPackage)
+	}
+}
+
 func TestAndroidTestImport(t *testing.T) {
 	ctx, config := testJava(t, `
 		android_test_import {
diff --git a/java/boot_image.go b/java/boot_image.go
index 0a525b7..8a1e3c9 100644
--- a/java/boot_image.go
+++ b/java/boot_image.go
@@ -90,6 +90,10 @@
 		// The dex2oat tool is only needed for building and is not required in the apex.
 		return false
 	}
+	if android.IsMetaDependencyTag(tag) {
+		// Cross-cutting metadata dependencies are metadata.
+		return false
+	}
 	panic(fmt.Errorf("boot_image module %q should not have a dependency on %q via tag %s", b, dep, android.PrettyPrintTag(tag)))
 }
 
diff --git a/java/builder.go b/java/builder.go
index 995160d..22a891a 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -193,12 +193,19 @@
 
 	jarjar = pctx.AndroidStaticRule("jarjar",
 		blueprint.RuleParams{
-			Command: "${config.JavaCmd} ${config.JavaVmFlags}" +
+			Command: "" +
+				// Jarjar doesn't exit with an error when the rules file contains a syntax error,
+				// leading to stale or missing files later in the build.  Remove the output file
+				// before running jarjar.
+				"rm -f ${out} && " +
+				"${config.JavaCmd} ${config.JavaVmFlags}" +
 				// b/146418363 Enable Android specific jarjar transformer to drop compat annotations
 				// for newly repackaged classes. Dropping @UnsupportedAppUsage on repackaged classes
 				// avoids adding new hiddenapis after jarjar'ing.
 				" -DremoveAndroidCompatAnnotations=true" +
-				" -jar ${config.JarjarCmd} process $rulesFile $in $out",
+				" -jar ${config.JarjarCmd} process $rulesFile $in $out && " +
+				// Turn a missing output file into a ninja error
+				`[ -e ${out} ] || (echo "Missing output file"; exit 1)`,
 			CommandDeps: []string{"${config.JavaCmd}", "${config.JarjarCmd}", "$rulesFile"},
 		},
 		"rulesFile")
diff --git a/java/dex.go b/java/dex.go
index 055d479..24600c2 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -263,10 +263,10 @@
 }
 
 func (d *dexer) compileDex(ctx android.ModuleContext, flags javaBuilderFlags, minSdkVersion sdkSpec,
-	classesJar android.Path, jarName string) android.ModuleOutPath {
+	classesJar android.Path, jarName string) android.OutputPath {
 
 	// Compile classes.jar into classes.dex and then javalib.jar
-	javalibJar := android.PathForModuleOut(ctx, "dex", jarName)
+	javalibJar := android.PathForModuleOut(ctx, "dex", jarName).OutputPath
 	outDir := android.PathForModuleOut(ctx, "dex")
 
 	zipFlags := "--ignore_missing_files"
@@ -329,7 +329,7 @@
 		})
 	}
 	if proptools.Bool(d.dexProperties.Uncompress_dex) {
-		alignedJavalibJar := android.PathForModuleOut(ctx, "aligned", jarName)
+		alignedJavalibJar := android.PathForModuleOut(ctx, "aligned", jarName).OutputPath
 		TransformZipAlign(ctx, alignedJavalibJar, javalibJar)
 		javalibJar = alignedJavalibJar
 	}
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index ac00592..29c73c1 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -118,7 +118,7 @@
 	return dexpreopt.OdexOnSystemOtherByName(ctx.ModuleName(), android.InstallPathToOnDevicePath(ctx, installPath), dexpreopt.GetGlobalConfig(ctx))
 }
 
-func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.ModuleOutPath) {
+func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.WritablePath) {
 	// TODO(b/148690468): The check on d.installPath is to bail out in cases where
 	// the dexpreopter struct hasn't been fully initialized before we're called,
 	// e.g. in aar.go. This keeps the behaviour that dexpreopting is effectively
@@ -129,8 +129,6 @@
 
 	dexLocation := android.InstallPathToOnDevicePath(ctx, d.installPath)
 
-	buildPath := android.PathForModuleOut(ctx, "dexpreopt", ctx.ModuleName()+".jar").OutputPath
-
 	providesUsesLib := ctx.ModuleName()
 	if ulib, ok := ctx.Module().(ProvidesUsesLib); ok {
 		name := ulib.ProvidesUsesLib()
@@ -146,7 +144,6 @@
 		slimDexpreoptConfig := &dexpreopt.ModuleConfig{
 			Name:                 ctx.ModuleName(),
 			DexLocation:          dexLocation,
-			BuildPath:            buildPath,
 			EnforceUsesLibraries: d.enforceUsesLibs,
 			ProvidesUsesLibrary:  providesUsesLib,
 			ClassLoaderContexts:  d.classLoaderContexts,
@@ -218,7 +215,7 @@
 	dexpreoptConfig := &dexpreopt.ModuleConfig{
 		Name:            ctx.ModuleName(),
 		DexLocation:     dexLocation,
-		BuildPath:       buildPath,
+		BuildPath:       android.PathForModuleOut(ctx, "dexpreopt", ctx.ModuleName()+".jar").OutputPath,
 		DexPath:         dexJarFile,
 		ManifestPath:    d.manifestFile,
 		UncompressedDex: d.uncompressedDex,
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index 2a7eb42..86b1895 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -435,6 +435,11 @@
 // Inspect this module to see if it contains a bootclasspath dex jar.
 // Note that the same jar may occur in multiple modules.
 // This logic is tested in the apex package to avoid import cycle apex <-> java.
+//
+// This is similar to logic in isModuleInConfiguredList() so any changes needed here are likely to
+// be needed there too.
+//
+// TODO(b/177892522): Avoid having to perform this type of check or if necessary dedup it.
 func getBootImageJar(ctx android.SingletonContext, image *bootImageConfig, module android.Module) (int, android.Path) {
 	name := ctx.ModuleName(module)
 
diff --git a/java/hiddenapi.go b/java/hiddenapi.go
index 2cd025e..069595e 100644
--- a/java/hiddenapi.go
+++ b/java/hiddenapi.go
@@ -28,10 +28,21 @@
 }, "outFlag", "stubAPIFlags")
 
 type hiddenAPI struct {
+	// True if the module containing this structure contributes to the hiddenapi information.
+	active bool
+
+	// True if the module only contains additional annotations and so does not require hiddenapi
+	// information to be encoded in its dex file and should not be used to generate the
+	// hiddenAPISingletonPathsStruct.stubFlags file.
+	annotationsOnly bool
+
 	// The path to the dex jar that is in the boot class path. If this is nil then the associated
 	// module is not a boot jar, but could be one of the <x>-hiddenapi modules that provide additional
 	// annotations for the <x> boot dex jar but which do not actually provide a boot dex jar
 	// themselves.
+	//
+	// This must be the path to the unencoded dex jar as the encoded dex jar indirectly depends on
+	// this file so using the encoded dex jar here would result in a cycle in the ninja rules.
 	bootDexJarPath android.Path
 
 	// The path to the CSV file that contains mappings from Java signature to various flags derived
@@ -89,52 +100,82 @@
 
 var _ hiddenAPIIntf = (*hiddenAPI)(nil)
 
-func (h *hiddenAPI) hiddenAPI(ctx android.ModuleContext, name string, primary bool, dexJar android.ModuleOutPath,
-	implementationJar android.Path, uncompressDex bool) android.ModuleOutPath {
-	if !ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") {
+// Initialize the hiddenapi structure
+func (h *hiddenAPI) initHiddenAPI(ctx android.BaseModuleContext, name string) {
+	// If hiddenapi processing is disabled treat this as inactive.
+	if ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") {
+		return
+	}
 
-		// Modules whose names are of the format <x>-hiddenapi provide hiddenapi information
-		// for the boot jar module <x>. Otherwise, the module provides information for itself.
-		// Either way extract the name of the boot jar module.
-		bootJarName := strings.TrimSuffix(name, "-hiddenapi")
+	// Modules whose names are of the format <x>-hiddenapi provide hiddenapi information for the boot
+	// jar module <x>. Otherwise, the module provides information for itself. Either way extract the
+	// name of the boot jar module.
+	bootJarName := strings.TrimSuffix(name, "-hiddenapi")
 
-		// If this module is on the boot jars list (or providing information for a module
-		// on the list) then extract the hiddenapi information from it, and if necessary
-		// encode that information in the generated dex file.
-		//
-		// It is important that hiddenapi information is only gathered for/from modules on
-		// that are actually on the boot jars list because the runtime only enforces access
-		// to the hidden API for the bootclassloader. If information is gathered for modules
-		// not on the list then that will cause failures in the CtsHiddenApiBlacklist...
-		// tests.
-		if inList(bootJarName, ctx.Config().BootJars()) {
-			// Create ninja rules to generate various CSV files needed by hiddenapi and store the paths
-			// in the hiddenAPI structure.
-			h.hiddenAPIGenerateCSV(ctx, implementationJar)
+	// It is important that hiddenapi information is only gathered for/from modules that are actually
+	// on the boot jars list because the runtime only enforces access to the hidden API for the
+	// bootclassloader. If information is gathered for modules not on the list then that will cause
+	// failures in the CtsHiddenApiBlocklist... tests.
+	h.active = inList(bootJarName, ctx.Config().BootJars())
 
-			// If this module is actually on the boot jars list and not providing
-			// hiddenapi information for a module on the boot jars list then encode
-			// the gathered information in the generated dex file.
-			if name == bootJarName {
-				hiddenAPIJar := android.PathForModuleOut(ctx, "hiddenapi", name+".jar")
+	// If this module has a suffix of -hiddenapi then it only provides additional annotation
+	// information for a module on the boot jars list.
+	h.annotationsOnly = strings.HasSuffix(name, "-hiddenapi")
+}
 
-				// More than one library with the same classes can be encoded but only one can
-				// be added to the global set of flags, otherwise it will result in duplicate
-				// classes which is an error. Therefore, only add the dex jar of one of them
-				// to the global set of flags.
-				if primary {
-					h.bootDexJarPath = dexJar
-				}
-				hiddenAPIEncodeDex(ctx, hiddenAPIJar, dexJar, uncompressDex)
-				dexJar = hiddenAPIJar
-			}
-		}
+// hiddenAPIExtractAndEncode is called by any module that could contribute to the hiddenapi
+// processing.
+//
+// It ignores any module that has not had initHiddenApi() called on it and which is not in the boot
+// jar list.
+//
+// Otherwise, it generates ninja rules to do the following:
+// 1. Extract information needed for hiddenapi processing from the module and output it into CSV
+//    files.
+// 2. Conditionally adds the supplied dex file to the list of files used to generate the
+//    hiddenAPISingletonPathsStruct.stubsFlag file.
+// 3. Conditionally creates a copy of the supplied dex file into which it has encoded the hiddenapi
+//    flags and returns this instead of the supplied dex jar, otherwise simply returns the supplied
+//    dex jar.
+func (h *hiddenAPI) hiddenAPIExtractAndEncode(ctx android.ModuleContext, name string, primary bool, dexJar android.OutputPath,
+	implementationJar android.Path, uncompressDex bool) android.OutputPath {
+
+	if !h.active {
+		return dexJar
+	}
+
+	h.hiddenAPIExtractInformation(ctx, dexJar, implementationJar, primary)
+
+	if !h.annotationsOnly {
+		hiddenAPIJar := android.PathForModuleOut(ctx, "hiddenapi", name+".jar").OutputPath
+
+		// Create a copy of the dex jar which has been encoded with hiddenapi flags.
+		hiddenAPIEncodeDex(ctx, hiddenAPIJar, dexJar, uncompressDex)
+
+		// Use the encoded dex jar from here onwards.
+		dexJar = hiddenAPIJar
 	}
 
 	return dexJar
 }
 
-func (h *hiddenAPI) hiddenAPIGenerateCSV(ctx android.ModuleContext, classesJar android.Path) {
+// hiddenAPIExtractInformation generates ninja rules to extract the information from the classes
+// jar, and outputs it to the appropriate module specific CSV file.
+//
+// It also makes the dex jar available for use when generating the
+// hiddenAPISingletonPathsStruct.stubFlags.
+func (h *hiddenAPI) hiddenAPIExtractInformation(ctx android.ModuleContext, dexJar, classesJar android.Path, primary bool) {
+	if !h.active {
+		return
+	}
+
+	// More than one library with the same classes may need to be encoded but only one should be
+	// used as a source of information for hidden API processing otherwise it will result in
+	// duplicate entries in the files.
+	if !primary {
+		return
+	}
+
 	stubFlagsCSV := hiddenAPISingletonPaths(ctx).stubFlags
 
 	flagsCSV := android.PathForModuleOut(ctx, "hiddenapi", "flags.csv")
@@ -173,6 +214,10 @@
 		FlagWithOutput("--output=", indexCSV)
 	rule.Build("merged-hiddenapi-index", "Merged Hidden API index")
 	h.indexCSVPath = indexCSV
+
+	// Save the unencoded dex jar so it can be used when generating the
+	// hiddenAPISingletonPathsStruct.stubFlags file.
+	h.bootDexJarPath = dexJar
 }
 
 var hiddenAPIEncodeDexRule = pctx.AndroidStaticRule("hiddenAPIEncodeDex", blueprint.RuleParams{
diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go
index ccb8745..568d15f 100644
--- a/java/hiddenapi_singleton.go
+++ b/java/hiddenapi_singleton.go
@@ -22,9 +22,13 @@
 )
 
 func init() {
-	android.RegisterSingletonType("hiddenapi", hiddenAPISingletonFactory)
-	android.RegisterSingletonType("hiddenapi_index", hiddenAPIIndexSingletonFactory)
-	android.RegisterModuleType("hiddenapi_flags", hiddenAPIFlagsFactory)
+	RegisterHiddenApiSingletonComponents(android.InitRegistrationContext)
+}
+
+func RegisterHiddenApiSingletonComponents(ctx android.RegistrationContext) {
+	ctx.RegisterSingletonType("hiddenapi", hiddenAPISingletonFactory)
+	ctx.RegisterSingletonType("hiddenapi_index", hiddenAPIIndexSingletonFactory)
+	ctx.RegisterModuleType("hiddenapi_flags", hiddenAPIFlagsFactory)
 }
 
 type hiddenAPISingletonPathsStruct struct {
@@ -213,6 +217,10 @@
 
 	var bootDexJars android.Paths
 
+	// Get the configured non-updatable and updatable boot jars.
+	nonUpdatableBootJars := ctx.Config().NonUpdatableBootJars()
+	updatableBootJars := ctx.Config().UpdatableBootJars()
+
 	ctx.VisitAllModules(func(module android.Module) {
 		// Collect dex jar paths for the modules listed above.
 		if j, ok := module.(Dependency); ok {
@@ -227,11 +235,8 @@
 		// Collect dex jar paths for modules that had hiddenapi encode called on them.
 		if h, ok := module.(hiddenAPIIntf); ok {
 			if jar := h.bootDexJar(); jar != nil {
-				// For a java lib included in an APEX, only take the one built for
-				// the platform variant, and skip the variants for APEXes.
-				// Otherwise, the hiddenapi tool will complain about duplicated classes
-				apexInfo := ctx.ModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo)
-				if !apexInfo.IsForPlatform() {
+				if !isModuleInConfiguredList(ctx, module, nonUpdatableBootJars) &&
+					!isModuleInConfiguredList(ctx, module, updatableBootJars) {
 					return
 				}
 
@@ -280,6 +285,47 @@
 	rule.Build("hiddenAPIStubFlagsFile", "hiddenapi stub flags")
 }
 
+// Checks to see whether the supplied module variant is in the list of boot jars.
+//
+// This is similar to logic in getBootImageJar() so any changes needed here are likely to be needed
+// there too.
+//
+// TODO(b/179354495): Avoid having to perform this type of check or if necessary dedup it.
+func isModuleInConfiguredList(ctx android.SingletonContext, module android.Module, configuredBootJars android.ConfiguredJarList) bool {
+	name := ctx.ModuleName(module)
+
+	// Strip a prebuilt_ prefix so that this can match a prebuilt module that has not been renamed.
+	name = android.RemoveOptionalPrebuiltPrefix(name)
+
+	// Ignore any module that is not listed in the boot image configuration.
+	index := configuredBootJars.IndexOfJar(name)
+	if index == -1 {
+		return false
+	}
+
+	// It is an error if the module is not an ApexModule.
+	if _, ok := module.(android.ApexModule); !ok {
+		ctx.Errorf("module %q configured in boot jars does not support being added to an apex", module)
+		return false
+	}
+
+	apexInfo := ctx.ModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo)
+
+	// Now match the apex part of the boot image configuration.
+	requiredApex := configuredBootJars.Apex(index)
+	if requiredApex == "platform" {
+		if len(apexInfo.InApexes) != 0 {
+			// A platform variant is required but this is for an apex so ignore it.
+			return false
+		}
+	} else if !apexInfo.InApexByBaseName(requiredApex) {
+		// An apex variant for a specific apex is required but this is the wrong apex.
+		return false
+	}
+
+	return true
+}
+
 func prebuiltFlagsRule(ctx android.SingletonContext) android.Path {
 	outputPath := hiddenAPISingletonPaths(ctx).flags
 	inputPath := android.PathForSource(ctx, ctx.Config().PrebuiltHiddenApiDir(ctx), "hiddenapi-flags.csv")
@@ -326,7 +372,7 @@
 	stubFlags := hiddenAPISingletonPaths(ctx).stubFlags
 
 	rule.Command().
-		Tool(android.PathForSource(ctx, "frameworks/base/tools/hiddenapi/generate_hiddenapi_lists.py")).
+		BuiltTool("generate_hiddenapi_lists").
 		FlagWithInput("--csv ", stubFlags).
 		Inputs(flagsCSV).
 		FlagWithInput("--unsupported ",
diff --git a/java/hiddenapi_singleton_test.go b/java/hiddenapi_singleton_test.go
index 0f9ef58..77cfff4 100644
--- a/java/hiddenapi_singleton_test.go
+++ b/java/hiddenapi_singleton_test.go
@@ -15,11 +15,12 @@
 package java
 
 import (
-	"android/soong/android"
 	"fmt"
 	"strings"
 	"testing"
 
+	"android/soong/android"
+
 	"github.com/google/blueprint/proptools"
 )
 
@@ -32,7 +33,7 @@
 
 func testContextWithHiddenAPI(config android.Config) *android.TestContext {
 	ctx := testContext(config)
-	ctx.RegisterSingletonType("hiddenapi", hiddenAPISingletonFactory)
+	RegisterHiddenApiSingletonComponents(ctx)
 	return ctx
 }
 
@@ -64,8 +65,8 @@
 			name: "foo",
 			srcs: ["a.java"],
 			compile_dex: true,
-	}
-	`, []string{":foo"}, nil)
+		}
+	`, []string{"platform:foo"}, nil)
 
 	hiddenAPI := ctx.SingletonForTests("hiddenapi")
 	hiddenapiRule := hiddenAPI.Rule("hiddenapi")
@@ -75,6 +76,37 @@
 	}
 }
 
+func TestHiddenAPIIndexSingleton(t *testing.T) {
+	ctx, _ := testHiddenAPIBootJars(t, `
+		java_library {
+			name: "foo",
+			srcs: ["a.java"],
+			compile_dex: true,
+		}
+
+		java_import {
+			name: "foo",
+			jars: ["a.jar"],
+			compile_dex: true,
+			prefer: false,
+		}
+
+		java_sdk_library {
+			name: "bar",
+			srcs: ["a.java"],
+			compile_dex: true,
+		}
+	`, []string{"platform:foo", "platform:bar"}, nil)
+
+	hiddenAPIIndex := ctx.SingletonForTests("hiddenapi_index")
+	indexRule := hiddenAPIIndex.Rule("singleton-merged-hiddenapi-index")
+	CheckHiddenAPIRuleInputs(t, `
+.intermediates/bar/android_common/hiddenapi/index.csv
+.intermediates/foo/android_common/hiddenapi/index.csv
+`,
+		indexRule)
+}
+
 func TestHiddenAPISingletonWithPrebuilt(t *testing.T) {
 	ctx, _ := testHiddenAPIBootJars(t, `
 		java_import {
@@ -82,7 +114,7 @@
 			jars: ["a.jar"],
 			compile_dex: true,
 	}
-	`, []string{":foo"}, nil)
+	`, []string{"platform:foo"}, nil)
 
 	hiddenAPI := ctx.SingletonForTests("hiddenapi")
 	hiddenapiRule := hiddenAPI.Rule("hiddenapi")
@@ -98,15 +130,15 @@
 			name: "foo",
 			srcs: ["a.java"],
 			compile_dex: true,
-	}
+		}
 
 		java_import {
 			name: "foo",
 			jars: ["a.jar"],
 			compile_dex: true,
 			prefer: false,
-	}
-	`, []string{":foo"}, nil)
+		}
+	`, []string{"platform:foo"}, nil)
 
 	hiddenAPI := ctx.SingletonForTests("hiddenapi")
 	hiddenapiRule := hiddenAPI.Rule("hiddenapi")
@@ -127,15 +159,15 @@
 			name: "foo",
 			srcs: ["a.java"],
 			compile_dex: true,
-	}
+		}
 
 		java_import {
 			name: "foo",
 			jars: ["a.jar"],
 			compile_dex: true,
 			prefer: true,
-	}
-	`, []string{":foo"}, nil)
+		}
+	`, []string{"platform:foo"}, nil)
 
 	hiddenAPI := ctx.SingletonForTests("hiddenapi")
 	hiddenapiRule := hiddenAPI.Rule("hiddenapi")
@@ -236,7 +268,7 @@
 			jars: ["a.jar"],
 			compile_dex: true,
 	}
-	`, []string{":foo"}, &prebuiltHiddenApiDir)
+	`, []string{"platform:foo"}, &prebuiltHiddenApiDir)
 
 	expectedCpInput := prebuiltHiddenApiDir + "/hiddenapi-flags.csv"
 	expectedCpOutput := buildDir + "/hiddenapi/hiddenapi-flags.csv"
diff --git a/java/java.go b/java/java.go
index 59ec94d..8cf4ee5 100644
--- a/java/java.go
+++ b/java/java.go
@@ -40,18 +40,21 @@
 	// Register sdk member types.
 	android.RegisterSdkMemberType(javaHeaderLibsSdkMemberType)
 
+	// Export implementation classes jar as part of the sdk.
+	exportImplementationClassesJar := func(_ android.SdkMemberContext, j *Library) android.Path {
+		implementationJars := j.ImplementationAndResourcesJars()
+		if len(implementationJars) != 1 {
+			panic(fmt.Errorf("there must be only one implementation jar from %q", j.Name()))
+		}
+		return implementationJars[0]
+	}
+
 	// Register java implementation libraries for use only in module_exports (not sdk).
 	android.RegisterSdkMemberType(&librarySdkMemberType{
 		android.SdkMemberTypeBase{
 			PropertyName: "java_libs",
 		},
-		func(_ android.SdkMemberContext, j *Library) android.Path {
-			implementationJars := j.ImplementationAndResourcesJars()
-			if len(implementationJars) != 1 {
-				panic(fmt.Errorf("there must be only one implementation jar from %q", j.Name()))
-			}
-			return implementationJars[0]
-		},
+		exportImplementationClassesJar,
 		sdkSnapshotFilePathForJar,
 		copyEverythingToSnapshot,
 	})
@@ -72,19 +75,11 @@
 			PropertyName: "java_boot_libs",
 			SupportsSdk:  true,
 		},
-		func(ctx android.SdkMemberContext, j *Library) android.Path {
-			// Java boot libs are only provided in the SDK to provide access to their dex implementation
-			// 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()
-		},
-		func(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_boot_libs", "snapshot", "jars", "are", "invalid", name+jarFileSuffix)
-		},
+		// Temporarily export implementation classes jar for java_boot_libs as it is required for the
+		// hiddenapi processing.
+		// TODO(b/179354495): Revert once hiddenapi processing has been modularized.
+		exportImplementationClassesJar,
+		sdkSnapshotFilePathForJar,
 		onlyCopyJarToSnapshot,
 	})
 
@@ -1689,35 +1684,44 @@
 
 	// Combine the classes built from sources, any manifests, and any static libraries into
 	// classes.jar. If there is only one input jar this step will be skipped.
-	var outputFile android.ModuleOutPath
+	var outputFile android.OutputPath
 
 	if len(jars) == 1 && !manifest.Valid() {
+		// Optimization: skip the combine step as there is nothing to do
+		// TODO(ccross): this leaves any module-info.class files, but those should only come from
+		// prebuilt dependencies until we support modules in the platform build, so there shouldn't be
+		// any if len(jars) == 1.
+
+		// Transform the single path to the jar into an OutputPath as that is required by the following
+		// code.
 		if moduleOutPath, ok := jars[0].(android.ModuleOutPath); ok {
-			// Optimization: skip the combine step if there is nothing to do
-			// TODO(ccross): this leaves any module-info.class files, but those should only come from
-			// prebuilt dependencies until we support modules in the platform build, so there shouldn't be
-			// any if len(jars) == 1.
-			outputFile = moduleOutPath
+			// The path contains an embedded OutputPath so reuse that.
+			outputFile = moduleOutPath.OutputPath
+		} else if outputPath, ok := jars[0].(android.OutputPath); ok {
+			// The path is an OutputPath so reuse it directly.
+			outputFile = outputPath
 		} else {
+			// The file is not in the out directory so create an OutputPath into which it can be copied
+			// and which the following code can use to refer to it.
 			combinedJar := android.PathForModuleOut(ctx, "combined", jarName)
 			ctx.Build(pctx, android.BuildParams{
 				Rule:   android.Cp,
 				Input:  jars[0],
 				Output: combinedJar,
 			})
-			outputFile = combinedJar
+			outputFile = combinedJar.OutputPath
 		}
 	} else {
 		combinedJar := android.PathForModuleOut(ctx, "combined", jarName)
 		TransformJarsToJar(ctx, combinedJar, "for javac", jars, manifest,
 			false, nil, nil)
-		outputFile = combinedJar
+		outputFile = combinedJar.OutputPath
 	}
 
 	// jarjar implementation jar if necessary
 	if j.expandJarjarRules != nil {
 		// Transform classes.jar into classes-jarjar.jar
-		jarjarFile := android.PathForModuleOut(ctx, "jarjar", jarName)
+		jarjarFile := android.PathForModuleOut(ctx, "jarjar", jarName).OutputPath
 		TransformJarJar(ctx, jarjarFile, outputFile, j.expandJarjarRules)
 		outputFile = jarjarFile
 
@@ -1762,7 +1766,7 @@
 	implementationAndResourcesJar := outputFile
 	if j.resourceJar != nil {
 		jars := android.Paths{j.resourceJar, implementationAndResourcesJar}
-		combinedJar := android.PathForModuleOut(ctx, "withres", jarName)
+		combinedJar := android.PathForModuleOut(ctx, "withres", jarName).OutputPath
 		TransformJarsToJar(ctx, combinedJar, "for resources", jars, manifest,
 			false, nil, nil)
 		implementationAndResourcesJar = combinedJar
@@ -1788,7 +1792,7 @@
 				android.PathForSource(ctx, "build/make/core/proguard.jacoco.flags"))
 		}
 		// Dex compilation
-		var dexOutputFile android.ModuleOutPath
+		var dexOutputFile android.OutputPath
 		dexOutputFile = j.dexer.compileDex(ctx, flags, j.minSdkVersion(), outputFile, jarName)
 		if ctx.Failed() {
 			return
@@ -1801,17 +1805,17 @@
 		primary = primary && !j.IsReplacedByPrebuilt()
 
 		// Hidden API CSV generation and dex encoding
-		dexOutputFile = j.hiddenAPI.hiddenAPI(ctx, configurationName, primary, dexOutputFile, j.implementationJarFile,
+		dexOutputFile = j.hiddenAPIExtractAndEncode(ctx, configurationName, primary, dexOutputFile, j.implementationJarFile,
 			proptools.Bool(j.dexProperties.Uncompress_dex))
 
 		// merge dex jar with resources if necessary
 		if j.resourceJar != nil {
 			jars := android.Paths{dexOutputFile, j.resourceJar}
-			combinedJar := android.PathForModuleOut(ctx, "dex-withres", jarName)
+			combinedJar := android.PathForModuleOut(ctx, "dex-withres", jarName).OutputPath
 			TransformJarsToJar(ctx, combinedJar, "for dex resources", jars, android.OptionalPath{},
 				false, nil, nil)
 			if *j.dexProperties.Uncompress_dex {
-				combinedAlignedJar := android.PathForModuleOut(ctx, "dex-withres-aligned", jarName)
+				combinedAlignedJar := android.PathForModuleOut(ctx, "dex-withres-aligned", jarName).OutputPath
 				TransformZipAlign(ctx, combinedAlignedJar, combinedJar)
 				dexOutputFile = combinedAlignedJar
 			} else {
@@ -1875,7 +1879,7 @@
 		jarName += strconv.Itoa(idx)
 	}
 
-	classes := android.PathForModuleOut(ctx, "javac", jarName)
+	classes := android.PathForModuleOut(ctx, "javac", jarName).OutputPath
 	TransformJavaToClasses(ctx, classes, idx, srcFiles, srcJars, flags, extraJarDeps)
 
 	if ctx.Config().EmitXrefRules() {
@@ -1955,12 +1959,12 @@
 }
 
 func (j *Module) instrument(ctx android.ModuleContext, flags javaBuilderFlags,
-	classesJar android.Path, jarName string) android.ModuleOutPath {
+	classesJar android.Path, jarName string) android.OutputPath {
 
 	specs := j.jacocoModuleToZipCommand(ctx)
 
 	jacocoReportClassesFile := android.PathForModuleOut(ctx, "jacoco-report-classes", jarName)
-	instrumentedJar := android.PathForModuleOut(ctx, "jacoco", jarName)
+	instrumentedJar := android.PathForModuleOut(ctx, "jacoco", jarName).OutputPath
 
 	jacocoInstrumentJar(ctx, instrumentedJar, jacocoReportClassesFile, classesJar, specs)
 
@@ -2086,6 +2090,11 @@
 	return proptools.StringDefault(j.deviceProperties.Stem, j.Name())
 }
 
+// ConfigurationName returns the name of the module as used in build configuration.
+//
+// This is usually the same as BaseModuleName() except for the <x>.impl libraries created by
+// java_sdk_library in which case this is the BaseModuleName() without the ".impl" suffix,
+// i.e. just <x>.
 func (j *Module) ConfigurationName() string {
 	return proptools.StringDefault(j.deviceProperties.ConfigurationName, j.BaseModuleName())
 }
@@ -2145,6 +2154,11 @@
 }
 
 func (j *Library) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	// Initialize the hiddenapi structure. Pass in the configuration name rather than the module name
+	// so the hidden api will encode the <x>.impl java_ library created by java_sdk_library just as it
+	// would the <x> library if <x> was configured as a boot jar.
+	j.initHiddenAPI(ctx, j.ConfigurationName())
+
 	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
 	if !apexInfo.IsForPlatform() {
 		j.hideApexVariantFromMake = true
@@ -2374,7 +2388,7 @@
 
 	// list of files or filegroup modules that provide data that should be installed alongside
 	// the test
-	Data []string `android:"path"`
+	Data []string `android:"path,arch_variant"`
 
 	// Flag to indicate whether or not to create test config automatically. If AndroidTest.xml
 	// doesn't exist next to the Android.bp, this attribute doesn't need to be set to true
@@ -2678,9 +2692,10 @@
 }
 
 func (j *Binary) DepsMutator(ctx android.BottomUpMutatorContext) {
-	if ctx.Arch().ArchType == android.Common {
+	if ctx.Arch().ArchType == android.Common || ctx.BazelConversionMode() {
 		j.deps(ctx)
-	} else {
+	}
+	if ctx.Arch().ArchType != android.Common || ctx.BazelConversionMode() {
 		// These dependencies ensure the host installation rules will install the jar file and
 		// the jni libraries when the wrapper is installed.
 		ctx.AddVariationDependencies(nil, jniInstallTag, j.binaryProperties.Jni_libs...)
@@ -2844,6 +2859,9 @@
 }
 
 func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	// Initialize the hiddenapi structure.
+	j.initHiddenAPI(ctx, j.BaseModuleName())
+
 	if !ctx.Provider(android.ApexInfoProvider).(android.ApexInfo).IsForPlatform() {
 		j.hideApexVariantFromMake = true
 	}
@@ -2899,6 +2917,9 @@
 	j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.properties.Aidl.Export_include_dirs)
 
 	if ctx.Device() {
+		configurationName := j.BaseModuleName()
+		primary := j.Prebuilt().UsePrebuilt()
+
 		// If this is a variant created for a prebuilt_apex then use the dex implementation jar
 		// obtained from the associated deapexer module.
 		ai := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
@@ -2912,8 +2933,10 @@
 
 			// Get the path of the dex implementation jar from the `deapexer` module.
 			di := ctx.OtherModuleProvider(deapexerModule, android.DeapexerProvider).(android.DeapexerInfo)
-			j.dexJarFile = di.PrebuiltExportPath(j.BaseModuleName(), ".dexjar")
-			if j.dexJarFile == nil {
+			if dexOutputPath := di.PrebuiltExportPath(j.BaseModuleName(), ".dexjar"); dexOutputPath != nil {
+				j.dexJarFile = dexOutputPath
+				j.hiddenAPI.hiddenAPIExtractInformation(ctx, dexOutputPath, outputFile, primary)
+			} else {
 				// This should never happen as a variant for a prebuilt_apex is only created if the
 				// prebuilt_apex has been configured to export the java library dex file.
 				ctx.ModuleErrorf("internal error: no dex implementation jar available from prebuilt_apex %q", deapexerModule.Name())
@@ -2937,17 +2960,14 @@
 			}
 			j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex
 
-			var dexOutputFile android.ModuleOutPath
+			var dexOutputFile android.OutputPath
 			dexOutputFile = j.dexer.compileDex(ctx, flags, j.minSdkVersion(), outputFile, jarName)
 			if ctx.Failed() {
 				return
 			}
 
-			configurationName := j.BaseModuleName()
-			primary := j.Prebuilt().UsePrebuilt()
-
 			// Hidden API CSV generation and dex encoding
-			dexOutputFile = j.hiddenAPI.hiddenAPI(ctx, configurationName, primary, dexOutputFile, outputFile,
+			dexOutputFile = j.hiddenAPIExtractAndEncode(ctx, configurationName, primary, dexOutputFile, outputFile,
 				proptools.Bool(j.dexProperties.Uncompress_dex))
 
 			j.dexJarFile = dexOutputFile
@@ -3313,6 +3333,7 @@
 		&android.ApexProperties{},
 		&RuntimeResourceOverlayProperties{},
 		&LintProperties{},
+		&appTestHelperAppProperties{},
 	)
 
 	android.InitDefaultsModule(module)
diff --git a/java/proto.go b/java/proto.go
index dc5519f..652a4da 100644
--- a/java/proto.go
+++ b/java/proto.go
@@ -82,7 +82,7 @@
 		case "lite", "":
 			ctx.AddVariationDependencies(nil, staticLibTag, "libprotobuf-java-lite")
 		case "full":
-			if ctx.Host() {
+			if ctx.Host() || ctx.BazelConversionMode() {
 				ctx.AddVariationDependencies(nil, staticLibTag, "libprotobuf-java-full")
 			} else {
 				ctx.PropertyErrorf("proto.type", "full java protos only supported on the host")
diff --git a/java/testing.go b/java/testing.go
index 5fcf84c..781106f 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -18,6 +18,7 @@
 	"fmt"
 	"reflect"
 	"sort"
+	"strings"
 	"testing"
 
 	"android/soong/android"
@@ -237,3 +238,11 @@
 		t.Errorf("expected %#q, found %#q", expected, actual)
 	}
 }
+
+func CheckHiddenAPIRuleInputs(t *testing.T, expected string, hiddenAPIRule android.TestingBuildParams) {
+	actual := strings.TrimSpace(strings.Join(android.NormalizePathsForTesting(hiddenAPIRule.Implicits), "\n"))
+	expected = strings.TrimSpace(expected)
+	if actual != expected {
+		t.Errorf("Expected hiddenapi rule inputs:\n%s\nactual inputs:\n%s", expected, actual)
+	}
+}
diff --git a/kernel/prebuilt_kernel_modules.go b/kernel/prebuilt_kernel_modules.go
index 94e04cb..14ac021 100644
--- a/kernel/prebuilt_kernel_modules.go
+++ b/kernel/prebuilt_kernel_modules.go
@@ -72,7 +72,7 @@
 	depmodOut := runDepmod(ctx, modules)
 	strippedModules := stripDebugSymbols(ctx, modules)
 
-	installDir := android.PathForModuleInstall(ctx, "lib", "module")
+	installDir := android.PathForModuleInstall(ctx, "lib", "modules")
 	if pkm.KernelVersion() != "" {
 		installDir = installDir.Join(ctx, pkm.KernelVersion())
 	}
diff --git a/kernel/prebuilt_kernel_modules_test.go b/kernel/prebuilt_kernel_modules_test.go
index b49e167..433548b 100644
--- a/kernel/prebuilt_kernel_modules_test.go
+++ b/kernel/prebuilt_kernel_modules_test.go
@@ -84,12 +84,12 @@
 		})
 
 	expected := []string{
-		"lib/module/5.10/mod1.ko",
-		"lib/module/5.10/mod2.ko",
-		"lib/module/5.10/modules.load",
-		"lib/module/5.10/modules.dep",
-		"lib/module/5.10/modules.softdep",
-		"lib/module/5.10/modules.alias",
+		"lib/modules/5.10/mod1.ko",
+		"lib/modules/5.10/mod2.ko",
+		"lib/modules/5.10/modules.load",
+		"lib/modules/5.10/modules.dep",
+		"lib/modules/5.10/modules.softdep",
+		"lib/modules/5.10/modules.alias",
 	}
 
 	var actual []string
diff --git a/rust/binary.go b/rust/binary.go
index 2963a37..df48916 100644
--- a/rust/binary.go
+++ b/rust/binary.go
@@ -119,6 +119,7 @@
 	outputFile := android.PathForModuleOut(ctx, fileName)
 
 	flags.RustFlags = append(flags.RustFlags, deps.depFlags...)
+	flags.LinkFlags = append(flags.LinkFlags, deps.depLinkFlags...)
 	flags.LinkFlags = append(flags.LinkFlags, deps.linkObjects...)
 
 	TransformSrcToBinary(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
@@ -132,7 +133,7 @@
 	return outputFile
 }
 
-func (binary *binaryDecorator) autoDep(ctx BaseModuleContext) autoDep {
+func (binary *binaryDecorator) autoDep(ctx android.BottomUpMutatorContext) autoDep {
 	// Binaries default to dylib dependencies for device, rlib for host.
 	if binary.preferRlib() {
 		return rlibAutoDep
diff --git a/rust/compiler.go b/rust/compiler.go
index c921824..586063e 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -96,7 +96,11 @@
 	// list of C shared library dependencies
 	Shared_libs []string `android:"arch_variant"`
 
-	// list of C static library dependencies
+	// list of C static library dependencies. Note, static libraries prefixed by "lib" will be passed to rustc
+	// along with "-lstatic=<name>". This will bundle the static library into rlib/static libraries so dependents do
+	// not need to also declare the static library as a dependency. Static libraries which are not prefixed by "lib"
+	// cannot be passed to rustc with this flag and will not be bundled into rlib/static libraries, and thus must
+	// be redeclared in dependents.
 	Static_libs []string `android:"arch_variant"`
 
 	// crate name, required for modules which produce Rust libraries: rust_library, rust_ffi and SourceProvider
diff --git a/rust/fuzz.go b/rust/fuzz.go
index da8f209..6035e68 100644
--- a/rust/fuzz.go
+++ b/rust/fuzz.go
@@ -91,6 +91,6 @@
 	return RlibLinkage
 }
 
-func (fuzzer *fuzzDecorator) autoDep(ctx BaseModuleContext) autoDep {
+func (fuzzer *fuzzDecorator) autoDep(ctx android.BottomUpMutatorContext) autoDep {
 	return rlibAutoDep
 }
diff --git a/rust/image.go b/rust/image.go
index 5ff10ae..628aca3 100644
--- a/rust/image.go
+++ b/rust/image.go
@@ -24,7 +24,7 @@
 var _ android.ImageInterface = (*Module)(nil)
 
 func (mod *Module) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
-	return false
+	return mod.Properties.VendorRamdiskVariantNeeded
 }
 
 func (mod *Module) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
@@ -52,6 +52,10 @@
 	return false
 }
 
+func (mod *Module) InVendorRamdisk() bool {
+	return mod.ModuleBase.InVendorRamdisk() || mod.ModuleBase.InstallInVendorRamdisk()
+}
+
 func (mod *Module) OnlyInRamdisk() bool {
 	// TODO(b/165791368)
 	return false
@@ -71,13 +75,24 @@
 	return Bool(mod.VendorProperties.Vendor_available) || Bool(mod.VendorProperties.Odm_available)
 }
 
+// Always returns false because rust modules do not support product variant.
+func (mod *Module) HasProductVariant() bool {
+	return Bool(mod.VendorProperties.Product_available)
+}
+
+func (mod *Module) HasNonSystemVariants() bool {
+	return mod.HasVendorVariant() || mod.HasProductVariant()
+}
+
 func (c *Module) InProduct() bool {
 	return false
 }
 
 func (mod *Module) SetImageVariation(ctx android.BaseModuleContext, variant string, module android.Module) {
 	m := module.(*Module)
-	if strings.HasPrefix(variant, cc.VendorVariationPrefix) {
+	if variant == android.VendorRamdiskVariation {
+		m.MakeAsPlatform()
+	} else if strings.HasPrefix(variant, cc.VendorVariationPrefix) {
 		m.Properties.ImageVariationPrefix = cc.VendorVariationPrefix
 		m.Properties.VndkVersion = strings.TrimPrefix(variant, cc.VendorVariationPrefix)
 
@@ -108,6 +123,8 @@
 	}
 
 	coreVariantNeeded := true
+	vendorRamdiskVariantNeeded := false
+
 	var vendorVariants []string
 
 	if mod.HasVendorVariant() {
@@ -129,15 +146,23 @@
 			// We can't check shared() here because image mutator is called before the library mutator, so we need to
 			// check buildShared()
 			if lib.buildShared() {
-				mctx.PropertyErrorf(prop, "can only be set for rust_ffi_static modules.")
+				mctx.PropertyErrorf(prop, "cannot be set for rust_ffi or rust_ffi_shared modules.")
 			} else {
 				vendorVariants = append(vendorVariants, platformVndkVersion)
 			}
 		}
 	}
 
+	if Bool(mod.Properties.Vendor_ramdisk_available) {
+		if lib, ok := mod.compiler.(libraryInterface); !ok || (ok && lib.buildShared()) {
+			mctx.PropertyErrorf("vendor_ramdisk_available", "cannot be set for rust_ffi or rust_ffi_shared modules.")
+		} else {
+			vendorRamdiskVariantNeeded = true
+		}
+	}
+
 	if vendorSpecific {
-		if lib, ok := mod.compiler.(libraryInterface); !ok || (ok && !lib.static()) {
+		if lib, ok := mod.compiler.(libraryInterface); !ok || (ok && (lib.buildShared() || lib.buildDylib() || lib.buildRlib())) {
 			mctx.ModuleErrorf("Rust vendor specific modules are currently only supported for rust_ffi_static modules.")
 		} else {
 			coreVariantNeeded = false
@@ -146,6 +171,8 @@
 	}
 
 	mod.Properties.CoreVariantNeeded = coreVariantNeeded
+	mod.Properties.VendorRamdiskVariantNeeded = vendorRamdiskVariantNeeded
+
 	for _, variant := range android.FirstUniqueStrings(vendorVariants) {
 		mod.Properties.ExtraVariants = append(mod.Properties.ExtraVariants, cc.VendorVariationPrefix+variant)
 	}
diff --git a/rust/image_test.go b/rust/image_test.go
index fd71962..1515aa2 100644
--- a/rust/image_test.go
+++ b/rust/image_test.go
@@ -21,7 +21,7 @@
 	"android/soong/cc"
 )
 
-// Test that cc_binaries can link against rust_ffi_static libraries.
+// Test that cc modules can link against vendor_available rust_ffi_static libraries.
 func TestVendorLinkage(t *testing.T) {
 	ctx := testRust(t, `
 			cc_binary {
@@ -44,9 +44,33 @@
 	}
 }
 
+// Test that cc modules can link against vendor_ramdisk_available rust_ffi_static libraries.
+func TestVendorRamdiskLinkage(t *testing.T) {
+	ctx := testRust(t, `
+			cc_library_static {
+				name: "libcc_vendor_ramdisk",
+				static_libs: ["libfoo_vendor_ramdisk"],
+				system_shared_libs: [],
+				vendor_ramdisk_available: true,
+			}
+			rust_ffi_static {
+				name: "libfoo_vendor_ramdisk",
+				crate_name: "foo",
+				srcs: ["foo.rs"],
+				vendor_ramdisk_available: true,
+			}
+		`)
+
+	vendorRamdiskLibrary := ctx.ModuleForTests("libcc_vendor_ramdisk", "android_vendor_ramdisk_arm64_armv8-a_static").Module().(*cc.Module)
+
+	if !android.InList("libfoo_vendor_ramdisk.vendor_ramdisk", vendorRamdiskLibrary.Properties.AndroidMkStaticLibs) {
+		t.Errorf("libcc_vendor_ramdisk should have a dependency on libfoo_vendor_ramdisk")
+	}
+}
+
 // Test that shared libraries cannot be made vendor available until proper support is added.
 func TestForbiddenVendorLinkage(t *testing.T) {
-	testRustError(t, "can only be set for rust_ffi_static modules", `
+	testRustError(t, "cannot be set for rust_ffi or rust_ffi_shared modules.", `
 		rust_ffi_shared {
 			name: "libfoo_vendor",
 			crate_name: "foo",
@@ -54,6 +78,14 @@
 			vendor_available: true,
 		}
 	`)
+	testRustError(t, "cannot be set for rust_ffi or rust_ffi_shared modules.", `
+		rust_ffi_shared {
+			name: "libfoo_vendor",
+			crate_name: "foo",
+			srcs: ["foo.rs"],
+			vendor_ramdisk_available: true,
+		}
+	`)
 	testRustError(t, "Rust vendor specific modules are currently only supported for rust_ffi_static modules.", `
 		rust_ffi {
 			name: "libfoo_vendor",
@@ -70,4 +102,13 @@
 			vendor: true,
 		}
 	`)
+	testRustError(t, "Rust vendor specific modules are currently only supported for rust_ffi_static modules.", `
+		rust_binary {
+			name: "foo_vendor",
+			crate_name: "foo",
+			srcs: ["foo.rs"],
+			vendor: true,
+		}
+	`)
+
 }
diff --git a/rust/library.go b/rust/library.go
index 6433285..7ff13ec 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -219,13 +219,17 @@
 	library.MutatedProperties.VariantIsSource = true
 }
 
-func (library *libraryDecorator) autoDep(ctx BaseModuleContext) autoDep {
+func (library *libraryDecorator) autoDep(ctx android.BottomUpMutatorContext) autoDep {
 	if library.preferRlib() {
 		return rlibAutoDep
 	} else if library.rlib() || library.static() {
 		return rlibAutoDep
 	} else if library.dylib() || library.shared() {
 		return dylibAutoDep
+	} else if ctx.BazelConversionMode() {
+		// In Bazel conversion mode, we are currently ignoring the deptag, so we just need to supply a
+		// compatible tag in order to add the dependency.
+		return rlibAutoDep
 	} else {
 		panic(fmt.Errorf("autoDep called on library %q that has no enabled variants.", ctx.ModuleName()))
 	}
@@ -439,6 +443,7 @@
 	}
 
 	flags.RustFlags = append(flags.RustFlags, deps.depFlags...)
+	flags.LinkFlags = append(flags.LinkFlags, deps.depLinkFlags...)
 	flags.LinkFlags = append(flags.LinkFlags, deps.linkObjects...)
 
 	if library.dylib() {
@@ -478,7 +483,6 @@
 
 	if library.rlib() || library.dylib() {
 		library.flagExporter.exportLinkDirs(deps.linkDirs...)
-		library.flagExporter.exportDepFlags(deps.depFlags...)
 		library.flagExporter.exportLinkObjects(deps.linkObjects...)
 	}
 
diff --git a/rust/proc_macro.go b/rust/proc_macro.go
index f753e7f..115045a 100644
--- a/rust/proc_macro.go
+++ b/rust/proc_macro.go
@@ -79,6 +79,6 @@
 	return stem + String(procMacro.baseCompiler.Properties.Suffix)
 }
 
-func (procMacro *procMacroDecorator) autoDep(ctx BaseModuleContext) autoDep {
+func (procMacro *procMacroDecorator) autoDep(ctx android.BottomUpMutatorContext) autoDep {
 	return rlibAutoDep
 }
diff --git a/rust/rust.go b/rust/rust.go
index 2ef9daf..dc23abb 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -74,8 +74,16 @@
 	SubName              string `blueprint:"mutated"`
 
 	// Set by imageMutator
-	CoreVariantNeeded bool     `blueprint:"mutated"`
-	ExtraVariants     []string `blueprint:"mutated"`
+	CoreVariantNeeded          bool     `blueprint:"mutated"`
+	VendorRamdiskVariantNeeded bool     `blueprint:"mutated"`
+	ExtraVariants              []string `blueprint:"mutated"`
+
+	// Make this module available when building for vendor ramdisk.
+	// On device without a dedicated recovery partition, the module is only
+	// available after switching root into
+	// /first_stage_ramdisk. To expose the module before switching root, install
+	// the recovery variant instead (TODO(b/165791368) recovery not yet supported)
+	Vendor_ramdisk_available *bool
 
 	// Minimum sdk version that the artifact should support when it runs as part of mainline modules(APEX).
 	Min_sdk_version *string
@@ -280,10 +288,15 @@
 	SharedLibDeps android.Paths
 	StaticLibs    android.Paths
 	ProcMacros    RustLibraries
-	linkDirs      []string
-	depFlags      []string
-	linkObjects   []string
-	//ReexportedDeps android.Paths
+
+	// depFlags and depLinkFlags are rustc and linker (clang) flags.
+	depFlags     []string
+	depLinkFlags []string
+
+	// linkDirs are link paths passed via -L to rustc. linkObjects are objects passed directly to the linker.
+	// Both of these are exported and propagate to dependencies.
+	linkDirs    []string
+	linkObjects []string
 
 	// Used by bindgen modules which call clang
 	depClangFlags         []string
@@ -328,12 +341,10 @@
 
 type exportedFlagsProducer interface {
 	exportLinkDirs(...string)
-	exportDepFlags(...string)
 	exportLinkObjects(...string)
 }
 
 type flagExporter struct {
-	depFlags    []string
 	linkDirs    []string
 	linkObjects []string
 }
@@ -342,17 +353,12 @@
 	flagExporter.linkDirs = android.FirstUniqueStrings(append(flagExporter.linkDirs, dirs...))
 }
 
-func (flagExporter *flagExporter) exportDepFlags(flags ...string) {
-	flagExporter.depFlags = android.FirstUniqueStrings(append(flagExporter.depFlags, flags...))
-}
-
 func (flagExporter *flagExporter) exportLinkObjects(flags ...string) {
 	flagExporter.linkObjects = android.FirstUniqueStrings(append(flagExporter.linkObjects, flags...))
 }
 
 func (flagExporter *flagExporter) setProvider(ctx ModuleContext) {
 	ctx.SetProvider(FlagExporterInfoProvider, FlagExporterInfo{
-		Flags:       flagExporter.depFlags,
 		LinkDirs:    flagExporter.linkDirs,
 		LinkObjects: flagExporter.linkObjects,
 	})
@@ -660,7 +666,9 @@
 
 	// Differentiate static libraries that are vendor available
 	if mod.UseVndk() {
-		mod.Properties.SubName += ".vendor"
+		mod.Properties.SubName += cc.VendorSuffix
+	} else if mod.InVendorRamdisk() && !mod.OnlyInVendorRamdisk() {
+		mod.Properties.SubName += cc.VendorRamdiskSuffix
 	}
 
 	if !toolchain.Supported() {
@@ -785,7 +793,7 @@
 )
 
 type autoDeppable interface {
-	autoDep(ctx BaseModuleContext) autoDep
+	autoDep(ctx android.BottomUpMutatorContext) autoDep
 }
 
 func (mod *Module) begin(ctx BaseModuleContext) {
@@ -898,8 +906,24 @@
 			exportDep := false
 			switch {
 			case cc.IsStaticDepTag(depTag):
-				depPaths.linkDirs = append(depPaths.linkDirs, linkPath)
+				// Only pass -lstatic for rlibs as it results in dylib bloat.
+				if lib, ok := ctx.Module().(*Module).compiler.(libraryInterface); ok && lib.rlib() {
+					// Link cc static libraries using "-lstatic" so rustc can reason about how to handle these
+					// (for example, bundling them into rlibs).
+					//
+					// rustc does not support linking libraries with the "-l" flag unless they are prefixed by "lib".
+					// If we need to link a library that isn't prefixed by "lib", we'll just link to it directly through
+					// linkObjects; such a library may need to be redeclared by static dependents.
+					if libName, ok := libNameFromFilePath(linkObject.Path()); ok {
+						depPaths.depFlags = append(depPaths.depFlags, "-lstatic="+libName)
+					}
+				}
+
+				// Add this to linkObjects to pass the library directly to the linker as well. This propagates
+				// to dependencies to avoid having to redeclare static libraries for dependents of the dylib variant.
 				depPaths.linkObjects = append(depPaths.linkObjects, linkObject.String())
+				depPaths.linkDirs = append(depPaths.linkDirs, linkPath)
+
 				exportedInfo := ctx.OtherModuleProvider(dep, cc.FlagExporterInfoProvider).(cc.FlagExporterInfo)
 				depPaths.depIncludePaths = append(depPaths.depIncludePaths, exportedInfo.IncludeDirs...)
 				depPaths.depSystemIncludePaths = append(depPaths.depSystemIncludePaths, exportedInfo.SystemIncludeDirs...)
@@ -1213,6 +1237,16 @@
 	return false
 }
 
+// If a library file has a "lib" prefix, extract the library name without the prefix.
+func libNameFromFilePath(filepath android.Path) (string, bool) {
+	libName := strings.TrimSuffix(filepath.Base(), filepath.Ext())
+	if strings.HasPrefix(libName, "lib") {
+		libName = libName[3:]
+		return libName, true
+	}
+	return "", false
+}
+
 var Bool = proptools.Bool
 var BoolDefault = proptools.BoolDefault
 var String = proptools.String
diff --git a/rust/rust_test.go b/rust/rust_test.go
index abc9af9..88d9643 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -213,6 +213,7 @@
 			name: "librlib",
 			srcs: ["foo.rs"],
 			crate_name: "rlib",
+			static_libs: ["libstatic"],
 		}
 		rust_proc_macro {
 			name: "libpm",
@@ -230,6 +231,7 @@
 		}
 	`)
 	module := ctx.ModuleForTests("fizz-buzz", "linux_glibc_x86_64").Module().(*Module)
+	rustc := ctx.ModuleForTests("librlib", "linux_glibc_x86_64_rlib_rlib-std").Rule("rustc")
 
 	// Since dependencies are added to AndroidMk* properties, we can check these to see if they've been picked up.
 	if !android.InList("libdylib", module.Properties.AndroidMkDylibs) {
@@ -251,6 +253,11 @@
 	if !android.InList("libstatic", module.Properties.AndroidMkStaticLibs) {
 		t.Errorf("Static library dependency not detected (dependency missing from AndroidMkStaticLibs)")
 	}
+
+	if !strings.Contains(rustc.Args["rustcFlags"], "-lstatic=static") {
+		t.Errorf("-lstatic flag not being passed to rustc for static library")
+	}
+
 }
 
 func TestSourceProviderDeps(t *testing.T) {
diff --git a/rust/test.go b/rust/test.go
index 92b4860..6caa7b1 100644
--- a/rust/test.go
+++ b/rust/test.go
@@ -15,6 +15,8 @@
 package rust
 
 import (
+	"github.com/google/blueprint/proptools"
+
 	"android/soong/android"
 	"android/soong/tradefed"
 )
@@ -127,6 +129,9 @@
 		ctx.PropertyErrorf("no_named_install_directory", "Module install directory may only be disabled if relative_install_path is set")
 	}
 
+	if ctx.Host() && test.Properties.Test_options.Unit_test == nil {
+		test.Properties.Test_options.Unit_test = proptools.BoolPtr(true)
+	}
 	test.binaryDecorator.install(ctx)
 }
 
@@ -141,7 +146,7 @@
 	return flags
 }
 
-func (test *testDecorator) autoDep(ctx BaseModuleContext) autoDep {
+func (test *testDecorator) autoDep(ctx android.BottomUpMutatorContext) autoDep {
 	return rlibAutoDep
 }
 
diff --git a/rust/testing.go b/rust/testing.go
index bb511b6..4c4df4a 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -101,6 +101,7 @@
 			no_stdlibs: true,
 			host_supported: true,
 			vendor_available: true,
+			vendor_ramdisk_available: true,
                         native_coverage: false,
 			sysroot: true,
 			apex_available: ["//apex_available:platform", "//apex_available:anyapex"],
@@ -113,6 +114,7 @@
 			no_stdlibs: true,
 			host_supported: true,
 			vendor_available: true,
+			vendor_ramdisk_available: true,
                         native_coverage: false,
 			sysroot: true,
 			apex_available: ["//apex_available:platform", "//apex_available:anyapex"],
diff --git a/scripts/build-mainline-modules.sh b/scripts/build-mainline-modules.sh
index ea62af4..b8485ea 100755
--- a/scripts/build-mainline-modules.sh
+++ b/scripts/build-mainline-modules.sh
@@ -61,6 +61,9 @@
   esac
 }
 
+# Make sure this build builds from source, regardless of the default.
+export SOONG_CONFIG_art_module_source_build=true
+
 OUT_DIR=$(source build/envsetup.sh > /dev/null; TARGET_PRODUCT= get_build_var OUT_DIR)
 DIST_DIR=$(source build/envsetup.sh > /dev/null; TARGET_PRODUCT= get_build_var DIST_DIR)
 
diff --git a/scripts/gen_ndk_backedby_apex.sh b/scripts/gen_ndk_backedby_apex.sh
index e0da602..4abaaba 100755
--- a/scripts/gen_ndk_backedby_apex.sh
+++ b/scripts/gen_ndk_backedby_apex.sh
@@ -23,33 +23,50 @@
     echo "**************************** Usage Instructions ****************************"
     echo "This script is used to generate the Mainline modules backed-by NDK symbols."
     echo ""
-    echo "To run this script use: ./ndk_backedby_module.sh \$BINARY_IMAGE_DIRECTORY \$OUTPUT_FILE_PATH \$NDK_LIB_NAME_LIST"
-    echo "For example: If all the module image files that you would like to run is under directory '/myModule' and output write to /backedby.txt then the command would be:"
-    echo "./ndk_usedby_module.sh /myModule /backedby.txt /ndkLibList.txt"
+    echo "To run this script use: ./gen_ndk_backed_by_apex.sh \$OUTPUT_FILE_PATH \$NDK_LIB_NAME_LIST \$MODULE_LIB1 \$MODULE_LIB2..."
+    echo "For example: If output write to /backedby.txt then the command would be:"
+    echo "./gen_ndk_backed_by_apex.sh /backedby.txt /ndkLibList.txt lib1.so lib2.so"
     echo "If the module1 is backing lib1 then the backedby.txt would contains: "
     echo "lib1"
 }
 
+contains() {
+  val="$1"
+  shift
+  for x in "$@"; do
+    if [ "$x" = "$val" ]; then
+      return 0
+    fi
+  done
+  return 1
+}
+
+
 genBackedByList() {
-  dir="$1"
-  [[ ! -e "$2" ]] && echo "" >> "$2"
+  out="$1"
+  shift
+  ndk_list="$1"
+  shift
+  rm -f "$out"
+  touch "$out"
   while IFS= read -r line
   do
     soFileName=$(echo "$line" | sed 's/\(.*so\).*/\1/')
     if [[ ! -z "$soFileName" && "$soFileName" != *"#"* ]]
     then
-      find "$dir" -type f -name "$soFileName" -exec echo "$soFileName" >> "$2" \;
+      if contains "$soFileName" "$@"; then
+        echo "$soFileName" >> "$out"
+      fi
     fi
-  done < "$3"
+  done < "$ndk_list"
 }
 
 if [[ "$1" == "help" ]]
 then
   printHelp
-elif [[ "$#" -ne 3 ]]
+elif [[ "$#" -lt 2 ]]
 then
-  echo "Wrong argument length. Expecting 3 argument representing image file directory, output path, path to ndk library list."
+  echo "Wrong argument length. Expecting at least 2 argument representing output path, path to ndk library list, followed by a list of libraries in the Mainline module."
 else
-  [[ -e "$2" ]] && rm "$2"
-  genBackedByList "$1" "$2" "$3"
+  genBackedByList "$@"
 fi
diff --git a/scripts/hiddenapi/Android.bp b/scripts/hiddenapi/Android.bp
new file mode 100644
index 0000000..a669cad
--- /dev/null
+++ b/scripts/hiddenapi/Android.bp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * 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.
+ */
+
+python_binary_host {
+    name: "merge_csv",
+    main: "merge_csv.py",
+    srcs: ["merge_csv.py"],
+    version: {
+        py2: {
+            enabled: false,
+        },
+        py3: {
+            enabled: true,
+            embedded_launcher: true,
+        },
+    },
+}
+
+python_binary_host {
+    name: "generate_hiddenapi_lists",
+    main: "generate_hiddenapi_lists.py",
+    srcs: ["generate_hiddenapi_lists.py"],
+    version: {
+        py2: {
+            enabled: false,
+        },
+        py3: {
+            enabled: true,
+            embedded_launcher: true,
+        },
+    },
+}
diff --git a/scripts/hiddenapi/generate_hiddenapi_lists.py b/scripts/hiddenapi/generate_hiddenapi_lists.py
new file mode 100755
index 0000000..6816475
--- /dev/null
+++ b/scripts/hiddenapi/generate_hiddenapi_lists.py
@@ -0,0 +1,383 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# 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.
+"""Generate API lists for non-SDK API enforcement."""
+import argparse
+from collections import defaultdict, namedtuple
+import functools
+import os
+import re
+import sys
+
+# Names of flags recognized by the `hiddenapi` tool.
+FLAG_SDK = 'sdk'
+FLAG_UNSUPPORTED = 'unsupported'
+FLAG_BLOCKED = 'blocked'
+FLAG_MAX_TARGET_O = 'max-target-o'
+FLAG_MAX_TARGET_P = 'max-target-p'
+FLAG_MAX_TARGET_Q = 'max-target-q'
+FLAG_MAX_TARGET_R = 'max-target-r'
+FLAG_CORE_PLATFORM_API = 'core-platform-api'
+FLAG_PUBLIC_API = 'public-api'
+FLAG_SYSTEM_API = 'system-api'
+FLAG_TEST_API = 'test-api'
+
+# List of all known flags.
+FLAGS_API_LIST = [
+    FLAG_SDK,
+    FLAG_UNSUPPORTED,
+    FLAG_BLOCKED,
+    FLAG_MAX_TARGET_O,
+    FLAG_MAX_TARGET_P,
+    FLAG_MAX_TARGET_Q,
+    FLAG_MAX_TARGET_R,
+]
+ALL_FLAGS = FLAGS_API_LIST + [
+    FLAG_CORE_PLATFORM_API,
+    FLAG_PUBLIC_API,
+    FLAG_SYSTEM_API,
+    FLAG_TEST_API,
+]
+
+FLAGS_API_LIST_SET = set(FLAGS_API_LIST)
+ALL_FLAGS_SET = set(ALL_FLAGS)
+
+# Option specified after one of FLAGS_API_LIST to indicate that
+# only known and otherwise unassigned entries should be assign the
+# given flag.
+# For example, the max-target-P list is checked in as it was in P,
+# but signatures have changes since then. The flag instructs this
+# script to skip any entries which do not exist any more.
+FLAG_IGNORE_CONFLICTS = "ignore-conflicts"
+
+# Option specified after one of FLAGS_API_LIST to express that all
+# apis within a given set of packages should be assign the given flag.
+FLAG_PACKAGES = "packages"
+
+# Option specified after one of FLAGS_API_LIST to indicate an extra
+# tag that should be added to the matching APIs.
+FLAG_TAG = "tag"
+
+# Regex patterns of fields/methods used in serialization. These are
+# considered public API despite being hidden.
+SERIALIZATION_PATTERNS = [
+    r'readObject\(Ljava/io/ObjectInputStream;\)V',
+    r'readObjectNoData\(\)V',
+    r'readResolve\(\)Ljava/lang/Object;',
+    r'serialVersionUID:J',
+    r'serialPersistentFields:\[Ljava/io/ObjectStreamField;',
+    r'writeObject\(Ljava/io/ObjectOutputStream;\)V',
+    r'writeReplace\(\)Ljava/lang/Object;',
+]
+
+# Single regex used to match serialization API. It combines all the
+# SERIALIZATION_PATTERNS into a single regular expression.
+SERIALIZATION_REGEX = re.compile(r'.*->(' + '|'.join(SERIALIZATION_PATTERNS) + r')$')
+
+# Predicates to be used with filter_apis.
+HAS_NO_API_LIST_ASSIGNED = lambda api, flags: not FLAGS_API_LIST_SET.intersection(flags)
+IS_SERIALIZATION = lambda api, flags: SERIALIZATION_REGEX.match(api)
+
+
+class StoreOrderedOptions(argparse.Action):
+    """An argparse action that stores a number of option arguments in the order that
+    they were specified.
+    """
+    def __call__(self, parser, args, values, option_string = None):
+        items = getattr(args, self.dest, None)
+        if items is None:
+            items = []
+        items.append([option_string.lstrip('-'), values])
+        setattr(args, self.dest, items)
+
+def get_args():
+    """Parses command line arguments.
+
+    Returns:
+        Namespace: dictionary of parsed arguments
+    """
+    parser = argparse.ArgumentParser()
+    parser.add_argument('--output', required=True)
+    parser.add_argument('--csv', nargs='*', default=[], metavar='CSV_FILE',
+        help='CSV files to be merged into output')
+
+    for flag in ALL_FLAGS:
+        parser.add_argument('--' + flag, dest='ordered_flags', metavar='TXT_FILE',
+            action=StoreOrderedOptions, help='lists of entries with flag "' + flag + '"')
+    parser.add_argument('--' + FLAG_IGNORE_CONFLICTS, dest='ordered_flags', nargs=0,
+        action=StoreOrderedOptions, help='Indicates that only known and otherwise unassigned '
+        'entries should be assign the given flag. Must follow a list of entries and applies '
+        'to the preceding such list.')
+    parser.add_argument('--' + FLAG_PACKAGES, dest='ordered_flags', nargs=0,
+        action=StoreOrderedOptions, help='Indicates that the previous list of entries '
+        'is a list of packages. All members in those packages will be given the flag. '
+        'Must follow a list of entries and applies to the preceding such list.')
+    parser.add_argument('--' + FLAG_TAG, dest='ordered_flags', nargs=1,
+        action=StoreOrderedOptions, help='Adds an extra tag to the previous list of entries. '
+        'Must follow a list of entries and applies to the preceding such list.')
+
+    return parser.parse_args()
+
+
+def read_lines(filename):
+    """Reads entire file and return it as a list of lines.
+
+    Lines which begin with a hash are ignored.
+
+    Args:
+        filename (string): Path to the file to read from.
+
+    Returns:
+        Lines of the file as a list of string.
+    """
+    with open(filename, 'r') as f:
+        lines = f.readlines();
+    lines = filter(lambda line: not line.startswith('#'), lines)
+    lines = map(lambda line: line.strip(), lines)
+    return set(lines)
+
+
+def write_lines(filename, lines):
+    """Writes list of lines into a file, overwriting the file if it exists.
+
+    Args:
+        filename (string): Path to the file to be writing into.
+        lines (list): List of strings to write into the file.
+    """
+    lines = map(lambda line: line + '\n', lines)
+    with open(filename, 'w') as f:
+        f.writelines(lines)
+
+
+def extract_package(signature):
+    """Extracts the package from a signature.
+
+    Args:
+        signature (string): JNI signature of a method or field.
+
+    Returns:
+        The package name of the class containing the field/method.
+    """
+    full_class_name = signature.split(";->")[0]
+    # Example: Landroid/hardware/radio/V1_2/IRadio$Proxy
+    if (full_class_name[0] != "L"):
+        raise ValueError("Expected to start with 'L': %s" % full_class_name)
+    full_class_name = full_class_name[1:]
+    # If full_class_name doesn't contain '/', then package_name will be ''.
+    package_name = full_class_name.rpartition("/")[0]
+    return package_name.replace('/', '.')
+
+
+class FlagsDict:
+    def __init__(self):
+        self._dict_keyset = set()
+        self._dict = defaultdict(set)
+
+    def _check_entries_set(self, keys_subset, source):
+        assert isinstance(keys_subset, set)
+        assert keys_subset.issubset(self._dict_keyset), (
+            "Error: {} specifies signatures not present in code:\n"
+            "{}"
+            "Please visit go/hiddenapi for more information.").format(
+                source, "".join(map(lambda x: "  " + str(x) + "\n", keys_subset - self._dict_keyset)))
+
+    def _check_flags_set(self, flags_subset, source):
+        assert isinstance(flags_subset, set)
+        assert flags_subset.issubset(ALL_FLAGS_SET), (
+            "Error processing: {}\n"
+            "The following flags were not recognized: \n"
+            "{}\n"
+            "Please visit go/hiddenapi for more information.").format(
+                source, "\n".join(flags_subset - ALL_FLAGS_SET))
+
+    def filter_apis(self, filter_fn):
+        """Returns APIs which match a given predicate.
+
+        This is a helper function which allows to filter on both signatures (keys) and
+        flags (values). The built-in filter() invokes the lambda only with dict's keys.
+
+        Args:
+            filter_fn : Function which takes two arguments (signature/flags) and returns a boolean.
+
+        Returns:
+            A set of APIs which match the predicate.
+        """
+        return set(filter(lambda x: filter_fn(x, self._dict[x]), self._dict_keyset))
+
+    def get_valid_subset_of_unassigned_apis(self, api_subset):
+        """Sanitizes a key set input to only include keys which exist in the dictionary
+        and have not been assigned any API list flags.
+
+        Args:
+            entries_subset (set/list): Key set to be sanitized.
+
+        Returns:
+            Sanitized key set.
+        """
+        assert isinstance(api_subset, set)
+        return api_subset.intersection(self.filter_apis(HAS_NO_API_LIST_ASSIGNED))
+
+    def generate_csv(self):
+        """Constructs CSV entries from a dictionary.
+
+        Old versions of flags are used to generate the file.
+
+        Returns:
+            List of lines comprising a CSV file. See "parse_and_merge_csv" for format description.
+        """
+        lines = []
+        for api in self._dict:
+          flags = sorted(self._dict[api])
+          lines.append(",".join([api] + flags))
+        return sorted(lines)
+
+    def parse_and_merge_csv(self, csv_lines, source = "<unknown>"):
+        """Parses CSV entries and merges them into a given dictionary.
+
+        The expected CSV format is:
+            <api signature>,<flag1>,<flag2>,...,<flagN>
+
+        Args:
+            csv_lines (list of strings): Lines read from a CSV file.
+            source (string): Origin of `csv_lines`. Will be printed in error messages.
+
+        Throws:
+            AssertionError if parsed flags are invalid.
+        """
+        # Split CSV lines into arrays of values.
+        csv_values = [ line.split(',') for line in csv_lines ]
+
+        # Update the full set of API signatures.
+        self._dict_keyset.update([ csv[0] for csv in csv_values ])
+
+        # Check that all flags are known.
+        csv_flags = set()
+        for csv in csv_values:
+          csv_flags.update(csv[1:])
+        self._check_flags_set(csv_flags, source)
+
+        # Iterate over all CSV lines, find entry in dict and append flags to it.
+        for csv in csv_values:
+            flags = csv[1:]
+            if (FLAG_PUBLIC_API in flags) or (FLAG_SYSTEM_API in flags):
+                flags.append(FLAG_SDK)
+            self._dict[csv[0]].update(flags)
+
+    def assign_flag(self, flag, apis, source="<unknown>", tag = None):
+        """Assigns a flag to given subset of entries.
+
+        Args:
+            flag (string): One of ALL_FLAGS.
+            apis (set): Subset of APIs to receive the flag.
+            source (string): Origin of `entries_subset`. Will be printed in error messages.
+
+        Throws:
+            AssertionError if parsed API signatures of flags are invalid.
+        """
+        # Check that all APIs exist in the dict.
+        self._check_entries_set(apis, source)
+
+        # Check that the flag is known.
+        self._check_flags_set(set([ flag ]), source)
+
+        # Iterate over the API subset, find each entry in dict and assign the flag to it.
+        for api in apis:
+            self._dict[api].add(flag)
+            if tag:
+                self._dict[api].add(tag)
+
+
+FlagFile = namedtuple('FlagFile', ('flag', 'file', 'ignore_conflicts', 'packages', 'tag'))
+
+def parse_ordered_flags(ordered_flags):
+    r = []
+    currentflag, file, ignore_conflicts, packages, tag = None, None, False, False, None
+    for flag_value in ordered_flags:
+        flag, value = flag_value[0], flag_value[1]
+        if flag in ALL_FLAGS_SET:
+            if currentflag:
+                r.append(FlagFile(currentflag, file, ignore_conflicts, packages, tag))
+                ignore_conflicts, packages, tag = False, False, None
+            currentflag = flag
+            file = value
+        else:
+            if currentflag is None:
+                raise argparse.ArgumentError('--%s is only allowed after one of %s' % (
+                    flag, ' '.join(['--%s' % f for f in ALL_FLAGS_SET])))
+            if flag == FLAG_IGNORE_CONFLICTS:
+                ignore_conflicts = True
+            elif flag == FLAG_PACKAGES:
+                packages = True
+            elif flag == FLAG_TAG:
+                tag = value[0]
+
+
+    if currentflag:
+        r.append(FlagFile(currentflag, file, ignore_conflicts, packages, tag))
+    return r
+
+
+def main(argv):
+    # Parse arguments.
+    args = vars(get_args())
+    flagfiles = parse_ordered_flags(args['ordered_flags'])
+
+    # Initialize API->flags dictionary.
+    flags = FlagsDict()
+
+    # Merge input CSV files into the dictionary.
+    # Do this first because CSV files produced by parsing API stubs will
+    # contain the full set of APIs. Subsequent additions from text files
+    # will be able to detect invalid entries, and/or filter all as-yet
+    # unassigned entries.
+    for filename in args["csv"]:
+        flags.parse_and_merge_csv(read_lines(filename), filename)
+
+    # Combine inputs which do not require any particular order.
+    # (1) Assign serialization API to SDK.
+    flags.assign_flag(FLAG_SDK, flags.filter_apis(IS_SERIALIZATION))
+
+    # (2) Merge text files with a known flag into the dictionary.
+    for info in flagfiles:
+        if (not info.ignore_conflicts) and (not info.packages):
+            flags.assign_flag(info.flag, read_lines(info.file), info.file, info.tag)
+
+    # Merge text files where conflicts should be ignored.
+    # This will only assign the given flag if:
+    # (a) the entry exists, and
+    # (b) it has not been assigned any other flag.
+    # Because of (b), this must run after all strict assignments have been performed.
+    for info in flagfiles:
+        if info.ignore_conflicts:
+            valid_entries = flags.get_valid_subset_of_unassigned_apis(read_lines(info.file))
+            flags.assign_flag(info.flag, valid_entries, filename, info.tag)
+
+    # All members in the specified packages will be assigned the appropriate flag.
+    for info in flagfiles:
+        if info.packages:
+            packages_needing_list = set(read_lines(info.file))
+            should_add_signature_to_list = lambda sig,lists: extract_package(
+                sig) in packages_needing_list and not lists
+            valid_entries = flags.filter_apis(should_add_signature_to_list)
+            flags.assign_flag(info.flag, valid_entries, info.file, info.tag)
+
+    # Mark all remaining entries as blocked.
+    flags.assign_flag(FLAG_BLOCKED, flags.filter_apis(HAS_NO_API_LIST_ASSIGNED))
+
+    # Write output.
+    write_lines(args["output"], flags.generate_csv())
+
+if __name__ == "__main__":
+    main(sys.argv)
diff --git a/scripts/hiddenapi/generate_hiddenapi_lists_test.py b/scripts/hiddenapi/generate_hiddenapi_lists_test.py
new file mode 100755
index 0000000..ff3d708
--- /dev/null
+++ b/scripts/hiddenapi/generate_hiddenapi_lists_test.py
@@ -0,0 +1,104 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# 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.
+"""Unit tests for Hidden API list generation."""
+import unittest
+from generate_hiddenapi_lists import *
+
+class TestHiddenapiListGeneration(unittest.TestCase):
+
+    def test_filter_apis(self):
+        # Initialize flags so that A and B are put on the allow list and
+        # C, D, E are left unassigned. Try filtering for the unassigned ones.
+        flags = FlagsDict()
+        flags.parse_and_merge_csv(['A,' + FLAG_SDK, 'B,' + FLAG_SDK,
+                        'C', 'D', 'E'])
+        filter_set = flags.filter_apis(lambda api, flags: not flags)
+        self.assertTrue(isinstance(filter_set, set))
+        self.assertEqual(filter_set, set([ 'C', 'D', 'E' ]))
+
+    def test_get_valid_subset_of_unassigned_keys(self):
+        # Create flags where only A is unassigned.
+        flags = FlagsDict()
+        flags.parse_and_merge_csv(['A,' + FLAG_SDK, 'B', 'C'])
+        flags.assign_flag(FLAG_UNSUPPORTED, set(['C']))
+        self.assertEqual(flags.generate_csv(),
+            [ 'A,' + FLAG_SDK, 'B', 'C,' + FLAG_UNSUPPORTED ])
+
+        # Check three things:
+        # (1) B is selected as valid unassigned
+        # (2) A is not selected because it is assigned to the allow list
+        # (3) D is not selected because it is not a valid key
+        self.assertEqual(
+            flags.get_valid_subset_of_unassigned_apis(set(['A', 'B', 'D'])), set([ 'B' ]))
+
+    def test_parse_and_merge_csv(self):
+        flags = FlagsDict()
+
+        # Test empty CSV entry.
+        self.assertEqual(flags.generate_csv(), [])
+
+        # Test new additions.
+        flags.parse_and_merge_csv([
+            'A,' + FLAG_UNSUPPORTED,
+            'B,' + FLAG_BLOCKED + ',' + FLAG_MAX_TARGET_O,
+            'C,' + FLAG_SDK + ',' + FLAG_SYSTEM_API,
+            'D,' + FLAG_UNSUPPORTED + ',' + FLAG_TEST_API,
+            'E,' + FLAG_BLOCKED + ',' + FLAG_TEST_API,
+        ])
+        self.assertEqual(flags.generate_csv(), [
+            'A,' + FLAG_UNSUPPORTED,
+            'B,' + FLAG_BLOCKED + "," + FLAG_MAX_TARGET_O,
+            'C,' + FLAG_SDK + ',' + FLAG_SYSTEM_API,
+            'D,' + FLAG_TEST_API + ',' + FLAG_UNSUPPORTED,
+            'E,' + FLAG_BLOCKED + ',' + FLAG_TEST_API,
+        ])
+
+        # Test unknown flag.
+        with self.assertRaises(AssertionError):
+            flags.parse_and_merge_csv([ 'Z,foo' ])
+
+    def test_assign_flag(self):
+        flags = FlagsDict()
+        flags.parse_and_merge_csv(['A,' + FLAG_SDK, 'B'])
+
+        # Test new additions.
+        flags.assign_flag(FLAG_UNSUPPORTED, set([ 'A', 'B' ]))
+        self.assertEqual(flags.generate_csv(),
+            [ 'A,' + FLAG_SDK + "," + FLAG_UNSUPPORTED, 'B,' + FLAG_UNSUPPORTED ])
+
+        # Test invalid API signature.
+        with self.assertRaises(AssertionError):
+            flags.assign_flag(FLAG_SDK, set([ 'C' ]))
+
+        # Test invalid flag.
+        with self.assertRaises(AssertionError):
+            flags.assign_flag('foo', set([ 'A' ]))
+
+    def test_extract_package(self):
+        signature = 'Lcom/foo/bar/Baz;->method1()Lcom/bar/Baz;'
+        expected_package = 'com.foo.bar'
+        self.assertEqual(extract_package(signature), expected_package)
+
+        signature = 'Lcom/foo1/bar/MyClass;->method2()V'
+        expected_package = 'com.foo1.bar'
+        self.assertEqual(extract_package(signature), expected_package)
+
+        signature = 'Lcom/foo_bar/baz/MyClass;->method3()V'
+        expected_package = 'com.foo_bar.baz'
+        self.assertEqual(extract_package(signature), expected_package)
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/scripts/hiddenapi/merge_csv.py b/scripts/hiddenapi/merge_csv.py
new file mode 100755
index 0000000..6a5b0e1
--- /dev/null
+++ b/scripts/hiddenapi/merge_csv.py
@@ -0,0 +1,69 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# 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.
+"""
+Merge multiple CSV files, possibly with different columns.
+"""
+
+import argparse
+import csv
+import io
+
+from zipfile import ZipFile
+
+args_parser = argparse.ArgumentParser(description='Merge given CSV files into a single one.')
+args_parser.add_argument('--header', help='Comma separated field names; '
+                                          'if missing determines the header from input files.')
+args_parser.add_argument('--zip_input', help='ZIP archive with all CSV files to merge.')
+args_parser.add_argument('--output', help='Output file for merged CSV.',
+                         default='-', type=argparse.FileType('w'))
+args_parser.add_argument('files', nargs=argparse.REMAINDER)
+args = args_parser.parse_args()
+
+
+def dict_reader(input):
+    return csv.DictReader(input, delimiter=',', quotechar='|')
+
+
+if args.zip_input and len(args.files) > 0:
+    raise ValueError('Expecting either a single ZIP with CSV files'
+                     ' or a list of CSV files as input; not both.')
+
+csv_readers = []
+if len(args.files) > 0:
+    for file in args.files:
+        csv_readers.append(dict_reader(open(file, 'r')))
+elif args.zip_input:
+    with ZipFile(args.zip_input) as zip:
+        for entry in zip.namelist():
+            if entry.endswith('.uau'):
+                csv_readers.append(dict_reader(io.TextIOWrapper(zip.open(entry, 'r'))))
+
+headers = set()
+if args.header:
+    fieldnames = args.header.split(',')
+else:
+    # Build union of all columns from source files:
+    for reader in csv_readers:
+        headers = headers.union(reader.fieldnames)
+    fieldnames = sorted(headers)
+
+# Concatenate all files to output:
+writer = csv.DictWriter(args.output, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL,
+                        dialect='unix', fieldnames=fieldnames)
+writer.writeheader()
+for reader in csv_readers:
+    for row in reader:
+        writer.writerow(row)
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index 488afd8..17a6ca9 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -503,7 +503,7 @@
     sdk_member_name: "myjavalib",
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
-    jars: ["java_boot_libs/snapshot/jars/are/invalid/myjavalib.jar"],
+    jars: ["java/myjavalib.jar"],
 }
 
 java_import {
@@ -511,7 +511,7 @@
     prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
-    jars: ["java_boot_libs/snapshot/jars/are/invalid/myjavalib.jar"],
+    jars: ["java/myjavalib.jar"],
 }
 
 module_exports_snapshot {
@@ -519,10 +519,9 @@
     visibility: ["//visibility:public"],
     java_boot_libs: ["myexports_myjavalib@current"],
 }
-
 `),
 		checkAllCopyRules(`
-.intermediates/myexports/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/myjavalib.jar
+.intermediates/myjavalib/android_common/withres/myjavalib.jar -> java/myjavalib.jar
 `),
 	)
 }
diff --git a/sdk/update.go b/sdk/update.go
index b5bc9f4..377aaae 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -653,9 +653,6 @@
 	filesToZip  android.Paths
 	zipsToMerge android.Paths
 
-	// The path to an empty file.
-	emptyFile android.WritablePath
-
 	prebuiltModules map[string]*bpModule
 	prebuiltOrder   []*bpModule
 
@@ -706,19 +703,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 {
diff --git a/sh/sh_binary.go b/sh/sh_binary.go
index 18749b5..54dfc24 100644
--- a/sh/sh_binary.go
+++ b/sh/sh_binary.go
@@ -24,6 +24,7 @@
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
+	"android/soong/bazel"
 	"android/soong/cc"
 	"android/soong/tradefed"
 )
@@ -43,6 +44,8 @@
 	android.RegisterModuleType("sh_binary_host", ShBinaryHostFactory)
 	android.RegisterModuleType("sh_test", ShTestFactory)
 	android.RegisterModuleType("sh_test_host", ShTestHostFactory)
+
+	android.RegisterBp2BuildMutator("sh_binary", ShBinaryBp2Build)
 }
 
 type shBinaryProperties struct {
@@ -81,6 +84,9 @@
 
 	// Make this module available when building for recovery.
 	Recovery_available *bool
+
+	// Properties for Bazel migration purposes.
+	bazel.Properties
 }
 
 type TestProperties struct {
@@ -154,9 +160,6 @@
 }
 
 func (s *ShBinary) DepsMutator(ctx android.BottomUpMutatorContext) {
-	if s.properties.Src == nil {
-		ctx.PropertyErrorf("src", "missing prebuilt source file")
-	}
 }
 
 func (s *ShBinary) OutputFile() android.OutputPath {
@@ -203,6 +206,10 @@
 }
 
 func (s *ShBinary) generateAndroidBuildActions(ctx android.ModuleContext) {
+	if s.properties.Src == nil {
+		ctx.PropertyErrorf("src", "missing prebuilt source file")
+	}
+
 	s.sourceFilePath = android.PathForModuleSrc(ctx, proptools.String(s.properties.Src))
 	filename := proptools.String(s.properties.Filename)
 	filenameFromSrc := proptools.Bool(s.properties.Filename_from_src)
@@ -275,7 +282,7 @@
 	ctx.AddFarVariationDependencies(ctx.Target().Variations(), shTestDataBinsTag, s.testProperties.Data_bins...)
 	ctx.AddFarVariationDependencies(append(ctx.Target().Variations(), sharedLibVariations...),
 		shTestDataLibsTag, s.testProperties.Data_libs...)
-	if ctx.Target().Os.Class == android.Host && len(ctx.Config().Targets[android.Android]) > 0 {
+	if (ctx.Target().Os.Class == android.Host || ctx.BazelConversionMode()) && len(ctx.Config().Targets[android.Android]) > 0 {
 		deviceVariations := ctx.Config().AndroidFirstDeviceTarget.Variations()
 		ctx.AddFarVariationDependencies(deviceVariations, shTestDataDeviceBinsTag, s.testProperties.Data_device_bins...)
 		ctx.AddFarVariationDependencies(append(deviceVariations, sharedLibVariations...),
@@ -460,4 +467,62 @@
 	return module
 }
 
+type bazelShBinaryAttributes struct {
+	Srcs bazel.LabelList
+	// Bazel also supports the attributes below, but (so far) these are not required for Bionic
+	// deps
+	// data
+	// args
+	// compatible_with
+	// deprecation
+	// distribs
+	// env
+	// exec_compatible_with
+	// exec_properties
+	// features
+	// licenses
+	// output_licenses
+	// restricted_to
+	// tags
+	// target_compatible_with
+	// testonly
+	// toolchains
+	// visibility
+}
+
+type bazelShBinary struct {
+	android.BazelTargetModuleBase
+	bazelShBinaryAttributes
+}
+
+func BazelShBinaryFactory() android.Module {
+	module := &bazelShBinary{}
+	module.AddProperties(&module.bazelShBinaryAttributes)
+	android.InitBazelTargetModule(module)
+	return module
+}
+
+func ShBinaryBp2Build(ctx android.TopDownMutatorContext) {
+	m, ok := ctx.Module().(*ShBinary)
+	if !ok || !m.properties.Bazel_module.Bp2build_available {
+		return
+	}
+
+	srcs := android.BazelLabelForModuleSrc(ctx, []string{*m.properties.Src})
+
+	attrs := &bazelShBinaryAttributes{
+		Srcs: srcs,
+	}
+
+	props := bazel.NewBazelTargetModuleProperties(m.Name(), "sh_binary", "")
+
+	ctx.CreateBazelTargetModule(BazelShBinaryFactory, props, attrs)
+}
+
+func (m *bazelShBinary) Name() string {
+	return m.BaseModuleName()
+}
+
+func (m *bazelShBinary) GenerateAndroidBuildActions(ctx android.ModuleContext) {}
+
 var Bool = proptools.Bool
diff --git a/zip/zip.go b/zip/zip.go
index cb85f5c..f731329 100644
--- a/zip/zip.go
+++ b/zip/zip.go
@@ -172,6 +172,9 @@
 
 	arg := b.state
 	arg.SourceFiles = ReadRespFile(list)
+	for i := range arg.SourceFiles {
+		arg.SourceFiles[i] = pathtools.MatchEscape(arg.SourceFiles[i])
+	}
 	b.fileArgs = append(b.fileArgs, arg)
 	return b
 }
diff --git a/zip/zip_test.go b/zip/zip_test.go
index a16e092..b456ef8 100644
--- a/zip/zip_test.go
+++ b/zip/zip_test.go
@@ -46,13 +46,14 @@
 	"dangling -> missing": nil,
 	"a/a/d -> b":          nil,
 	"c":                   fileC,
-	"l_nl":                []byte("a/a/a\na/a/b\nc\n"),
-	"l_sp":                []byte("a/a/a a/a/b c"),
+	"l_nl":                []byte("a/a/a\na/a/b\nc\n\\[\n"),
+	"l_sp":                []byte("a/a/a a/a/b c \\["),
 	"l2":                  []byte("missing\n"),
-	"rsp":                 []byte("'a/a/a'\na/a/b\n'@'\n'foo'\\''bar'"),
+	"rsp":                 []byte("'a/a/a'\na/a/b\n'@'\n'foo'\\''bar'\n'['"),
 	"@ -> c":              nil,
 	"foo'bar -> c":        nil,
 	"manifest.txt":        fileCustomManifest,
+	"[":                   fileEmpty,
 })
 
 func fh(name string, contents []byte, method uint16) zip.FileHeader {
@@ -127,13 +128,15 @@
 			args: fileArgsBuilder().
 				File("a/a/a").
 				File("a/a/b").
-				File("c"),
+				File("c").
+				File(`\[`),
 			compressionLevel: 9,
 
 			files: []zip.FileHeader{
 				fh("a/a/a", fileA, zip.Deflate),
 				fh("a/a/b", fileB, zip.Deflate),
 				fh("c", fileC, zip.Deflate),
+				fh("[", fileEmpty, zip.Store),
 			},
 		},
 		{
@@ -235,6 +238,7 @@
 				fh("a/a/a", fileA, zip.Deflate),
 				fh("a/a/b", fileB, zip.Deflate),
 				fh("c", fileC, zip.Deflate),
+				fh("[", fileEmpty, zip.Store),
 			},
 		},
 		{
@@ -247,6 +251,7 @@
 				fh("a/a/a", fileA, zip.Deflate),
 				fh("a/a/b", fileB, zip.Deflate),
 				fh("c", fileC, zip.Deflate),
+				fh("[", fileEmpty, zip.Store),
 			},
 		},
 		{
@@ -260,6 +265,7 @@
 				fh("a/a/b", fileB, zip.Deflate),
 				fh("@", fileC, zip.Deflate),
 				fh("foo'bar", fileC, zip.Deflate),
+				fh("[", fileEmpty, zip.Store),
 			},
 		},
 		{