Merge "Add android_test_helper_app properties to java_defaults"
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index 4a25119..5ba6d62 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...)
diff --git a/android/filegroup.go b/android/filegroup.go
index b36238c..674a196 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -49,19 +49,15 @@
 
 func FilegroupBp2Build(ctx TopDownMutatorContext) {
 	fg, ok := ctx.Module().(*fileGroup)
-	if !ok {
+	if !ok || !fg.properties.Bazel_module.Bp2build_available {
 		return
 	}
+
 	attrs := &bazelFilegroupAttributes{
 		Srcs: BazelLabelForModuleSrcExcludes(ctx, fg.properties.Srcs, fg.properties.Exclude_srcs),
 	}
 
-	// Can we automate this?
-	name := "__bp2build__" + fg.Name()
-	props := bazel.BazelTargetModuleProperties{
-		Name:       &name,
-		Rule_class: "filegroup",
-	}
+	props := bazel.NewBazelTargetModuleProperties(fg.Name(), "filegroup", "")
 
 	ctx.CreateBazelTargetModule(BazelFileGroupFactory, props, attrs)
 }
diff --git a/android/mutator.go b/android/mutator.go
index 91753d1..c387193 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -16,7 +16,9 @@
 
 import (
 	"android/soong/bazel"
+	"fmt"
 	"reflect"
+	"strings"
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
@@ -513,6 +515,14 @@
 	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)
 }
 
diff --git a/android/variable.go b/android/variable.go
index 9b3ed17..799369d 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"`
 
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 9f8cd06..83eb56a 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -4495,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 {
@@ -4519,6 +4525,11 @@
 
 		ctx := testDexpreoptWithApexes(t, bp, "", transform)
 		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) {
@@ -4588,6 +4599,11 @@
 
 		ctx := testDexpreoptWithApexes(t, bp, "", transform)
 		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) {
@@ -4631,7 +4647,12 @@
 	`
 
 		ctx := testDexpreoptWithApexes(t, bp, "", transform)
-		checkBootDexJarPath(t, 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) {
@@ -4678,6 +4699,11 @@
 
 		ctx := testDexpreoptWithApexes(t, bp, "", transform)
 		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
+`)
 	})
 }
 
@@ -6304,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)
@@ -6315,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/prebuilt.go b/apex/prebuilt.go
index 3149952..041afb3 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -260,12 +260,36 @@
 	}
 }
 
+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)
 	}
 }
 
@@ -305,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 fbbbfe6..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.
@@ -38,6 +46,23 @@
 	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 {
diff --git a/bp2build/Android.bp b/bp2build/Android.bp
index 2bbe4b5..54fc93f 100644
--- a/bp2build/Android.bp
+++ b/bp2build/Android.bp
@@ -12,6 +12,7 @@
         "soong-android",
         "soong-bazel",
         "soong-genrule",
+        "soong-sh",
     ],
     testSrcs: [
         "build_conversion_test.go",
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index 59af22d..7ffcfa4 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -219,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)
@@ -467,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 081b0e5..df554a0 100644
--- a/bp2build/build_conversion_test.go
+++ b/bp2build/build_conversion_test.go
@@ -17,6 +17,7 @@
 import (
 	"android/soong/android"
 	"android/soong/genrule"
+	"android/soong/sh"
 	"strings"
 	"testing"
 )
@@ -228,6 +229,7 @@
 	name: "foo",
     string_list_prop: ["a", "b"],
     string_prop: "a",
+    bazel_module: { bp2build_available: true },
 }`,
 			expectedBazelTarget: `custom(
     name = "foo",
@@ -356,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")`,
@@ -382,6 +390,7 @@
 		{
 			bp: `custom {
     name: "bar",
+    bazel_module: { bp2build_available: true  },
 }`,
 			expectedBazelTarget: `my_library(
     name = "bar",
@@ -474,6 +483,7 @@
 			bp: `filegroup {
     name: "fg_foo",
     srcs: [],
+    bazel_module: { bp2build_available: true },
 }`,
 			expectedBazelTargets: []string{
 				`filegroup(
@@ -491,6 +501,7 @@
 			bp: `filegroup {
     name: "fg_foo",
     srcs: ["a", "b"],
+    bazel_module: { bp2build_available: true },
 }`,
 			expectedBazelTargets: []string{`filegroup(
     name = "fg_foo",
@@ -510,6 +521,7 @@
     name: "fg_foo",
     srcs: ["a", "b"],
     exclude_srcs: ["a"],
+    bazel_module: { bp2build_available: true },
 }`,
 			expectedBazelTargets: []string{`filegroup(
     name = "fg_foo",
@@ -527,6 +539,7 @@
 			bp: `filegroup {
     name: "foo",
     srcs: ["**/*.txt"],
+    bazel_module: { bp2build_available: true },
 }`,
 			expectedBazelTargets: []string{`filegroup(
     name = "foo",
@@ -552,6 +565,7 @@
 			bp: `filegroup {
     name: "foo",
     srcs: ["a.txt"],
+    bazel_module: { bp2build_available: true },
 }`,
 			dir: "other",
 			expectedBazelTargets: []string{`filegroup(
@@ -567,6 +581,7 @@
 				"other/Android.bp": `filegroup {
     name: "fg_foo",
     srcs: ["**/*.txt"],
+    bazel_module: { bp2build_available: true },
 }`,
 				"other/a.txt":        "",
 				"other/b.txt":        "",
@@ -585,6 +600,7 @@
       ":foo",
         "c",
     ],
+    bazel_module: { bp2build_available: true },
 }`,
 			expectedBazelTargets: []string{`filegroup(
     name = "foobar",
@@ -612,6 +628,7 @@
     out: ["foo_tool.out"],
     srcs: ["foo_tool.in"],
     cmd: "cp $(in) $(out)",
+    bazel_module: { bp2build_available: true },
 }
 
 genrule {
@@ -620,6 +637,7 @@
     srcs: ["foo.in"],
     tools: [":foo.tool"],
     cmd: "$(location :foo.tool) --genDir=$(genDir) arg $(in) $(out)",
+    bazel_module: { bp2build_available: true },
 }`,
 			expectedBazelTargets: []string{
 				`genrule(
@@ -658,7 +676,8 @@
     out: ["foo_tool.out", "foo_tool2.out"],
     srcs: ["foo_tool.in"],
     cmd: "cp $(in) $(out)",
-   }
+    bazel_module: { bp2build_available: true },
+}
 
 genrule {
     name: "foo",
@@ -666,6 +685,7 @@
     srcs: ["foo.in"],
     tools: [":foo.tools"],
     cmd: "$(locations :foo.tools) -s $(out) $(in)",
+    bazel_module: { bp2build_available: true },
 }`,
 			expectedBazelTargets: []string{`genrule(
     name = "foo",
@@ -705,6 +725,7 @@
     srcs: ["foo.in"],
     tool_files: [":foo.tool"],
     cmd: "$(locations :foo.tool) -s $(out) $(in)",
+    bazel_module: { bp2build_available: true },
 }`,
 			expectedBazelTargets: []string{`genrule(
     name = "foo",
@@ -734,6 +755,7 @@
     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",
@@ -763,6 +785,7 @@
     srcs: ["foo.in"],
     tool_files: [":foo.tool", ":other.tool"],
     cmd: "$(location) -s $(out) $(in)",
+    bazel_module: { bp2build_available: true },
 }`,
 			expectedBazelTargets: []string{`genrule(
     name = "foo",
@@ -793,6 +816,7 @@
     srcs: ["foo.in"],
     tools: [":foo.tool", ":other.tool"],
     cmd: "$(locations) -s $(out) $(in)",
+    bazel_module: { bp2build_available: true },
 }`,
 			expectedBazelTargets: []string{`genrule(
     name = "foo",
@@ -822,6 +846,7 @@
     out: ["foo.out"],
     srcs: ["foo.in"],
     cmd: "cp $(in) $(out)",
+    bazel_module: { bp2build_available: true },
 }`,
 			expectedBazelTargets: []string{`genrule(
     name = "foo",
@@ -835,6 +860,23 @@
 )`,
 			},
 		},
+		{
+			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 := "."
@@ -927,6 +969,7 @@
     out: ["out"],
     srcs: ["in1"],
     defaults: ["gen_defaults"],
+    bazel_module: { bp2build_available: true },
 }
 `,
 			expectedBazelTarget: `genrule(
@@ -961,6 +1004,7 @@
     srcs: ["in1"],
     defaults: ["gen_defaults"],
     cmd: "do-something $(in) $(out)",
+    bazel_module: { bp2build_available: true },
 }
 `,
 			expectedBazelTarget: `genrule(
@@ -999,6 +1043,7 @@
     name: "gen",
     out: ["out"],
     defaults: ["gen_defaults1", "gen_defaults2"],
+    bazel_module: { bp2build_available: true },
 }
 `,
 			expectedBazelTarget: `genrule(
@@ -1045,6 +1090,7 @@
     name: "gen",
     out: ["out"],
     defaults: ["gen_defaults1"],
+    bazel_module: { bp2build_available: true },
 }
 `,
 			expectedBazelTarget: `genrule(
@@ -1097,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 b5f5448..2e59999 100644
--- a/bp2build/testing.go
+++ b/bp2build/testing.go
@@ -27,6 +27,8 @@
 	android.ModuleBase
 
 	props customProps
+
+	bazelProps bazel.Properties
 }
 
 // OutputFiles is needed because some instances of this module use dist with a
@@ -42,6 +44,7 @@
 func customModuleFactoryBase() android.Module {
 	module := &customModule{}
 	module.AddProperties(&module.props)
+	module.AddProperties(&module.bazelProps)
 	return module
 }
 
@@ -124,16 +127,16 @@
 
 func customBp2BuildMutator(ctx android.TopDownMutatorContext) {
 	if m, ok := ctx.Module().(*customModule); ok {
+		if !m.bazelProps.Bazel_module.Bp2build_available {
+			return
+		}
+
 		attrs := &customBazelModuleAttributes{
 			String_prop:      m.props.String_prop,
 			String_list_prop: m.props.String_list_prop,
 		}
 
-		name := "__bp2build__" + m.Name()
-		props := bazel.BazelTargetModuleProperties{
-			Name:       &name,
-			Rule_class: "custom",
-		}
+		props := bazel.NewBazelTargetModuleProperties(m.Name(), "custom", "")
 
 		ctx.CreateBazelTargetModule(customBazelModuleFactory, props, attrs)
 	}
@@ -143,31 +146,32 @@
 // module to target.
 func customBp2BuildMutatorFromStarlark(ctx android.TopDownMutatorContext) {
 	if m, ok := ctx.Module().(*customModule); ok {
+		if !m.bazelProps.Bazel_module.Bp2build_available {
+			return
+		}
+
 		baseName := m.Name()
 		attrs := &customBazelModuleAttributes{}
 
-		myLibraryName := "__bp2build__" + baseName
-		myLibraryProps := bazel.BazelTargetModuleProperties{
-			Name:              &myLibraryName,
-			Rule_class:        "my_library",
-			Bzl_load_location: "//build/bazel/rules:rules.bzl",
-		}
+		myLibraryProps := bazel.NewBazelTargetModuleProperties(
+			baseName,
+			"my_library",
+			"//build/bazel/rules:rules.bzl",
+		)
 		ctx.CreateBazelTargetModule(customBazelModuleFactory, myLibraryProps, attrs)
 
-		protoLibraryName := "__bp2build__" + baseName + "_proto_library_deps"
-		protoLibraryProps := bazel.BazelTargetModuleProperties{
-			Name:              &protoLibraryName,
-			Rule_class:        "proto_library",
-			Bzl_load_location: "//build/bazel/rules:proto.bzl",
-		}
+		protoLibraryProps := bazel.NewBazelTargetModuleProperties(
+			baseName+"_proto_library_deps",
+			"proto_library",
+			"//build/bazel/rules:proto.bzl",
+		)
 		ctx.CreateBazelTargetModule(customBazelModuleFactory, protoLibraryProps, attrs)
 
-		myProtoLibraryName := "__bp2build__" + baseName + "_my_proto_library_deps"
-		myProtoLibraryProps := bazel.BazelTargetModuleProperties{
-			Name:              &myProtoLibraryName,
-			Rule_class:        "my_proto_library",
-			Bzl_load_location: "//build/bazel/rules:proto.bzl",
-		}
+		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/cc.go b/cc/cc.go
index df38499..7f59158 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -1544,7 +1544,7 @@
 		nameSuffix = productSuffix
 	} else {
 		vndkVersion = ctx.DeviceConfig().VndkVersion()
-		nameSuffix = vendorSuffix
+		nameSuffix = VendorSuffix
 	}
 	if vndkVersion == "current" {
 		vndkVersion = ctx.DeviceConfig().PlatformVndkVersion()
@@ -1591,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()) {
@@ -2927,7 +2927,7 @@
 	} 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/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/snapshot_prebuilt.go b/cc/snapshot_prebuilt.go
index ffaed8e..b82628c 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) {
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/genrule/genrule.go b/genrule/genrule.go
index 427c995..9fa6c48 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -800,7 +800,7 @@
 
 func GenruleBp2Build(ctx android.TopDownMutatorContext) {
 	m, ok := ctx.Module().(*Module)
-	if !ok {
+	if !ok || !m.properties.Bazel_module.Bp2build_available {
 		return
 	}
 
@@ -853,12 +853,7 @@
 		Tools: tools,
 	}
 
-	// Can we automate this?
-	name := "__bp2build__" + m.Name()
-	props := bazel.BazelTargetModuleProperties{
-		Name:       &name,
-		Rule_class: "genrule",
-	}
+	props := bazel.NewBazelTargetModuleProperties(m.Name(), "genrule", "")
 
 	// Create the BazelTargetModule.
 	ctx.CreateBazelTargetModule(BazelGenruleFactory, props, attrs)
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/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/hiddenapi.go b/java/hiddenapi.go
index eafbf5d..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.OutputPath,
+// 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")
+
+	// 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 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")
+}
+
+// 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 !ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") {
 
-		// 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 !h.active {
+		return dexJar
+	}
 
-		// 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)
+	h.hiddenAPIExtractInformation(ctx, dexJar, implementationJar, primary)
 
-			// 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").OutputPath
+	if !h.annotationsOnly {
+		hiddenAPIJar := android.PathForModuleOut(ctx, "hiddenapi", name+".jar").OutputPath
 
-				// 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
-			}
-		}
+		// 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 46994c3..568d15f 100644
--- a/java/hiddenapi_singleton.go
+++ b/java/hiddenapi_singleton.go
@@ -372,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 58f1b1d..77cfff4 100644
--- a/java/hiddenapi_singleton_test.go
+++ b/java/hiddenapi_singleton_test.go
@@ -76,14 +76,6 @@
 	}
 }
 
-func checkRuleInputs(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)
-	}
-}
-
 func TestHiddenAPIIndexSingleton(t *testing.T) {
 	ctx, _ := testHiddenAPIBootJars(t, `
 		java_library {
@@ -108,11 +100,9 @@
 
 	hiddenAPIIndex := ctx.SingletonForTests("hiddenapi_index")
 	indexRule := hiddenAPIIndex.Rule("singleton-merged-hiddenapi-index")
-	checkRuleInputs(t, `
-.intermediates/bar.impl/android_common/hiddenapi/index.csv
+	CheckHiddenAPIRuleInputs(t, `
 .intermediates/bar/android_common/hiddenapi/index.csv
 .intermediates/foo/android_common/hiddenapi/index.csv
-.intermediates/prebuilt_foo/android_common/hiddenapi/index.csv
 `,
 		indexRule)
 }
diff --git a/java/java.go b/java/java.go
index 1b01f38..8cf4ee5 100644
--- a/java/java.go
+++ b/java/java.go
@@ -1805,7 +1805,7 @@
 		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
@@ -2090,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())
 }
@@ -2149,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
@@ -2849,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
 	}
@@ -2904,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)
@@ -2917,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())
@@ -2948,11 +2966,8 @@
 				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
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/rust/image.go b/rust/image.go
index ac8c1b3..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
@@ -86,7 +90,9 @@
 
 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)
 
@@ -117,6 +123,8 @@
 	}
 
 	coreVariantNeeded := true
+	vendorRamdiskVariantNeeded := false
+
 	var vendorVariants []string
 
 	if mod.HasVendorVariant() {
@@ -138,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
@@ -155,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/rust.go b/rust/rust.go
index 0b733cc..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
@@ -658,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() {
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/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/sh/sh_binary.go b/sh/sh_binary.go
index 66e493b..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 {
@@ -461,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