Merge "Add ndk api parser for ndk api coverage."
diff --git a/Android.bp b/Android.bp
index ec7d13a..0b44198 100644
--- a/Android.bp
+++ b/Android.bp
@@ -11,14 +11,6 @@
 ]
 
 bootstrap_go_package {
-    name: "soong-env",
-    pkgPath: "android/soong/env",
-    srcs: [
-        "env/env.go",
-    ],
-}
-
-bootstrap_go_package {
     name: "soong",
     pkgPath: "android/soong",
     deps: [
@@ -29,545 +21,6 @@
     ],
 }
 
-bootstrap_go_package {
-    name: "soong-android",
-    pkgPath: "android/soong/android",
-    deps: [
-        "blueprint",
-        "blueprint-bootstrap",
-        "soong",
-        "soong-android-soongconfig",
-        "soong-env",
-        "soong-shared",
-        "soong-ui-metrics_proto",
-    ],
-    srcs: [
-        "android/androidmk.go",
-        "android/apex.go",
-        "android/api_levels.go",
-        "android/arch.go",
-        "android/config.go",
-        "android/csuite_config.go",
-        "android/defaults.go",
-        "android/defs.go",
-        "android/expand.go",
-        "android/filegroup.go",
-        "android/hooks.go",
-        "android/image.go",
-        "android/makevars.go",
-        "android/metrics.go",
-        "android/module.go",
-        "android/mutator.go",
-        "android/namespace.go",
-        "android/neverallow.go",
-        "android/notices.go",
-        "android/onceper.go",
-        "android/override_module.go",
-        "android/package.go",
-        "android/package_ctx.go",
-        "android/path_properties.go",
-        "android/paths.go",
-        "android/prebuilt.go",
-        "android/prebuilt_etc.go",
-        "android/proto.go",
-        "android/register.go",
-        "android/rule_builder.go",
-        "android/sandbox.go",
-        "android/sdk.go",
-        "android/sh_binary.go",
-        "android/singleton.go",
-        "android/soong_config_modules.go",
-        "android/testing.go",
-        "android/util.go",
-        "android/variable.go",
-        "android/visibility.go",
-        "android/vts_config.go",
-        "android/writedocs.go",
-
-        // Lock down environment access last
-        "android/env.go",
-    ],
-    testSrcs: [
-        "android/android_test.go",
-        "android/androidmk_test.go",
-        "android/arch_test.go",
-        "android/config_test.go",
-        "android/csuite_config_test.go",
-        "android/expand_test.go",
-        "android/module_test.go",
-        "android/mutator_test.go",
-        "android/namespace_test.go",
-        "android/neverallow_test.go",
-        "android/onceper_test.go",
-        "android/package_test.go",
-        "android/path_properties_test.go",
-        "android/paths_test.go",
-        "android/prebuilt_test.go",
-        "android/prebuilt_etc_test.go",
-        "android/rule_builder_test.go",
-        "android/soong_config_modules_test.go",
-        "android/util_test.go",
-        "android/variable_test.go",
-        "android/visibility_test.go",
-        "android/vts_config_test.go",
-    ],
-}
-
-bootstrap_go_package {
-    name: "soong-android-soongconfig",
-    pkgPath: "android/soong/android/soongconfig",
-    deps: [
-        "blueprint",
-        "blueprint-parser",
-        "blueprint-proptools",
-    ],
-    srcs: [
-        "android/soongconfig/config.go",
-        "android/soongconfig/modules.go",
-    ],
-}
-
-bootstrap_go_package {
-    name: "soong-cc-config",
-    pkgPath: "android/soong/cc/config",
-    deps: [
-        "soong-android",
-        "soong-remoteexec",
-    ],
-    srcs: [
-        "cc/config/clang.go",
-        "cc/config/global.go",
-        "cc/config/tidy.go",
-        "cc/config/toolchain.go",
-        "cc/config/vndk.go",
-
-        "cc/config/arm_device.go",
-        "cc/config/arm64_device.go",
-        "cc/config/arm64_fuchsia_device.go",
-        "cc/config/x86_device.go",
-        "cc/config/x86_64_device.go",
-        "cc/config/x86_64_fuchsia_device.go",
-
-        "cc/config/x86_darwin_host.go",
-        "cc/config/x86_linux_host.go",
-        "cc/config/x86_linux_bionic_host.go",
-        "cc/config/x86_windows_host.go",
-    ],
-    testSrcs: [
-        "cc/config/tidy_test.go",
-    ],
-}
-
-bootstrap_go_package {
-    name: "soong-cc",
-    pkgPath: "android/soong/cc",
-    deps: [
-        "blueprint",
-        "blueprint-pathtools",
-        "soong",
-        "soong-android",
-        "soong-cc-config",
-        "soong-genrule",
-        "soong-tradefed",
-    ],
-    srcs: [
-        "cc/androidmk.go",
-        "cc/builder.go",
-        "cc/cc.go",
-        "cc/ccdeps.go",
-        "cc/check.go",
-        "cc/coverage.go",
-        "cc/gen.go",
-        "cc/linkable.go",
-        "cc/lto.go",
-        "cc/makevars.go",
-        "cc/pgo.go",
-        "cc/prebuilt.go",
-        "cc/proto.go",
-        "cc/rs.go",
-        "cc/sanitize.go",
-        "cc/sabi.go",
-        "cc/sdk.go",
-        "cc/snapshot_utils.go",
-        "cc/stl.go",
-        "cc/strip.go",
-        "cc/sysprop.go",
-        "cc/tidy.go",
-        "cc/util.go",
-        "cc/vendor_snapshot.go",
-        "cc/vndk.go",
-        "cc/vndk_prebuilt.go",
-
-        "cc/cflag_artifacts.go",
-        "cc/cmakelists.go",
-        "cc/compdb.go",
-        "cc/compiler.go",
-        "cc/installer.go",
-        "cc/linker.go",
-
-        "cc/binary.go",
-        "cc/binary_sdk_member.go",
-        "cc/fuzz.go",
-        "cc/library.go",
-        "cc/library_headers.go",
-        "cc/library_sdk_member.go",
-        "cc/object.go",
-        "cc/test.go",
-        "cc/toolchain_library.go",
-
-        "cc/ndk_prebuilt.go",
-        "cc/ndk_headers.go",
-        "cc/ndk_library.go",
-        "cc/ndk_sysroot.go",
-
-        "cc/llndk_library.go",
-
-        "cc/kernel_headers.go",
-
-        "cc/genrule.go",
-
-        "cc/vendor_public_library.go",
-
-        "cc/testing.go",
-    ],
-    testSrcs: [
-        "cc/cc_test.go",
-        "cc/compiler_test.go",
-        "cc/gen_test.go",
-        "cc/genrule_test.go",
-        "cc/library_headers_test.go",
-        "cc/library_test.go",
-        "cc/object_test.go",
-        "cc/prebuilt_test.go",
-        "cc/proto_test.go",
-        "cc/test_data_test.go",
-    ],
-    pluginFor: ["soong_build"],
-}
-
-bootstrap_go_package {
-    name: "soong-genrule",
-    pkgPath: "android/soong/genrule",
-    deps: [
-        "blueprint",
-        "blueprint-pathtools",
-        "soong",
-        "soong-android",
-        "soong-shared",
-    ],
-    srcs: [
-        "genrule/genrule.go",
-    ],
-    testSrcs: [
-        "genrule/genrule_test.go",
-    ],
-    pluginFor: ["soong_build"],
-}
-
-bootstrap_go_package {
-    name: "soong-phony",
-    pkgPath: "android/soong/phony",
-    deps: [
-        "blueprint",
-        "soong-android",
-    ],
-    srcs: [
-        "phony/phony.go",
-    ],
-    pluginFor: ["soong_build"],
-}
-
-bootstrap_go_package {
-    name: "soong-java",
-    pkgPath: "android/soong/java",
-    deps: [
-        "blueprint",
-        "blueprint-pathtools",
-        "soong",
-        "soong-android",
-        "soong-cc",
-        "soong-dexpreopt",
-        "soong-genrule",
-        "soong-java-config",
-        "soong-tradefed",
-    ],
-    srcs: [
-        "java/aapt2.go",
-        "java/aar.go",
-        "java/android_manifest.go",
-        "java/android_resources.go",
-        "java/androidmk.go",
-        "java/app_builder.go",
-        "java/app.go",
-        "java/builder.go",
-        "java/device_host_converter.go",
-        "java/dex.go",
-        "java/dexpreopt.go",
-        "java/dexpreopt_bootjars.go",
-        "java/dexpreopt_config.go",
-        "java/droiddoc.go",
-        "java/gen.go",
-        "java/genrule.go",
-        "java/hiddenapi.go",
-        "java/hiddenapi_singleton.go",
-        "java/jacoco.go",
-        "java/java.go",
-        "java/jdeps.go",
-        "java/java_resources.go",
-        "java/kotlin.go",
-        "java/platform_compat_config.go",
-        "java/plugin.go",
-        "java/prebuilt_apis.go",
-        "java/proto.go",
-        "java/robolectric.go",
-        "java/sdk.go",
-        "java/sdk_library.go",
-        "java/support_libraries.go",
-        "java/sysprop.go",
-        "java/system_modules.go",
-        "java/testing.go",
-        "java/tradefed.go",
-    ],
-    testSrcs: [
-        "java/androidmk_test.go",
-        "java/app_test.go",
-        "java/device_host_converter_test.go",
-        "java/dexpreopt_test.go",
-        "java/dexpreopt_bootjars_test.go",
-        "java/java_test.go",
-        "java/jdeps_test.go",
-        "java/kotlin_test.go",
-        "java/plugin_test.go",
-        "java/sdk_test.go",
-    ],
-    pluginFor: ["soong_build"],
-}
-
-bootstrap_go_package {
-    name: "soong-java-config",
-    pkgPath: "android/soong/java/config",
-    deps: [
-        "blueprint-proptools",
-        "soong-android",
-        "soong-remoteexec",
-    ],
-    srcs: [
-        "java/config/config.go",
-        "java/config/error_prone.go",
-        "java/config/kotlin.go",
-        "java/config/makevars.go",
-    ],
-}
-
-bootstrap_go_package {
-    name: "soong-rust-config",
-    pkgPath: "android/soong/rust/config",
-    deps: [
-        "soong-android",
-        "soong-cc-config",
-    ],
-    srcs: [
-        "rust/config/arm_device.go",
-        "rust/config/arm64_device.go",
-        "rust/config/global.go",
-        "rust/config/toolchain.go",
-        "rust/config/whitelist.go",
-        "rust/config/x86_darwin_host.go",
-        "rust/config/x86_linux_host.go",
-        "rust/config/x86_device.go",
-        "rust/config/x86_64_device.go",
-    ],
-}
-
-bootstrap_go_package {
-    name: "soong-rust",
-    pkgPath: "android/soong/rust",
-    deps: [
-        "soong",
-        "soong-android",
-        "soong-cc",
-        "soong-rust-config",
-    ],
-    srcs: [
-        "rust/androidmk.go",
-        "rust/compiler.go",
-        "rust/coverage.go",
-        "rust/binary.go",
-        "rust/builder.go",
-        "rust/library.go",
-        "rust/prebuilt.go",
-        "rust/proc_macro.go",
-        "rust/rust.go",
-        "rust/test.go",
-        "rust/testing.go",
-    ],
-    testSrcs: [
-        "rust/binary_test.go",
-        "rust/compiler_test.go",
-        "rust/coverage_test.go",
-        "rust/library_test.go",
-        "rust/rust_test.go",
-        "rust/test_test.go",
-    ],
-    pluginFor: ["soong_build"],
-}
-
-bootstrap_go_package {
-    name: "soong-python",
-    pkgPath: "android/soong/python",
-    deps: [
-        "blueprint",
-        "soong-android",
-        "soong-tradefed",
-    ],
-    srcs: [
-        "python/androidmk.go",
-        "python/binary.go",
-        "python/builder.go",
-        "python/defaults.go",
-        "python/installer.go",
-        "python/library.go",
-        "python/proto.go",
-        "python/python.go",
-        "python/test.go",
-    ],
-    testSrcs: [
-        "python/python_test.go",
-    ],
-    pluginFor: ["soong_build"],
-}
-
-bootstrap_go_package {
-    name: "soong-shared",
-    pkgPath: "android/soong/shared",
-    srcs: [
-        "shared/paths.go",
-    ],
-}
-
-bootstrap_go_package {
-    name: "soong-tradefed",
-    pkgPath: "android/soong/tradefed",
-    deps: [
-        "blueprint",
-        "soong-android",
-    ],
-    srcs: [
-        "tradefed/autogen.go",
-        "tradefed/config.go",
-        "tradefed/makevars.go",
-    ],
-    pluginFor: ["soong_build"],
-}
-
-bootstrap_go_package {
-    name: "soong-xml",
-    pkgPath: "android/soong/xml",
-    deps: [
-        "blueprint",
-        "blueprint-pathtools",
-        "soong",
-        "soong-android",
-    ],
-    srcs: [
-        "xml/xml.go",
-    ],
-    testSrcs: [
-        "xml/xml_test.go",
-    ],
-    pluginFor: ["soong_build"],
-}
-
-bootstrap_go_package {
-    name: "soong-apex",
-    pkgPath: "android/soong/apex",
-    deps: [
-        "blueprint",
-        "soong",
-        "soong-android",
-        "soong-cc",
-        "soong-java",
-        "soong-python",
-    ],
-    srcs: [
-        "apex/androidmk.go",
-        "apex/apex.go",
-        "apex/apex_singleton.go",
-        "apex/builder.go",
-        "apex/key.go",
-        "apex/prebuilt.go",
-        "apex/vndk.go",
-    ],
-    testSrcs: [
-        "apex/apex_test.go",
-        "apex/vndk_test.go",
-    ],
-    pluginFor: ["soong_build"],
-}
-
-bootstrap_go_package {
-    name: "soong-sysprop",
-    pkgPath: "android/soong/sysprop",
-    deps: [
-        "blueprint",
-        "soong",
-        "soong-android",
-        "soong-cc",
-        "soong-java",
-    ],
-    srcs: [
-        "sysprop/sysprop_library.go",
-    ],
-    testSrcs: [
-        "sysprop/sysprop_test.go",
-    ],
-    pluginFor: ["soong_build"],
-}
-
-bootstrap_go_package {
-    name: "soong-sdk",
-    pkgPath: "android/soong/sdk",
-    deps: [
-        "blueprint",
-        "soong",
-        "soong-android",
-        "soong-apex",
-        "soong-cc",
-        "soong-java",
-    ],
-    srcs: [
-        "sdk/bp.go",
-        "sdk/exports.go",
-        "sdk/sdk.go",
-        "sdk/update.go",
-    ],
-    testSrcs: [
-        "sdk/bp_test.go",
-        "sdk/cc_sdk_test.go",
-        "sdk/exports_test.go",
-        "sdk/java_sdk_test.go",
-        "sdk/sdk_test.go",
-        "sdk/testing.go",
-    ],
-    pluginFor: ["soong_build"],
-}
-
-bootstrap_go_package {
-    name: "soong-remoteexec",
-    pkgPath: "android/soong/remoteexec",
-    deps: [
-        "blueprint",
-        "soong-android",
-    ],
-    srcs: [
-        "remoteexec/remoteexec.go",
-    ],
-    testSrcs: [
-        "remoteexec/remoteexec_test.go",
-    ],
-    pluginFor: ["soong_build"],
-}
-
 //
 // Defaults to enable various configurations of host bionic
 //
diff --git a/OWNERS b/OWNERS
index 4cecb3d..dbb491d 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,4 +1,10 @@
-per-file * = asmundak@google.com,ccross@android.com,dwillemsen@google.com,jungjw@google.com,paulduffin@google.com
+per-file * = asmundak@google.com
+per-file * = ccross@android.com
+per-file * = dwillemsen@google.com
+per-file * = eakammer@google.com
+per-file * = jungjw@google.com
+per-file * = patricearruda@google.com
+per-file * = paulduffin@google.com
 
 per-file ndk_*.go, *gen_stub_libs.py = danalbert@google.com
 per-file clang.go,global.go = srhines@google.com, chh@google.com, pirama@google.com, yikong@google.com
diff --git a/android/Android.bp b/android/Android.bp
new file mode 100644
index 0000000..47dbc5d
--- /dev/null
+++ b/android/Android.bp
@@ -0,0 +1,80 @@
+bootstrap_go_package {
+    name: "soong-android",
+    pkgPath: "android/soong/android",
+    deps: [
+        "blueprint",
+        "blueprint-bootstrap",
+        "soong",
+        "soong-android-soongconfig",
+        "soong-env",
+        "soong-shared",
+        "soong-ui-metrics_proto",
+    ],
+    srcs: [
+        "androidmk.go",
+        "apex.go",
+        "api_levels.go",
+        "arch.go",
+        "config.go",
+        "csuite_config.go",
+        "defaults.go",
+        "defs.go",
+        "expand.go",
+        "filegroup.go",
+        "hooks.go",
+        "image.go",
+        "makevars.go",
+        "metrics.go",
+        "module.go",
+        "mutator.go",
+        "namespace.go",
+        "neverallow.go",
+        "notices.go",
+        "onceper.go",
+        "override_module.go",
+        "package.go",
+        "package_ctx.go",
+        "path_properties.go",
+        "paths.go",
+        "prebuilt.go",
+        "proto.go",
+        "register.go",
+        "rule_builder.go",
+        "sandbox.go",
+        "sdk.go",
+        "singleton.go",
+        "soong_config_modules.go",
+        "testing.go",
+        "util.go",
+        "variable.go",
+        "visibility.go",
+        "vts_config.go",
+        "writedocs.go",
+
+        // Lock down environment access last
+        "env.go",
+    ],
+    testSrcs: [
+        "android_test.go",
+        "androidmk_test.go",
+        "arch_test.go",
+        "config_test.go",
+        "csuite_config_test.go",
+        "expand_test.go",
+        "module_test.go",
+        "mutator_test.go",
+        "namespace_test.go",
+        "neverallow_test.go",
+        "onceper_test.go",
+        "package_test.go",
+        "path_properties_test.go",
+        "paths_test.go",
+        "prebuilt_test.go",
+        "rule_builder_test.go",
+        "soong_config_modules_test.go",
+        "util_test.go",
+        "variable_test.go",
+        "visibility_test.go",
+        "vts_config_test.go",
+    ],
+}
diff --git a/android/arch.go b/android/arch.go
index 08c0256..9a54614 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -810,7 +810,7 @@
 // Valid multilib values include:
 //    "both": compile for all Targets supported by the OsClass (generally x86_64 and x86, or arm64 and arm).
 //    "first": compile for only a single preferred Target supported by the OsClass.  This is generally x86_64 or arm64,
-//        but may be arm for a 32-bit only build or a build with TARGET_PREFER_32_BIT=true set.
+//        but may be arm for a 32-bit only build.
 //    "32": compile for only a single 32-bit Target supported by the OsClass.
 //    "64": compile for only a single 64-bit Target supported by the OsClass.
 //    "common": compile a for a single Target that will work on all Targets suported by the OsClass (for example Java).
@@ -1026,6 +1026,7 @@
 			"Not_windows",
 			"Arm_on_x86",
 			"Arm_on_x86_64",
+			"Native_bridge",
 		}
 		for _, os := range OsTypeList {
 			targets = append(targets, os.Field)
@@ -1413,6 +1414,11 @@
 					prefix := "target.arm_on_x86_64"
 					m.appendProperties(ctx, genProps, targetProp, field, prefix)
 				}
+				if os == Android && m.Target().NativeBridge == NativeBridgeEnabled {
+					field := "Native_bridge"
+					prefix := "target.native_bridge"
+					m.appendProperties(ctx, genProps, targetProp, field, prefix)
+				}
 			}
 		}
 	}
diff --git a/android/arch_test.go b/android/arch_test.go
index b987d56..8525b03 100644
--- a/android/arch_test.go
+++ b/android/arch_test.go
@@ -383,3 +383,88 @@
 		})
 	}
 }
+
+func TestArchMutatorNativeBridge(t *testing.T) {
+	bp := `
+		// This module is only enabled for x86.
+		module {
+			name: "foo",
+		}
+
+		// This module is enabled for x86 and arm (via native bridge).
+		module {
+			name: "bar",
+			native_bridge_supported: true,
+		}
+
+		// This module is enabled for arm (native_bridge) only.
+		module {
+			name: "baz",
+			native_bridge_supported: true,
+			enabled: false,
+			target: {
+				native_bridge: {
+					enabled: true,
+				}
+			}
+		}
+	`
+
+	testCases := []struct {
+		name        string
+		config      func(Config)
+		fooVariants []string
+		barVariants []string
+		bazVariants []string
+	}{
+		{
+			name:        "normal",
+			config:      nil,
+			fooVariants: []string{"android_x86_64_silvermont", "android_x86_silvermont"},
+			barVariants: []string{"android_x86_64_silvermont", "android_native_bridge_arm64_armv8-a", "android_x86_silvermont", "android_native_bridge_arm_armv7-a-neon"},
+			bazVariants: []string{"android_native_bridge_arm64_armv8-a", "android_native_bridge_arm_armv7-a-neon"},
+		},
+	}
+
+	enabledVariants := func(ctx *TestContext, name string) []string {
+		var ret []string
+		variants := ctx.ModuleVariantsForTests(name)
+		for _, variant := range variants {
+			m := ctx.ModuleForTests(name, variant)
+			if m.Module().Enabled() {
+				ret = append(ret, variant)
+			}
+		}
+		return ret
+	}
+
+	for _, tt := range testCases {
+		t.Run(tt.name, func(t *testing.T) {
+			config := TestArchConfigNativeBridge(buildDir, nil, bp, nil)
+
+			ctx := NewTestArchContext()
+			ctx.RegisterModuleType("module", archTestModuleFactory)
+			ctx.Register(config)
+			if tt.config != nil {
+				tt.config(config)
+			}
+
+			_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+			FailIfErrored(t, errs)
+			_, errs = ctx.PrepareBuildActions(config)
+			FailIfErrored(t, errs)
+
+			if g, w := enabledVariants(ctx, "foo"), tt.fooVariants; !reflect.DeepEqual(w, g) {
+				t.Errorf("want foo variants:\n%q\ngot:\n%q\n", w, g)
+			}
+
+			if g, w := enabledVariants(ctx, "bar"), tt.barVariants; !reflect.DeepEqual(w, g) {
+				t.Errorf("want bar variants:\n%q\ngot:\n%q\n", w, g)
+			}
+
+			if g, w := enabledVariants(ctx, "baz"), tt.bazVariants; !reflect.DeepEqual(w, g) {
+				t.Errorf("want qux variants:\n%q\ngot:\n%q\n", w, g)
+			}
+		})
+	}
+}
diff --git a/android/config.go b/android/config.go
index 59118ce..675660e 100644
--- a/android/config.go
+++ b/android/config.go
@@ -732,14 +732,6 @@
 	return Bool(c.productVariables.Eng)
 }
 
-func (c *config) DevicePrefer32BitApps() bool {
-	return Bool(c.productVariables.DevicePrefer32BitApps)
-}
-
-func (c *config) DevicePrefer32BitExecutables() bool {
-	return Bool(c.productVariables.DevicePrefer32BitExecutables)
-}
-
 func (c *config) DevicePrimaryArchType() ArchType {
 	return c.Targets[Android][0].Arch.ArchType
 }
diff --git a/android/module.go b/android/module.go
index 6239d27..82321f4 100644
--- a/android/module.go
+++ b/android/module.go
@@ -111,6 +111,8 @@
 	OtherModuleErrorf(m blueprint.Module, fmt string, args ...interface{})
 	OtherModuleDependencyTag(m blueprint.Module) blueprint.DependencyTag
 	OtherModuleExists(name string) bool
+	OtherModuleDependencyVariantExists(variations []blueprint.Variation, name string) bool
+	OtherModuleReverseDependencyVariantExists(name string) bool
 	OtherModuleType(m blueprint.Module) string
 
 	GetDirectDepsWithTag(tag blueprint.DependencyTag) []Module
@@ -1433,6 +1435,12 @@
 	return b.bp.OtherModuleDependencyTag(m)
 }
 func (b *baseModuleContext) OtherModuleExists(name string) bool { return b.bp.OtherModuleExists(name) }
+func (b *baseModuleContext) OtherModuleDependencyVariantExists(variations []blueprint.Variation, name string) bool {
+	return b.bp.OtherModuleDependencyVariantExists(variations, name)
+}
+func (b *baseModuleContext) OtherModuleReverseDependencyVariantExists(name string) bool {
+	return b.bp.OtherModuleReverseDependencyVariantExists(name)
+}
 func (b *baseModuleContext) OtherModuleType(m blueprint.Module) string {
 	return b.bp.OtherModuleType(m)
 }
diff --git a/android/neverallow.go b/android/neverallow.go
index 9e075b7..be3f712 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -187,6 +187,10 @@
 		// derive_sdk_prefer32 suppress the platform installation rules, but fails when
 		// the APEX modules contain the SDK variant and the platform variant still exists.
 		"frameworks/base/apex/sdkextensions/derive_sdk",
+		// These are for apps and shouldn't be used by non-SDK variant modules.
+		"prebuilts/ndk",
+		"tools/test/graphicsbenchmark/apps/sample_app",
+		"tools/test/graphicsbenchmark/functional_tests/java",
 	}
 
 	platformVariantPropertiesWhitelist := []string{
diff --git a/android/paths.go b/android/paths.go
index fcea65c..3ad27ac 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -485,6 +485,15 @@
 	return firstUniquePathsList(list)
 }
 
+// SortedUniquePaths returns what its name says
+func SortedUniquePaths(list Paths) Paths {
+	unique := FirstUniquePaths(list)
+	sort.Slice(unique, func(i, j int) bool {
+		return unique[i].String() < unique[j].String()
+	})
+	return unique
+}
+
 func firstUniquePathsList(list Paths) Paths {
 	k := 0
 outer:
diff --git a/android/prebuilt.go b/android/prebuilt.go
index ee4a13a..a29ec91 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -52,6 +52,9 @@
 
 	SourceExists bool `blueprint:"mutated"`
 	UsePrebuilt  bool `blueprint:"mutated"`
+
+	// Set if the module has been renamed to remove the "prebuilt_" prefix.
+	PrebuiltRenamedToSource bool `blueprint:"mutated"`
 }
 
 type Prebuilt struct {
@@ -188,25 +191,38 @@
 }
 
 func RegisterPrebuiltsPreArchMutators(ctx RegisterMutatorsContext) {
-	ctx.BottomUp("prebuilts", PrebuiltMutator).Parallel()
+	ctx.BottomUp("prebuilt_rename", PrebuiltRenameMutator).Parallel()
 }
 
 func RegisterPrebuiltsPostDepsMutators(ctx RegisterMutatorsContext) {
+	ctx.BottomUp("prebuilt_source", PrebuiltSourceDepsMutator).Parallel()
 	ctx.TopDown("prebuilt_select", PrebuiltSelectModuleMutator).Parallel()
 	ctx.BottomUp("prebuilt_postdeps", PrebuiltPostDepsMutator).Parallel()
 }
 
-// PrebuiltMutator ensures that there is always a module with an undecorated name, and marks
-// prebuilt modules that have both a prebuilt and a source module.
-func PrebuiltMutator(ctx BottomUpMutatorContext) {
+// PrebuiltRenameMutator ensures that there always is a module with an
+// undecorated name.
+func PrebuiltRenameMutator(ctx BottomUpMutatorContext) {
+	if m, ok := ctx.Module().(PrebuiltInterface); ok && m.Prebuilt() != nil {
+		name := m.base().BaseModuleName()
+		if !ctx.OtherModuleExists(name) {
+			ctx.Rename(name)
+			m.Prebuilt().properties.PrebuiltRenamedToSource = true
+		}
+	}
+}
+
+// PrebuiltSourceDepsMutator adds dependencies to the prebuilt module from the
+// corresponding source module, if one exists for the same variant.
+func PrebuiltSourceDepsMutator(ctx BottomUpMutatorContext) {
 	if m, ok := ctx.Module().(PrebuiltInterface); ok && m.Prebuilt() != nil {
 		p := m.Prebuilt()
-		name := m.base().BaseModuleName()
-		if ctx.OtherModuleExists(name) {
-			ctx.AddReverseDependency(ctx.Module(), PrebuiltDepTag, name)
-			p.properties.SourceExists = true
-		} else {
-			ctx.Rename(name)
+		if !p.properties.PrebuiltRenamedToSource {
+			name := m.base().BaseModuleName()
+			if ctx.OtherModuleReverseDependencyVariantExists(name) {
+				ctx.AddReverseDependency(ctx.Module(), PrebuiltDepTag, name)
+				p.properties.SourceExists = true
+			}
 		}
 	}
 }
diff --git a/android/prebuilt_etc.go b/android/prebuilt_etc.go
deleted file mode 100644
index 3dea6d8..0000000
--- a/android/prebuilt_etc.go
+++ /dev/null
@@ -1,279 +0,0 @@
-// Copyright 2016 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package android
-
-import "strconv"
-
-// TODO(jungw): Now that it handles more than the ones in etc/, consider renaming this file.
-
-func init() {
-	RegisterModuleType("prebuilt_etc", PrebuiltEtcFactory)
-	RegisterModuleType("prebuilt_etc_host", PrebuiltEtcHostFactory)
-	RegisterModuleType("prebuilt_usr_share", PrebuiltUserShareFactory)
-	RegisterModuleType("prebuilt_usr_share_host", PrebuiltUserShareHostFactory)
-	RegisterModuleType("prebuilt_font", PrebuiltFontFactory)
-	RegisterModuleType("prebuilt_firmware", PrebuiltFirmwareFactory)
-}
-
-type prebuiltEtcProperties struct {
-	// Source file of this prebuilt.
-	Src *string `android:"path,arch_variant"`
-
-	// optional subdirectory under which this file is installed into
-	Sub_dir *string `android:"arch_variant"`
-
-	// optional name for the installed file. If unspecified, name of the module is used as the file name
-	Filename *string `android:"arch_variant"`
-
-	// when set to true, and filename property is not set, the name for the installed file
-	// is the same as the file name of the source file.
-	Filename_from_src *bool `android:"arch_variant"`
-
-	// Make this module available when building for ramdisk.
-	Ramdisk_available *bool
-
-	// Make this module available when building for recovery.
-	Recovery_available *bool
-
-	// Whether this module is directly installable to one of the partitions. Default: true.
-	Installable *bool
-}
-
-type PrebuiltEtcModule interface {
-	Module
-	SubDir() string
-	OutputFile() OutputPath
-}
-
-type PrebuiltEtc struct {
-	ModuleBase
-
-	properties prebuiltEtcProperties
-
-	sourceFilePath Path
-	outputFilePath OutputPath
-	// The base install location, e.g. "etc" for prebuilt_etc, "usr/share" for prebuilt_usr_share.
-	installDirBase string
-	// The base install location when soc_specific property is set to true, e.g. "firmware" for prebuilt_firmware.
-	socInstallDirBase      string
-	installDirPath         InstallPath
-	additionalDependencies *Paths
-}
-
-func (p *PrebuiltEtc) inRamdisk() bool {
-	return p.ModuleBase.InRamdisk() || p.ModuleBase.InstallInRamdisk()
-}
-
-func (p *PrebuiltEtc) onlyInRamdisk() bool {
-	return p.ModuleBase.InstallInRamdisk()
-}
-
-func (p *PrebuiltEtc) InstallInRamdisk() bool {
-	return p.inRamdisk()
-}
-
-func (p *PrebuiltEtc) inRecovery() bool {
-	return p.ModuleBase.InRecovery() || p.ModuleBase.InstallInRecovery()
-}
-
-func (p *PrebuiltEtc) onlyInRecovery() bool {
-	return p.ModuleBase.InstallInRecovery()
-}
-
-func (p *PrebuiltEtc) InstallInRecovery() bool {
-	return p.inRecovery()
-}
-
-var _ ImageInterface = (*PrebuiltEtc)(nil)
-
-func (p *PrebuiltEtc) ImageMutatorBegin(ctx BaseModuleContext) {}
-
-func (p *PrebuiltEtc) CoreVariantNeeded(ctx BaseModuleContext) bool {
-	return !p.ModuleBase.InstallInRecovery() && !p.ModuleBase.InstallInRamdisk()
-}
-
-func (p *PrebuiltEtc) RamdiskVariantNeeded(ctx BaseModuleContext) bool {
-	return Bool(p.properties.Ramdisk_available) || p.ModuleBase.InstallInRamdisk()
-}
-
-func (p *PrebuiltEtc) RecoveryVariantNeeded(ctx BaseModuleContext) bool {
-	return Bool(p.properties.Recovery_available) || p.ModuleBase.InstallInRecovery()
-}
-
-func (p *PrebuiltEtc) ExtraImageVariations(ctx BaseModuleContext) []string {
-	return nil
-}
-
-func (p *PrebuiltEtc) SetImageVariation(ctx BaseModuleContext, variation string, module Module) {
-}
-
-func (p *PrebuiltEtc) DepsMutator(ctx BottomUpMutatorContext) {
-	if p.properties.Src == nil {
-		ctx.PropertyErrorf("src", "missing prebuilt source file")
-	}
-}
-
-func (p *PrebuiltEtc) SourceFilePath(ctx ModuleContext) Path {
-	return PathForModuleSrc(ctx, String(p.properties.Src))
-}
-
-func (p *PrebuiltEtc) InstallDirPath() InstallPath {
-	return p.installDirPath
-}
-
-// This allows other derivative modules (e.g. prebuilt_etc_xml) to perform
-// additional steps (like validating the src) before the file is installed.
-func (p *PrebuiltEtc) SetAdditionalDependencies(paths Paths) {
-	p.additionalDependencies = &paths
-}
-
-func (p *PrebuiltEtc) OutputFile() OutputPath {
-	return p.outputFilePath
-}
-
-func (p *PrebuiltEtc) SubDir() string {
-	return String(p.properties.Sub_dir)
-}
-
-func (p *PrebuiltEtc) Installable() bool {
-	return p.properties.Installable == nil || Bool(p.properties.Installable)
-}
-
-func (p *PrebuiltEtc) GenerateAndroidBuildActions(ctx ModuleContext) {
-	p.sourceFilePath = PathForModuleSrc(ctx, String(p.properties.Src))
-	filename := String(p.properties.Filename)
-	filename_from_src := Bool(p.properties.Filename_from_src)
-	if filename == "" {
-		if filename_from_src {
-			filename = p.sourceFilePath.Base()
-		} else {
-			filename = ctx.ModuleName()
-		}
-	} else if filename_from_src {
-		ctx.PropertyErrorf("filename_from_src", "filename is set. filename_from_src can't be true")
-		return
-	}
-	p.outputFilePath = PathForModuleOut(ctx, filename).OutputPath
-
-	// If soc install dir was specified and SOC specific is set, set the installDirPath to the specified
-	// socInstallDirBase.
-	installBaseDir := p.installDirBase
-	if ctx.SocSpecific() && p.socInstallDirBase != "" {
-		installBaseDir = p.socInstallDirBase
-	}
-	p.installDirPath = PathForModuleInstall(ctx, installBaseDir, String(p.properties.Sub_dir))
-
-	// This ensures that outputFilePath has the correct name for others to
-	// use, as the source file may have a different name.
-	ctx.Build(pctx, BuildParams{
-		Rule:   Cp,
-		Output: p.outputFilePath,
-		Input:  p.sourceFilePath,
-	})
-}
-
-func (p *PrebuiltEtc) AndroidMkEntries() []AndroidMkEntries {
-	nameSuffix := ""
-	if p.inRamdisk() && !p.onlyInRamdisk() {
-		nameSuffix = ".ramdisk"
-	}
-	if p.inRecovery() && !p.onlyInRecovery() {
-		nameSuffix = ".recovery"
-	}
-	return []AndroidMkEntries{AndroidMkEntries{
-		Class:      "ETC",
-		SubName:    nameSuffix,
-		OutputFile: OptionalPathForPath(p.outputFilePath),
-		ExtraEntries: []AndroidMkExtraEntriesFunc{
-			func(entries *AndroidMkEntries) {
-				entries.SetString("LOCAL_MODULE_TAGS", "optional")
-				entries.SetString("LOCAL_MODULE_PATH", p.installDirPath.ToMakePath().String())
-				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.outputFilePath.Base())
-				entries.SetString("LOCAL_UNINSTALLABLE_MODULE", strconv.FormatBool(!p.Installable()))
-				if p.additionalDependencies != nil {
-					for _, path := range *p.additionalDependencies {
-						entries.SetString("LOCAL_ADDITIONAL_DEPENDENCIES", path.String())
-					}
-				}
-			},
-		},
-	}}
-}
-
-func InitPrebuiltEtcModule(p *PrebuiltEtc, dirBase string) {
-	p.installDirBase = dirBase
-	p.AddProperties(&p.properties)
-}
-
-// prebuilt_etc is for a prebuilt artifact that is installed in
-// <partition>/etc/<sub_dir> directory.
-func PrebuiltEtcFactory() Module {
-	module := &PrebuiltEtc{}
-	InitPrebuiltEtcModule(module, "etc")
-	// This module is device-only
-	InitAndroidArchModule(module, DeviceSupported, MultilibFirst)
-	return module
-}
-
-// prebuilt_etc_host is for a host prebuilt artifact that is installed in
-// $(HOST_OUT)/etc/<sub_dir> directory.
-func PrebuiltEtcHostFactory() Module {
-	module := &PrebuiltEtc{}
-	InitPrebuiltEtcModule(module, "etc")
-	// This module is host-only
-	InitAndroidArchModule(module, HostSupported, MultilibCommon)
-	return module
-}
-
-// prebuilt_usr_share is for a prebuilt artifact that is installed in
-// <partition>/usr/share/<sub_dir> directory.
-func PrebuiltUserShareFactory() Module {
-	module := &PrebuiltEtc{}
-	InitPrebuiltEtcModule(module, "usr/share")
-	// This module is device-only
-	InitAndroidArchModule(module, DeviceSupported, MultilibFirst)
-	return module
-}
-
-// prebuild_usr_share_host is for a host prebuilt artifact that is installed in
-// $(HOST_OUT)/usr/share/<sub_dir> directory.
-func PrebuiltUserShareHostFactory() Module {
-	module := &PrebuiltEtc{}
-	InitPrebuiltEtcModule(module, "usr/share")
-	// This module is host-only
-	InitAndroidArchModule(module, HostSupported, MultilibCommon)
-	return module
-}
-
-// prebuilt_font installs a font in <partition>/fonts directory.
-func PrebuiltFontFactory() Module {
-	module := &PrebuiltEtc{}
-	InitPrebuiltEtcModule(module, "fonts")
-	// This module is device-only
-	InitAndroidArchModule(module, DeviceSupported, MultilibFirst)
-	return module
-}
-
-// prebuilt_firmware installs a firmware file to <partition>/etc/firmware directory for system image.
-// If soc_specific property is set to true, the firmware file is installed to the vendor <partition>/firmware
-// directory for vendor image.
-func PrebuiltFirmwareFactory() Module {
-	module := &PrebuiltEtc{}
-	module.socInstallDirBase = "firmware"
-	InitPrebuiltEtcModule(module, "etc/firmware")
-	// This module is device-only
-	InitAndroidArchModule(module, DeviceSupported, MultilibFirst)
-	return module
-}
diff --git a/android/prebuilt_test.go b/android/prebuilt_test.go
index 8ff5c40..b568f78 100644
--- a/android/prebuilt_test.go
+++ b/android/prebuilt_test.go
@@ -24,7 +24,7 @@
 var prebuiltsTests = []struct {
 	name     string
 	modules  string
-	prebuilt bool
+	prebuilt []OsClass
 }{
 	{
 		name: "no prebuilt",
@@ -32,7 +32,7 @@
 			source {
 				name: "bar",
 			}`,
-		prebuilt: false,
+		prebuilt: nil,
 	},
 	{
 		name: "no source prebuilt not preferred",
@@ -42,7 +42,7 @@
 				prefer: false,
 				srcs: ["prebuilt_file"],
 			}`,
-		prebuilt: true,
+		prebuilt: []OsClass{Device, Host},
 	},
 	{
 		name: "no source prebuilt preferred",
@@ -52,7 +52,7 @@
 				prefer: true,
 				srcs: ["prebuilt_file"],
 			}`,
-		prebuilt: true,
+		prebuilt: []OsClass{Device, Host},
 	},
 	{
 		name: "prebuilt not preferred",
@@ -60,13 +60,13 @@
 			source {
 				name: "bar",
 			}
-			
+
 			prebuilt {
 				name: "bar",
 				prefer: false,
 				srcs: ["prebuilt_file"],
 			}`,
-		prebuilt: false,
+		prebuilt: nil,
 	},
 	{
 		name: "prebuilt preferred",
@@ -74,13 +74,13 @@
 			source {
 				name: "bar",
 			}
-			
+
 			prebuilt {
 				name: "bar",
 				prefer: true,
 				srcs: ["prebuilt_file"],
 			}`,
-		prebuilt: true,
+		prebuilt: []OsClass{Device, Host},
 	},
 	{
 		name: "prebuilt no file not preferred",
@@ -88,12 +88,12 @@
 			source {
 				name: "bar",
 			}
-			
+
 			prebuilt {
 				name: "bar",
 				prefer: false,
 			}`,
-		prebuilt: false,
+		prebuilt: nil,
 	},
 	{
 		name: "prebuilt no file preferred",
@@ -101,12 +101,12 @@
 			source {
 				name: "bar",
 			}
-			
+
 			prebuilt {
 				name: "bar",
 				prefer: true,
 			}`,
-		prebuilt: false,
+		prebuilt: nil,
 	},
 	{
 		name: "prebuilt file from filegroup preferred",
@@ -120,7 +120,40 @@
 				prefer: true,
 				srcs: [":fg"],
 			}`,
-		prebuilt: true,
+		prebuilt: []OsClass{Device, Host},
+	},
+	{
+		name: "prebuilt module for device only",
+		modules: `
+			source {
+				name: "bar",
+			}
+
+			prebuilt {
+				name: "bar",
+				host_supported: false,
+				prefer: true,
+				srcs: ["prebuilt_file"],
+			}`,
+		prebuilt: []OsClass{Device},
+	},
+	{
+		name: "prebuilt file for host only",
+		modules: `
+			source {
+				name: "bar",
+			}
+
+			prebuilt {
+				name: "bar",
+				prefer: true,
+				target: {
+					host: {
+						srcs: ["prebuilt_file"],
+					},
+				},
+			}`,
+		prebuilt: []OsClass{Host},
 	},
 }
 
@@ -138,9 +171,9 @@
 					deps: [":bar"],
 				}
 				` + test.modules
-			config := TestConfig(buildDir, nil, bp, fs)
+			config := TestArchConfig(buildDir, nil, bp, fs)
 
-			ctx := NewTestContext()
+			ctx := NewTestArchContext()
 			registerTestPrebuiltBuildComponents(ctx)
 			ctx.RegisterModuleType("filegroup", FileGroupFactory)
 			ctx.Register(config)
@@ -150,61 +183,71 @@
 			_, errs = ctx.PrepareBuildActions(config)
 			FailIfErrored(t, errs)
 
-			foo := ctx.ModuleForTests("foo", "")
+			for _, variant := range ctx.ModuleVariantsForTests("foo") {
+				foo := ctx.ModuleForTests("foo", variant)
+				t.Run(foo.Module().Target().Os.Class.String(), func(t *testing.T) {
+					var dependsOnSourceModule, dependsOnPrebuiltModule bool
+					ctx.VisitDirectDeps(foo.Module(), func(m blueprint.Module) {
+						if _, ok := m.(*sourceModule); ok {
+							dependsOnSourceModule = true
+						}
+						if p, ok := m.(*prebuiltModule); ok {
+							dependsOnPrebuiltModule = true
+							if !p.Prebuilt().properties.UsePrebuilt {
+								t.Errorf("dependency on prebuilt module not marked used")
+							}
+						}
+					})
 
-			var dependsOnSourceModule, dependsOnPrebuiltModule bool
-			ctx.VisitDirectDeps(foo.Module(), func(m blueprint.Module) {
-				if _, ok := m.(*sourceModule); ok {
-					dependsOnSourceModule = true
-				}
-				if p, ok := m.(*prebuiltModule); ok {
-					dependsOnPrebuiltModule = true
-					if !p.Prebuilt().properties.UsePrebuilt {
-						t.Errorf("dependency on prebuilt module not marked used")
+					deps := foo.Module().(*sourceModule).deps
+					if deps == nil || len(deps) != 1 {
+						t.Errorf("deps does not have single path, but is %v", deps)
 					}
-				}
-			})
+					var usingSourceFile, usingPrebuiltFile bool
+					if deps[0].String() == "source_file" {
+						usingSourceFile = true
+					}
+					if deps[0].String() == "prebuilt_file" {
+						usingPrebuiltFile = true
+					}
 
-			deps := foo.Module().(*sourceModule).deps
-			if deps == nil || len(deps) != 1 {
-				t.Errorf("deps does not have single path, but is %v", deps)
-			}
-			var usingSourceFile, usingPrebuiltFile bool
-			if deps[0].String() == "source_file" {
-				usingSourceFile = true
-			}
-			if deps[0].String() == "prebuilt_file" {
-				usingPrebuiltFile = true
-			}
+					prebuilt := false
+					for _, os := range test.prebuilt {
+						if os == foo.Module().Target().Os.Class {
+							prebuilt = true
+						}
+					}
 
-			if test.prebuilt {
-				if !dependsOnPrebuiltModule {
-					t.Errorf("doesn't depend on prebuilt module")
-				}
-				if !usingPrebuiltFile {
-					t.Errorf("doesn't use prebuilt_file")
-				}
+					if prebuilt {
+						if !dependsOnPrebuiltModule {
+							t.Errorf("doesn't depend on prebuilt module")
+						}
+						if !usingPrebuiltFile {
+							t.Errorf("doesn't use prebuilt_file")
+						}
 
-				if dependsOnSourceModule {
-					t.Errorf("depends on source module")
-				}
-				if usingSourceFile {
-					t.Errorf("using source_file")
-				}
-			} else {
-				if dependsOnPrebuiltModule {
-					t.Errorf("depends on prebuilt module")
-				}
-				if usingPrebuiltFile {
-					t.Errorf("using prebuilt_file")
-				}
+						if dependsOnSourceModule {
+							t.Errorf("depends on source module")
+						}
+						if usingSourceFile {
+							t.Errorf("using source_file")
+						}
+					} else {
+						if dependsOnPrebuiltModule {
+							t.Errorf("depends on prebuilt module")
+						}
+						if usingPrebuiltFile {
+							t.Errorf("using prebuilt_file")
+						}
 
-				if !dependsOnSourceModule {
-					t.Errorf("doesn't depend on source module")
-				}
-				if !usingSourceFile {
-					t.Errorf("doesn't use source_file")
-				}
+						if !dependsOnSourceModule {
+							t.Errorf("doesn't depend on source module")
+						}
+						if !usingSourceFile {
+							t.Errorf("doesn't use source_file")
+						}
+					}
+				})
 			}
 		})
 	}
@@ -221,7 +264,7 @@
 	ModuleBase
 	prebuilt   Prebuilt
 	properties struct {
-		Srcs []string `android:"path"`
+		Srcs []string `android:"path,arch_variant"`
 	}
 	src Path
 }
@@ -230,7 +273,7 @@
 	m := &prebuiltModule{}
 	m.AddProperties(&m.properties)
 	InitPrebuiltModule(m, &m.properties.Srcs)
-	InitAndroidModule(m)
+	InitAndroidArchModule(m, HostAndDeviceDefault, MultilibCommon)
 	return m
 }
 
@@ -260,7 +303,7 @@
 type sourceModule struct {
 	ModuleBase
 	properties struct {
-		Deps []string `android:"path"`
+		Deps []string `android:"path,arch_variant"`
 	}
 	dependsOnSourceModule, dependsOnPrebuiltModule bool
 	deps                                           Paths
@@ -270,7 +313,7 @@
 func newSourceModule() Module {
 	m := &sourceModule{}
 	m.AddProperties(&m.properties)
-	InitAndroidModule(m)
+	InitAndroidArchModule(m, HostAndDeviceDefault, MultilibCommon)
 	return m
 }
 
diff --git a/android/sh_binary.go b/android/sh_binary.go
deleted file mode 100644
index 7d9dc67..0000000
--- a/android/sh_binary.go
+++ /dev/null
@@ -1,259 +0,0 @@
-// Copyright 2019 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package android
-
-import (
-	"fmt"
-	"path/filepath"
-	"strings"
-)
-
-// sh_binary is for shell scripts (and batch files) that are installed as
-// executable files into .../bin/
-//
-// Do not use them for prebuilt C/C++/etc files.  Use cc_prebuilt_binary
-// instead.
-
-func init() {
-	RegisterModuleType("sh_binary", ShBinaryFactory)
-	RegisterModuleType("sh_binary_host", ShBinaryHostFactory)
-	RegisterModuleType("sh_test", ShTestFactory)
-	RegisterModuleType("sh_test_host", ShTestHostFactory)
-}
-
-type shBinaryProperties struct {
-	// Source file of this prebuilt.
-	Src *string `android:"path,arch_variant"`
-
-	// optional subdirectory under which this file is installed into
-	Sub_dir *string `android:"arch_variant"`
-
-	// optional name for the installed file. If unspecified, name of the module is used as the file name
-	Filename *string `android:"arch_variant"`
-
-	// when set to true, and filename property is not set, the name for the installed file
-	// is the same as the file name of the source file.
-	Filename_from_src *bool `android:"arch_variant"`
-
-	// Whether this module is directly installable to one of the partitions. Default: true.
-	Installable *bool
-
-	// install symlinks to the binary
-	Symlinks []string `android:"arch_variant"`
-}
-
-type TestProperties struct {
-	// list of compatibility suites (for example "cts", "vts") that the module should be
-	// installed into.
-	Test_suites []string `android:"arch_variant"`
-
-	// the name of the test configuration (for example "AndroidTest.xml") that should be
-	// installed with the module.
-	Test_config *string `android:"arch_variant"`
-
-	// list of files or filegroup modules that provide data that should be installed alongside
-	// the test.
-	Data []string `android:"path,arch_variant"`
-}
-
-type ShBinary struct {
-	ModuleBase
-
-	properties shBinaryProperties
-
-	sourceFilePath Path
-	outputFilePath OutputPath
-	installedFile  InstallPath
-}
-
-var _ HostToolProvider = (*ShBinary)(nil)
-
-type ShTest struct {
-	ShBinary
-
-	testProperties TestProperties
-
-	data Paths
-}
-
-func (s *ShBinary) HostToolPath() OptionalPath {
-	return OptionalPathForPath(s.installedFile)
-}
-
-func (s *ShBinary) DepsMutator(ctx BottomUpMutatorContext) {
-	if s.properties.Src == nil {
-		ctx.PropertyErrorf("src", "missing prebuilt source file")
-	}
-}
-
-func (s *ShBinary) OutputFile() OutputPath {
-	return s.outputFilePath
-}
-
-func (s *ShBinary) SubDir() string {
-	return String(s.properties.Sub_dir)
-}
-
-func (s *ShBinary) Installable() bool {
-	return s.properties.Installable == nil || Bool(s.properties.Installable)
-}
-
-func (s *ShBinary) Symlinks() []string {
-	return s.properties.Symlinks
-}
-
-func (s *ShBinary) generateAndroidBuildActions(ctx ModuleContext) {
-	s.sourceFilePath = PathForModuleSrc(ctx, String(s.properties.Src))
-	filename := String(s.properties.Filename)
-	filename_from_src := Bool(s.properties.Filename_from_src)
-	if filename == "" {
-		if filename_from_src {
-			filename = s.sourceFilePath.Base()
-		} else {
-			filename = ctx.ModuleName()
-		}
-	} else if filename_from_src {
-		ctx.PropertyErrorf("filename_from_src", "filename is set. filename_from_src can't be true")
-		return
-	}
-	s.outputFilePath = PathForModuleOut(ctx, filename).OutputPath
-
-	// This ensures that outputFilePath has the correct name for others to
-	// use, as the source file may have a different name.
-	ctx.Build(pctx, BuildParams{
-		Rule:   CpExecutable,
-		Output: s.outputFilePath,
-		Input:  s.sourceFilePath,
-	})
-}
-
-func (s *ShBinary) GenerateAndroidBuildActions(ctx ModuleContext) {
-	s.generateAndroidBuildActions(ctx)
-	installDir := PathForModuleInstall(ctx, "bin", String(s.properties.Sub_dir))
-	s.installedFile = ctx.InstallExecutable(installDir, s.outputFilePath.Base(), s.outputFilePath)
-}
-
-func (s *ShBinary) AndroidMkEntries() []AndroidMkEntries {
-	return []AndroidMkEntries{AndroidMkEntries{
-		Class:      "EXECUTABLES",
-		OutputFile: OptionalPathForPath(s.outputFilePath),
-		Include:    "$(BUILD_SYSTEM)/soong_cc_prebuilt.mk",
-		ExtraEntries: []AndroidMkExtraEntriesFunc{
-			func(entries *AndroidMkEntries) {
-				s.customAndroidMkEntries(entries)
-			},
-		},
-	}}
-}
-
-func (s *ShBinary) customAndroidMkEntries(entries *AndroidMkEntries) {
-	entries.SetString("LOCAL_MODULE_RELATIVE_PATH", String(s.properties.Sub_dir))
-	entries.SetString("LOCAL_MODULE_SUFFIX", "")
-	entries.SetString("LOCAL_MODULE_STEM", s.outputFilePath.Rel())
-	if len(s.properties.Symlinks) > 0 {
-		entries.SetString("LOCAL_MODULE_SYMLINKS", strings.Join(s.properties.Symlinks, " "))
-	}
-}
-
-func (s *ShTest) GenerateAndroidBuildActions(ctx ModuleContext) {
-	s.ShBinary.generateAndroidBuildActions(ctx)
-	testDir := "nativetest"
-	if ctx.Target().Arch.ArchType.Multilib == "lib64" {
-		testDir = "nativetest64"
-	}
-	if ctx.Target().NativeBridge == NativeBridgeEnabled {
-		testDir = filepath.Join(testDir, ctx.Target().NativeBridgeRelativePath)
-	} else if !ctx.Host() && ctx.Config().HasMultilibConflict(ctx.Arch().ArchType) {
-		testDir = filepath.Join(testDir, ctx.Arch().ArchType.String())
-	}
-	installDir := PathForModuleInstall(ctx, testDir, String(s.properties.Sub_dir))
-	s.installedFile = ctx.InstallExecutable(installDir, s.outputFilePath.Base(), s.outputFilePath)
-
-	s.data = PathsForModuleSrc(ctx, s.testProperties.Data)
-}
-
-func (s *ShTest) InstallInData() bool {
-	return true
-}
-
-func (s *ShTest) AndroidMkEntries() []AndroidMkEntries {
-	return []AndroidMkEntries{AndroidMkEntries{
-		Class:      "NATIVE_TESTS",
-		OutputFile: OptionalPathForPath(s.outputFilePath),
-		Include:    "$(BUILD_SYSTEM)/soong_cc_prebuilt.mk",
-		ExtraEntries: []AndroidMkExtraEntriesFunc{
-			func(entries *AndroidMkEntries) {
-				s.customAndroidMkEntries(entries)
-
-				entries.AddStrings("LOCAL_COMPATIBILITY_SUITE", s.testProperties.Test_suites...)
-				entries.SetString("LOCAL_TEST_CONFIG", String(s.testProperties.Test_config))
-				for _, d := range s.data {
-					rel := d.Rel()
-					path := d.String()
-					if !strings.HasSuffix(path, rel) {
-						panic(fmt.Errorf("path %q does not end with %q", path, rel))
-					}
-					path = strings.TrimSuffix(path, rel)
-					entries.AddStrings("LOCAL_TEST_DATA", path+":"+rel)
-				}
-			},
-		},
-	}}
-}
-
-func InitShBinaryModule(s *ShBinary) {
-	s.AddProperties(&s.properties)
-}
-
-// sh_binary is for a shell script or batch file to be installed as an
-// executable binary to <partition>/bin.
-func ShBinaryFactory() Module {
-	module := &ShBinary{}
-	module.Prefer32(func(ctx BaseModuleContext, base *ModuleBase, class OsClass) bool {
-		return class == Device && ctx.Config().DevicePrefer32BitExecutables()
-	})
-	InitShBinaryModule(module)
-	InitAndroidArchModule(module, HostAndDeviceSupported, MultilibFirst)
-	return module
-}
-
-// sh_binary_host is for a shell script to be installed as an executable binary
-// to $(HOST_OUT)/bin.
-func ShBinaryHostFactory() Module {
-	module := &ShBinary{}
-	InitShBinaryModule(module)
-	InitAndroidArchModule(module, HostSupported, MultilibFirst)
-	return module
-}
-
-// sh_test defines a shell script based test module.
-func ShTestFactory() Module {
-	module := &ShTest{}
-	InitShBinaryModule(&module.ShBinary)
-	module.AddProperties(&module.testProperties)
-
-	InitAndroidArchModule(module, HostAndDeviceSupported, MultilibFirst)
-	return module
-}
-
-// sh_test_host defines a shell script based test module that runs on a host.
-func ShTestHostFactory() Module {
-	module := &ShTest{}
-	InitShBinaryModule(&module.ShBinary)
-	module.AddProperties(&module.testProperties)
-
-	InitAndroidArchModule(module, HostSupported, MultilibFirst)
-	return module
-}
diff --git a/android/sh_binary_test.go b/android/sh_binary_test.go
deleted file mode 100644
index 137e773..0000000
--- a/android/sh_binary_test.go
+++ /dev/null
@@ -1,70 +0,0 @@
-package android
-
-import (
-	"reflect"
-	"testing"
-)
-
-func testShBinary(t *testing.T, bp string) (*TestContext, Config) {
-	fs := map[string][]byte{
-		"test.sh":            nil,
-		"testdata/data1":     nil,
-		"testdata/sub/data2": nil,
-	}
-
-	config := TestArchConfig(buildDir, nil, bp, fs)
-
-	ctx := NewTestArchContext()
-	ctx.RegisterModuleType("sh_test", ShTestFactory)
-	ctx.RegisterModuleType("sh_test_host", ShTestHostFactory)
-	ctx.Register(config)
-	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
-	FailIfErrored(t, errs)
-	_, errs = ctx.PrepareBuildActions(config)
-	FailIfErrored(t, errs)
-
-	return ctx, config
-}
-
-func TestShTestTestData(t *testing.T) {
-	ctx, config := testShBinary(t, `
-		sh_test {
-			name: "foo",
-			src: "test.sh",
-			filename: "test.sh",
-			data: [
-				"testdata/data1",
-				"testdata/sub/data2",
-			],
-		}
-	`)
-
-	mod := ctx.ModuleForTests("foo", "android_arm64_armv8-a").Module().(*ShTest)
-
-	entries := AndroidMkEntriesForTest(t, config, "", mod)[0]
-	expected := []string{":testdata/data1", ":testdata/sub/data2"}
-	actual := entries.EntryMap["LOCAL_TEST_DATA"]
-	if !reflect.DeepEqual(expected, actual) {
-		t.Errorf("Unexpected test data expected: %q, actual: %q", expected, actual)
-	}
-}
-
-func TestShTestHost(t *testing.T) {
-	ctx, _ := testShBinary(t, `
-		sh_test_host {
-			name: "foo",
-			src: "test.sh",
-			filename: "test.sh",
-			data: [
-				"testdata/data1",
-				"testdata/sub/data2",
-			],
-		}
-	`)
-
-	buildOS := BuildOs.String()
-	mod := ctx.ModuleForTests("foo", buildOS+"_x86_64").Module().(*ShTest)
-	if !mod.Host() {
-		t.Errorf("host bit is not set for a sh_test_host module.")
-	}
-}
diff --git a/android/soongconfig/Android.bp b/android/soongconfig/Android.bp
new file mode 100644
index 0000000..df912e6
--- /dev/null
+++ b/android/soongconfig/Android.bp
@@ -0,0 +1,13 @@
+bootstrap_go_package {
+    name: "soong-android-soongconfig",
+    pkgPath: "android/soong/android/soongconfig",
+    deps: [
+        "blueprint",
+        "blueprint-parser",
+        "blueprint-proptools",
+    ],
+    srcs: [
+        "config.go",
+        "modules.go",
+    ],
+}
diff --git a/android/testing.go b/android/testing.go
index 90989ef..696efb6 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -18,6 +18,7 @@
 	"fmt"
 	"path/filepath"
 	"regexp"
+	"sort"
 	"strings"
 	"testing"
 
@@ -122,9 +123,10 @@
 		ctx.VisitAllModules(func(m blueprint.Module) {
 			allModuleNames = append(allModuleNames, m.(Module).Name()+"("+ctx.ModuleSubDir(m)+")")
 		})
+		sort.Strings(allModuleNames)
 
-		panic(fmt.Errorf("failed to find module %q variant %q."+
-			"\nall modules: %v", name, variant, allModuleNames))
+		panic(fmt.Errorf("failed to find module %q variant %q. All modules:\n  %s",
+			name, variant, strings.Join(allModuleNames, "\n  ")))
 	}
 
 	return TestingModule{module}
diff --git a/android/variable.go b/android/variable.go
index 3c08405..e025ae0 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -272,10 +272,6 @@
 	CoveragePaths        []string `json:",omitempty"`
 	CoverageExcludePaths []string `json:",omitempty"`
 
-	DevicePrefer32BitApps        *bool `json:",omitempty"`
-	DevicePrefer32BitExecutables *bool `json:",omitempty"`
-	HostPrefer32BitExecutables   *bool `json:",omitempty"`
-
 	SanitizeHost       []string `json:",omitempty"`
 	SanitizeDevice     []string `json:",omitempty"`
 	SanitizeDeviceDiag []string `json:",omitempty"`
diff --git a/apex/Android.bp b/apex/Android.bp
new file mode 100644
index 0000000..144f441
--- /dev/null
+++ b/apex/Android.bp
@@ -0,0 +1,27 @@
+bootstrap_go_package {
+    name: "soong-apex",
+    pkgPath: "android/soong/apex",
+    deps: [
+        "blueprint",
+        "soong",
+        "soong-android",
+        "soong-cc",
+        "soong-java",
+        "soong-python",
+        "soong-sh",
+    ],
+    srcs: [
+        "androidmk.go",
+        "apex.go",
+        "apex_singleton.go",
+        "builder.go",
+        "key.go",
+        "prebuilt.go",
+        "vndk.go",
+    ],
+    testSrcs: [
+        "apex_test.go",
+        "vndk_test.go",
+    ],
+    pluginFor: ["soong_build"],
+}
diff --git a/apex/androidmk.go b/apex/androidmk.go
index 08d190c..774b62d 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -81,7 +81,7 @@
 	seenDataOutPaths := make(map[string]bool)
 
 	for _, fi := range a.filesInfo {
-		if cc, ok := fi.module.(*cc.Module); ok && cc.Properties.HideFromMake {
+		if ccMod, ok := fi.module.(*cc.Module); ok && ccMod.Properties.HideFromMake {
 			continue
 		}
 
@@ -178,41 +178,53 @@
 		if fi.jacocoReportClassesFile != nil {
 			fmt.Fprintln(w, "LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR :=", fi.jacocoReportClassesFile.String())
 		}
-		if fi.class == javaSharedLib {
-			javaModule := fi.module.(java.Dependency)
+		switch fi.class {
+		case javaSharedLib:
 			// soong_java_prebuilt.mk sets LOCAL_MODULE_SUFFIX := .jar  Therefore
 			// we need to remove the suffix from LOCAL_MODULE_STEM, otherwise
 			// we will have foo.jar.jar
-			fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", strings.TrimSuffix(fi.builtFile.Base(), ".jar"))
-			fmt.Fprintln(w, "LOCAL_SOONG_CLASSES_JAR :=", javaModule.ImplementationAndResourcesJars()[0].String())
-			fmt.Fprintln(w, "LOCAL_SOONG_HEADER_JAR :=", javaModule.HeaderJars()[0].String())
+			fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", strings.TrimSuffix(fi.Stem(), ".jar"))
+			if javaModule, ok := fi.module.(java.Dependency); ok {
+				fmt.Fprintln(w, "LOCAL_SOONG_CLASSES_JAR :=", javaModule.ImplementationAndResourcesJars()[0].String())
+				fmt.Fprintln(w, "LOCAL_SOONG_HEADER_JAR :=", javaModule.HeaderJars()[0].String())
+			} else {
+				fmt.Fprintln(w, "LOCAL_SOONG_CLASSES_JAR :=", fi.builtFile.String())
+				fmt.Fprintln(w, "LOCAL_SOONG_HEADER_JAR :=", fi.builtFile.String())
+			}
 			fmt.Fprintln(w, "LOCAL_SOONG_DEX_JAR :=", fi.builtFile.String())
 			fmt.Fprintln(w, "LOCAL_DEX_PREOPT := false")
 			fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_java_prebuilt.mk")
-		} else if fi.class == app {
+		case app:
 			fmt.Fprintln(w, "LOCAL_CERTIFICATE :=", fi.certificate.AndroidMkString())
 			// soong_app_prebuilt.mk sets LOCAL_MODULE_SUFFIX := .apk  Therefore
 			// we need to remove the suffix from LOCAL_MODULE_STEM, otherwise
 			// we will have foo.apk.apk
-			fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", strings.TrimSuffix(fi.builtFile.Base(), ".apk"))
+			fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", strings.TrimSuffix(fi.Stem(), ".apk"))
 			if app, ok := fi.module.(*java.AndroidApp); ok && len(app.JniCoverageOutputs()) > 0 {
 				fmt.Fprintln(w, "LOCAL_PREBUILT_COVERAGE_ARCHIVE :=", strings.Join(app.JniCoverageOutputs().Strings(), " "))
 			}
 			fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_app_prebuilt.mk")
-		} else if fi.class == nativeSharedLib || fi.class == nativeExecutable || fi.class == nativeTest {
-			fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", fi.builtFile.Base())
-			if cc, ok := fi.module.(*cc.Module); ok {
-				if cc.UnstrippedOutputFile() != nil {
-					fmt.Fprintln(w, "LOCAL_SOONG_UNSTRIPPED_BINARY :=", cc.UnstrippedOutputFile().String())
+		case appSet:
+			as, ok := fi.module.(*java.AndroidAppSet)
+			if !ok {
+				panic(fmt.Sprintf("Expected %s to be AndroidAppSet", fi.module))
+			}
+			fmt.Fprintln(w, "LOCAL_APK_SET_MASTER_FILE :=", as.MasterFile())
+			fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_android_app_set.mk")
+		case nativeSharedLib, nativeExecutable, nativeTest:
+			fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", fi.Stem())
+			if ccMod, ok := fi.module.(*cc.Module); ok {
+				if ccMod.UnstrippedOutputFile() != nil {
+					fmt.Fprintln(w, "LOCAL_SOONG_UNSTRIPPED_BINARY :=", ccMod.UnstrippedOutputFile().String())
 				}
-				cc.AndroidMkWriteAdditionalDependenciesForSourceAbiDiff(w)
-				if cc.CoverageOutputFile().Valid() {
-					fmt.Fprintln(w, "LOCAL_PREBUILT_COVERAGE_ARCHIVE :=", cc.CoverageOutputFile().String())
+				ccMod.AndroidMkWriteAdditionalDependenciesForSourceAbiDiff(w)
+				if ccMod.CoverageOutputFile().Valid() {
+					fmt.Fprintln(w, "LOCAL_PREBUILT_COVERAGE_ARCHIVE :=", ccMod.CoverageOutputFile().String())
 				}
 			}
 			fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_cc_prebuilt.mk")
-		} else {
-			fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", fi.builtFile.Base())
+		default:
+			fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", fi.Stem())
 			if fi.builtFile == a.manifestPbOut && apexType == flattenedApex {
 				if a.primaryApexType {
 					// Make apex_manifest.pb module for this APEX to override all other
diff --git a/apex/apex.go b/apex/apex.go
index 866c0f1..d45dd6f 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -22,14 +22,16 @@
 	"strings"
 	"sync"
 
-	"android/soong/android"
-	"android/soong/cc"
-	"android/soong/java"
-	"android/soong/python"
-
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/bootstrap"
 	"github.com/google/blueprint/proptools"
+
+	"android/soong/android"
+	"android/soong/cc"
+	prebuilt_etc "android/soong/etc"
+	"android/soong/java"
+	"android/soong/python"
+	"android/soong/sh"
 )
 
 const (
@@ -1113,6 +1115,7 @@
 	javaSharedLib
 	nativeTest
 	app
+	appSet
 )
 
 func (class apexFileClass) NameInMake() string {
@@ -1127,7 +1130,7 @@
 		return "JAVA_LIBRARIES"
 	case nativeTest:
 		return "NATIVE_TESTS"
-	case app:
+	case app, appSet:
 		// b/142537672 Why isn't this APP? We want to have full control over
 		// the paths and file names of the apk file under the flattend APEX.
 		// If this is set to APP, then the paths and file names are modified
@@ -1144,6 +1147,7 @@
 // apexFile represents a file in an APEX bundle
 type apexFile struct {
 	builtFile  android.Path
+	stem       string
 	moduleName string
 	installDir string
 	class      apexFileClass
@@ -1192,7 +1196,14 @@
 
 // Path() returns path of this apex file relative to the APEX root
 func (af *apexFile) Path() string {
-	return af.apexRelativePath(af.builtFile.Base())
+	return af.apexRelativePath(af.Stem())
+}
+
+func (af *apexFile) Stem() string {
+	if af.stem != "" {
+		return af.stem
+	}
+	return af.builtFile.Base()
 }
 
 // SymlinkPaths() returns paths of the symlinks (if any) relative to the APEX root
@@ -1631,7 +1642,7 @@
 	return newApexFile(ctx, fileToCopy, depName, dirInApex, goBinary, nil)
 }
 
-func apexFileForShBinary(ctx android.BaseModuleContext, sh *android.ShBinary) apexFile {
+func apexFileForShBinary(ctx android.BaseModuleContext, sh *sh.ShBinary) apexFile {
 	dirInApex := filepath.Join("bin", sh.SubDir())
 	fileToCopy := sh.OutputFile()
 	af := newApexFile(ctx, fileToCopy, sh.Name(), dirInApex, shBinary, sh)
@@ -1639,15 +1650,22 @@
 	return af
 }
 
-func apexFileForJavaLibrary(ctx android.BaseModuleContext, lib java.Dependency, module android.Module) apexFile {
+type javaDependency interface {
+	DexJarBuildPath() android.Path
+	JacocoReportClassesFile() android.Path
+	Stem() string
+}
+
+func apexFileForJavaLibrary(ctx android.BaseModuleContext, lib javaDependency, module android.Module) apexFile {
 	dirInApex := "javalib"
-	fileToCopy := lib.DexJar()
+	fileToCopy := lib.DexJarBuildPath()
 	af := newApexFile(ctx, fileToCopy, module.Name(), dirInApex, javaSharedLib, module)
 	af.jacocoReportClassesFile = lib.JacocoReportClassesFile()
+	af.stem = lib.Stem() + ".jar"
 	return af
 }
 
-func apexFileForPrebuiltEtc(ctx android.BaseModuleContext, prebuilt android.PrebuiltEtcModule, depName string) apexFile {
+func apexFileForPrebuiltEtc(ctx android.BaseModuleContext, prebuilt prebuilt_etc.PrebuiltEtcModule, depName string) apexFile {
 	dirInApex := filepath.Join("etc", prebuilt.SubDir())
 	fileToCopy := prebuilt.OutputFile()
 	return newApexFile(ctx, fileToCopy, depName, dirInApex, etc, prebuilt)
@@ -1932,13 +1950,13 @@
 			case sharedLibTag, jniLibTag:
 				isJniLib := depTag == jniLibTag
 				if c, ok := child.(*cc.Module); ok {
-					// bootstrap bionic libs are treated as provided by system
-					if c.HasStubsVariants() && !cc.InstallToBootstrap(c.BaseModuleName(), ctx.Config()) {
-						provideNativeLibs = append(provideNativeLibs, c.OutputFile().Path().Base())
-					}
 					fi := apexFileForNativeLibrary(ctx, c, handleSpecialLibs)
 					fi.isJniLib = isJniLib
 					filesInfo = append(filesInfo, fi)
+					// bootstrap bionic libs are treated as provided by system
+					if c.HasStubsVariants() && !cc.InstallToBootstrap(c.BaseModuleName(), ctx.Config()) {
+						provideNativeLibs = append(provideNativeLibs, fi.Stem())
+					}
 					return true // track transitive dependencies
 				} else {
 					propertyName := "native_shared_libs"
@@ -1951,7 +1969,7 @@
 				if cc, ok := child.(*cc.Module); ok {
 					filesInfo = append(filesInfo, apexFileForExecutable(ctx, cc))
 					return true // track transitive dependencies
-				} else if sh, ok := child.(*android.ShBinary); ok {
+				} else if sh, ok := child.(*sh.ShBinary); ok {
 					filesInfo = append(filesInfo, apexFileForShBinary(ctx, sh))
 				} else if py, ok := child.(*python.Module); ok && py.HostToolPath().Valid() {
 					filesInfo = append(filesInfo, apexFileForPyBinary(ctx, py))
@@ -1961,23 +1979,16 @@
 					ctx.PropertyErrorf("binaries", "%q is neither cc_binary, (embedded) py_binary, (host) blueprint_go_binary, (host) bootstrap_go_binary, nor sh_binary", depName)
 				}
 			case javaLibTag:
-				if javaLib, ok := child.(*java.Library); ok {
-					af := apexFileForJavaLibrary(ctx, javaLib, javaLib)
-					if !af.Ok() {
-						ctx.PropertyErrorf("java_libs", "%q is not configured to be compiled into dex", depName)
-					} else {
-						filesInfo = append(filesInfo, af)
-						return true // track transitive dependencies
-					}
-				} else if sdkLib, ok := child.(*java.SdkLibrary); ok {
-					af := apexFileForJavaLibrary(ctx, sdkLib, sdkLib)
+				switch child.(type) {
+				case *java.Library, *java.SdkLibrary, *java.DexImport:
+					af := apexFileForJavaLibrary(ctx, child.(javaDependency), child.(android.Module))
 					if !af.Ok() {
 						ctx.PropertyErrorf("java_libs", "%q is not configured to be compiled into dex", depName)
 						return false
 					}
 					filesInfo = append(filesInfo, af)
 					return true // track transitive dependencies
-				} else {
+				default:
 					ctx.PropertyErrorf("java_libs", "%q of type %q is not supported", depName, ctx.OtherModuleType(child))
 				}
 			case androidAppTag:
@@ -1988,6 +1999,15 @@
 					filesInfo = append(filesInfo, apexFileForAndroidApp(ctx, ap))
 				} else if ap, ok := child.(*java.AndroidTestHelperApp); ok {
 					filesInfo = append(filesInfo, apexFileForAndroidApp(ctx, ap))
+				} else if ap, ok := child.(*java.AndroidAppSet); ok {
+					appDir := "app"
+					if ap.Privileged() {
+						appDir = "priv-app"
+					}
+					af := newApexFile(ctx, ap.OutputFile(), ap.Name(),
+						filepath.Join(appDir, ap.BaseModuleName()), appSet, ap)
+					af.certificate = java.PresignedCertificate
+					filesInfo = append(filesInfo, af)
 				} else {
 					ctx.PropertyErrorf("apps", "%q is not an android_app module", depName)
 				}
@@ -1998,7 +2018,7 @@
 					ctx.PropertyErrorf("rros", "%q is not an runtime_resource_overlay module", depName)
 				}
 			case prebuiltTag:
-				if prebuilt, ok := child.(android.PrebuiltEtcModule); ok {
+				if prebuilt, ok := child.(prebuilt_etc.PrebuiltEtcModule); ok {
 					filesInfo = append(filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, depName))
 				} else if prebuilt, ok := child.(java.PlatformCompatConfigIntf); ok {
 					filesInfo = append(filesInfo, apexFileForCompatConfig(ctx, prebuilt, depName))
@@ -2058,6 +2078,8 @@
 							// don't include it in this APEX
 							return false
 						}
+						af := apexFileForNativeLibrary(ctx, cc, handleSpecialLibs)
+						af.transitiveDep = true
 						if !a.Host() && !android.DirectlyInApex(ctx.ModuleName(), ctx.OtherModuleName(cc)) && (cc.IsStubs() || cc.HasStubsVariants()) {
 							// If the dependency is a stubs lib, don't include it in this APEX,
 							// but make sure that the lib is installed on the device.
@@ -2069,12 +2091,10 @@
 							if !android.DirectlyInAnyApex(ctx, cc.Name()) && !android.InList(cc.BaseModuleName(), a.requiredDeps) {
 								a.requiredDeps = append(a.requiredDeps, cc.BaseModuleName())
 							}
-							requireNativeLibs = append(requireNativeLibs, cc.OutputFile().Path().Base())
+							requireNativeLibs = append(requireNativeLibs, af.Stem())
 							// Don't track further
 							return false
 						}
-						af := apexFileForNativeLibrary(ctx, cc, handleSpecialLibs)
-						af.transitiveDep = true
 						filesInfo = append(filesInfo, af)
 						return true // track transitive dependencies
 					}
@@ -2095,7 +2115,7 @@
 					// Because APK-in-APEX embeds jni_libs transitively, we don't need to track transitive deps
 					return false
 				} else if java.IsXmlPermissionsFileDepTag(depTag) {
-					if prebuilt, ok := child.(android.PrebuiltEtcModule); ok {
+					if prebuilt, ok := child.(prebuilt_etc.PrebuiltEtcModule); ok {
 						filesInfo = append(filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, depName))
 					}
 				} else if am.CanHaveApexVariants() && am.IsInstallableToApex() {
@@ -2260,9 +2280,6 @@
 	module.AddProperties(&module.properties)
 	module.AddProperties(&module.targetProperties)
 	module.AddProperties(&module.overridableProperties)
-	module.Prefer32(func(ctx android.BaseModuleContext, base *android.ModuleBase, class android.OsClass) bool {
-		return class == android.Device && ctx.Config().DevicePrefer32BitExecutables()
-	})
 	android.InitAndroidMultiTargetsArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon)
 	android.InitDefaultableModule(module)
 	android.InitSdkAwareModule(module)
diff --git a/apex/apex_test.go b/apex/apex_test.go
index ced113f..7bd0374 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -29,7 +29,9 @@
 	"android/soong/android"
 	"android/soong/cc"
 	"android/soong/dexpreopt"
+	prebuilt_etc "android/soong/etc"
 	"android/soong/java"
+	"android/soong/sh"
 )
 
 var buildDir string
@@ -94,7 +96,7 @@
 // - X86 (secondary)
 // - Arm64 on X86_64 (native bridge)
 // - Arm on X86 (native bridge)
-func withNativeBridgeEnabled(fs map[string][]byte, config android.Config) {
+func withNativeBridgeEnabled(_ map[string][]byte, config android.Config) {
 	config.Targets[android.Android] = []android.Target{
 		{Os: android.Android, Arch: android.Arch{ArchType: android.X86_64, ArchVariant: "silvermont", Abi: []string{"arm64-v8a"}},
 			NativeBridge: android.NativeBridgeDisabled, NativeBridgeHostArchName: "", NativeBridgeRelativePath: ""},
@@ -113,15 +115,15 @@
 	}
 }
 
-func withBinder32bit(fs map[string][]byte, config android.Config) {
+func withBinder32bit(_ map[string][]byte, config android.Config) {
 	config.TestProductVariables.Binder32bit = proptools.BoolPtr(true)
 }
 
-func withUnbundledBuild(fs map[string][]byte, config android.Config) {
+func withUnbundledBuild(_ map[string][]byte, config android.Config) {
 	config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true)
 }
 
-func testApexContext(t *testing.T, bp string, handlers ...testCustomizer) (*android.TestContext, android.Config) {
+func testApexContext(_ *testing.T, bp string, handlers ...testCustomizer) (*android.TestContext, android.Config) {
 	android.ClearApexDependency()
 
 	bp = bp + `
@@ -185,6 +187,7 @@
 		"baz":                                        nil,
 		"bar/baz":                                    nil,
 		"testdata/baz":                               nil,
+		"AppSet.apks":                                nil,
 	}
 
 	cc.GatherRequiredFilesForTest(fs)
@@ -230,9 +233,9 @@
 	ctx.RegisterModuleType("cc_test", cc.TestFactory)
 	ctx.RegisterModuleType("vndk_prebuilt_shared", cc.VndkPrebuiltSharedFactory)
 	ctx.RegisterModuleType("vndk_libraries_txt", cc.VndkLibrariesTxtFactory)
-	ctx.RegisterModuleType("prebuilt_etc", android.PrebuiltEtcFactory)
+	ctx.RegisterModuleType("prebuilt_etc", prebuilt_etc.PrebuiltEtcFactory)
 	ctx.RegisterModuleType("platform_compat_config", java.PlatformCompatConfigFactory)
-	ctx.RegisterModuleType("sh_binary", android.ShBinaryFactory)
+	ctx.RegisterModuleType("sh_binary", sh.ShBinaryFactory)
 	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
 	java.RegisterJavaBuildComponents(ctx)
 	java.RegisterSystemModulesBuildComponents(ctx)
@@ -256,7 +259,7 @@
 }
 
 func tearDown() {
-	os.RemoveAll(buildDir)
+	_ = os.RemoveAll(buildDir)
 }
 
 // ensure that 'result' equals 'expected'
@@ -292,6 +295,17 @@
 	}
 }
 
+func ensureMatches(t *testing.T, result string, expectedRex string) {
+	ok, err := regexp.MatchString(expectedRex, result)
+	if err != nil {
+		t.Fatalf("regexp failure trying to match %s against `%s` expression: %s", result, expectedRex, err)
+		return
+	}
+	if !ok {
+		t.Errorf("%s does not match regular expession %s", result, expectedRex)
+	}
+}
+
 func ensureListContains(t *testing.T, result []string, expected string) {
 	t.Helper()
 	if !android.InList(expected, result) {
@@ -315,7 +329,7 @@
 
 // Minimal test
 func TestBasicApex(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx, config := testApex(t, `
 		apex_defaults {
 			name: "myapex-defaults",
 			manifest: ":myapex.manifest",
@@ -327,7 +341,10 @@
 					binaries: ["foo",],
 				}
 			},
-			java_libs: ["myjar"],
+			java_libs: [
+				"myjar",
+				"myjar_dex",
+			],
 		}
 
 		apex {
@@ -424,6 +441,7 @@
 		java_library {
 			name: "myjar",
 			srcs: ["foo/bar/MyClass.java"],
+			stem: "myjar_stem",
 			sdk_version: "none",
 			system_modules: "none",
 			static_libs: ["myotherjar"],
@@ -435,6 +453,15 @@
 			],
 		}
 
+		dex_import {
+			name: "myjar_dex",
+			jars: ["prebuilt.jar"],
+			apex_available: [
+				"//apex_available:platform",
+				"myapex",
+			],
+		}
+
 		java_library {
 			name: "myotherjar",
 			srcs: ["foo/bar/MyClass.java"],
@@ -457,6 +484,16 @@
 
 	apexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexRule")
 
+	// Make sure that Android.mk is created
+	ab := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
+	data := android.AndroidMkDataForTest(t, config, "", ab)
+	var builder strings.Builder
+	data.Custom(&builder, ab.BaseModuleName(), "TARGET_", "", data)
+
+	androidMk := builder.String()
+	ensureContains(t, androidMk, "LOCAL_MODULE := mylib.myapex\n")
+	ensureNotContains(t, androidMk, "LOCAL_MODULE := mylib.com.android.myapex\n")
+
 	optFlags := apexRule.Args["opt_flags"]
 	ensureContains(t, optFlags, "--pubkey vendor/foo/devkeys/testkey.avbpubkey")
 	// Ensure that the NOTICE output is being packaged as an asset.
@@ -470,6 +507,7 @@
 	// Ensure that apex variant is created for the direct dep
 	ensureListContains(t, ctx.ModuleVariantsForTests("mylib"), "android_arm64_armv8-a_shared_myapex")
 	ensureListContains(t, ctx.ModuleVariantsForTests("myjar"), "android_common_myapex")
+	ensureListContains(t, ctx.ModuleVariantsForTests("myjar_dex"), "android_common_myapex")
 
 	// Ensure that apex variant is created for the indirect dep
 	ensureListContains(t, ctx.ModuleVariantsForTests("mylib2"), "android_arm64_armv8-a_shared_myapex")
@@ -478,7 +516,8 @@
 	// Ensure that both direct and indirect deps are copied into apex
 	ensureContains(t, copyCmds, "image.apex/lib64/mylib.so")
 	ensureContains(t, copyCmds, "image.apex/lib64/mylib2.so")
-	ensureContains(t, copyCmds, "image.apex/javalib/myjar.jar")
+	ensureContains(t, copyCmds, "image.apex/javalib/myjar_stem.jar")
+	ensureContains(t, copyCmds, "image.apex/javalib/myjar_dex.jar")
 	// .. but not for java libs
 	ensureNotContains(t, copyCmds, "image.apex/javalib/myotherjar.jar")
 	ensureNotContains(t, copyCmds, "image.apex/javalib/msharedjar.jar")
@@ -4755,6 +4794,38 @@
 	ensureContains(t, content, `"apex_config":{"apex_embedded_apk_config":[{"package_name":"com.android.foo","path":"app/AppFoo/AppFoo.apk"}]}`)
 }
 
+func TestAppSetBundle(t *testing.T) {
+	ctx, _ := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			apps: ["AppSet"],
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		android_app_set {
+			name: "AppSet",
+			set: "AppSet.apks",
+		}`)
+	mod := ctx.ModuleForTests("myapex", "android_common_myapex_image")
+	bundleConfigRule := mod.Description("Bundle Config")
+	content := bundleConfigRule.Args["content"]
+	ensureContains(t, content, `"compression":{"uncompressed_glob":["apex_payload.img","apex_manifest.*"]}`)
+	s := mod.Rule("apexRule").Args["copy_commands"]
+	copyCmds := regexp.MustCompile(" *&& *").Split(s, -1)
+	if len(copyCmds) != 3 {
+		t.Fatalf("Expected 3 commands, got %d in:\n%s", len(copyCmds), s)
+	}
+	ensureMatches(t, copyCmds[0], "^rm -rf .*/app/AppSet$")
+	ensureMatches(t, copyCmds[1], "^mkdir -p .*/app/AppSet$")
+	ensureMatches(t, copyCmds[2], "^unzip .*-d .*/app/AppSet .*/AppSet.zip$")
+}
+
 func testNoUpdatableJarsInBootImage(t *testing.T, errmsg string, transformDexpreoptConfig func(*dexpreopt.GlobalConfig)) {
 	t.Helper()
 
@@ -4914,7 +4985,7 @@
 
 func TestNoUpdatableJarsInBootImage(t *testing.T) {
 
-	var error string
+	var err string
 	var transform func(*dexpreopt.GlobalConfig)
 
 	t.Run("updatable jar from ART apex in the ART boot image => ok", func(t *testing.T) {
@@ -4925,35 +4996,35 @@
 	})
 
 	t.Run("updatable jar from ART apex in the framework boot image => error", func(t *testing.T) {
-		error = "module 'some-art-lib' from updatable apex 'com.android.art.something' is not allowed in the framework boot image"
+		err = "module 'some-art-lib' from updatable apex 'com.android.art.something' is not allowed in the framework boot image"
 		transform = func(config *dexpreopt.GlobalConfig) {
 			config.BootJars = []string{"com.android.art.something:some-art-lib"}
 		}
-		testNoUpdatableJarsInBootImage(t, error, transform)
+		testNoUpdatableJarsInBootImage(t, err, transform)
 	})
 
 	t.Run("updatable jar from some other apex in the ART boot image => error", func(t *testing.T) {
-		error = "module 'some-updatable-apex-lib' from updatable apex 'some-updatable-apex' is not allowed in the ART boot image"
+		err = "module 'some-updatable-apex-lib' from updatable apex 'some-updatable-apex' is not allowed in the ART boot image"
 		transform = func(config *dexpreopt.GlobalConfig) {
 			config.ArtApexJars = []string{"some-updatable-apex:some-updatable-apex-lib"}
 		}
-		testNoUpdatableJarsInBootImage(t, error, transform)
+		testNoUpdatableJarsInBootImage(t, err, transform)
 	})
 
 	t.Run("non-updatable jar from some other apex in the ART boot image => error", func(t *testing.T) {
-		error = "module 'some-non-updatable-apex-lib' is not allowed in the ART boot image"
+		err = "module 'some-non-updatable-apex-lib' is not allowed in the ART boot image"
 		transform = func(config *dexpreopt.GlobalConfig) {
 			config.ArtApexJars = []string{"some-non-updatable-apex:some-non-updatable-apex-lib"}
 		}
-		testNoUpdatableJarsInBootImage(t, error, transform)
+		testNoUpdatableJarsInBootImage(t, err, transform)
 	})
 
 	t.Run("updatable jar from some other apex in the framework boot image => error", func(t *testing.T) {
-		error = "module 'some-updatable-apex-lib' from updatable apex 'some-updatable-apex' is not allowed in the framework boot image"
+		err = "module 'some-updatable-apex-lib' from updatable apex 'some-updatable-apex' is not allowed in the framework boot image"
 		transform = func(config *dexpreopt.GlobalConfig) {
 			config.BootJars = []string{"some-updatable-apex:some-updatable-apex-lib"}
 		}
-		testNoUpdatableJarsInBootImage(t, error, transform)
+		testNoUpdatableJarsInBootImage(t, err, transform)
 	})
 
 	t.Run("non-updatable jar from some other apex in the framework boot image => ok", func(t *testing.T) {
@@ -4964,27 +5035,27 @@
 	})
 
 	t.Run("nonexistent jar in the ART boot image => error", func(t *testing.T) {
-		error = "failed to find a dex jar path for module 'nonexistent'"
+		err = "failed to find a dex jar path for module 'nonexistent'"
 		transform = func(config *dexpreopt.GlobalConfig) {
 			config.ArtApexJars = []string{"platform:nonexistent"}
 		}
-		testNoUpdatableJarsInBootImage(t, error, transform)
+		testNoUpdatableJarsInBootImage(t, err, transform)
 	})
 
 	t.Run("nonexistent jar in the framework boot image => error", func(t *testing.T) {
-		error = "failed to find a dex jar path for module 'nonexistent'"
+		err = "failed to find a dex jar path for module 'nonexistent'"
 		transform = func(config *dexpreopt.GlobalConfig) {
 			config.BootJars = []string{"platform:nonexistent"}
 		}
-		testNoUpdatableJarsInBootImage(t, error, transform)
+		testNoUpdatableJarsInBootImage(t, err, transform)
 	})
 
 	t.Run("platform jar in the ART boot image => error", func(t *testing.T) {
-		error = "module 'some-platform-lib' is not allowed in the ART boot image"
+		err = "module 'some-platform-lib' is not allowed in the ART boot image"
 		transform = func(config *dexpreopt.GlobalConfig) {
 			config.ArtApexJars = []string{"platform:some-platform-lib"}
 		}
-		testNoUpdatableJarsInBootImage(t, error, transform)
+		testNoUpdatableJarsInBootImage(t, err, transform)
 	})
 
 	t.Run("platform jar in the framework boot image => ok", func(t *testing.T) {
diff --git a/apex/builder.go b/apex/builder.go
index 11652bc..17eac1a 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -69,11 +69,14 @@
 	// by default set to (uid/gid/mode) = (1000/1000/0644)
 	// TODO(b/113082813) make this configurable using config.fs syntax
 	generateFsConfig = pctx.StaticRule("generateFsConfig", blueprint.RuleParams{
-		Command: `echo '/ 1000 1000 0755' > ${out} && ` +
-			`echo ${ro_paths} | tr ' ' '\n' | awk '{print "/"$$1 " 1000 1000 0644"}' >> ${out} && ` +
-			`echo ${exec_paths} | tr ' ' '\n' | awk '{print "/"$$1 " 0 2000 0755"}' >> ${out}`,
-		Description: "fs_config ${out}",
-	}, "ro_paths", "exec_paths")
+		Command: `( echo '/ 1000 1000 0755' ` +
+			`&& for i in ${ro_paths}; do echo "/$$i 1000 1000 0644"; done ` +
+			`&& for i in  ${exec_paths}; do echo "/$$i 0 2000 0755"; done ` +
+			`&& ( tr ' ' '\n' <${out}.apklist | for i in ${apk_paths}; do read apk; echo "/$$i 0 2000 0755"; zipinfo -1 $$apk | sed "s:\(.*\):/$$i/\1 1000 1000 0644:"; done ) ) > ${out}`,
+		Description:    "fs_config ${out}",
+		Rspfile:        "$out.apklist",
+		RspfileContent: "$in",
+	}, "ro_paths", "exec_paths", "apk_paths")
 
 	apexManifestRule = pctx.StaticRule("apexManifestRule", blueprint.RuleParams{
 		Command: `rm -f $out && ${jsonmodify} $in ` +
@@ -190,7 +193,7 @@
 	var jniLibs []string
 	for _, fi := range a.filesInfo {
 		if fi.isJniLib {
-			jniLibs = append(jniLibs, fi.builtFile.Base())
+			jniLibs = append(jniLibs, fi.Stem())
 		}
 	}
 	if len(jniLibs) > 0 {
@@ -247,7 +250,7 @@
 		return android.NoticeOutputs{}
 	}
 
-	return android.BuildNoticeOutput(ctx, a.installDir, apexFileName, android.FirstUniquePaths(noticeFiles))
+	return android.BuildNoticeOutput(ctx, a.installDir, apexFileName, android.SortedUniquePaths(noticeFiles))
 }
 
 func (a *apexBundle) buildInstalledFilesFile(ctx android.ModuleContext, builtApex android.Path, imageDir android.Path) android.OutputPath {
@@ -287,7 +290,7 @@
 	// collect the manifest names and paths of android apps
 	// if their manifest names are overridden
 	for _, fi := range a.filesInfo {
-		if fi.class != app {
+		if fi.class != app && fi.class != appSet {
 			continue
 		}
 		packageName := fi.overriddenPackageName
@@ -337,13 +340,22 @@
 	var copyCommands []string
 	for _, fi := range a.filesInfo {
 		destPath := android.PathForModuleOut(ctx, "image"+suffix, fi.Path()).String()
-		copyCommands = append(copyCommands, "mkdir -p "+filepath.Dir(destPath))
+		destPathDir := filepath.Dir(destPath)
+		if fi.class == appSet {
+			copyCommands = append(copyCommands, "rm -rf "+destPathDir)
+		}
+		copyCommands = append(copyCommands, "mkdir -p "+destPathDir)
 		if a.linkToSystemLib && fi.transitiveDep && fi.AvailableToPlatform() {
 			// TODO(jiyong): pathOnDevice should come from fi.module, not being calculated here
 			pathOnDevice := filepath.Join("/system", fi.Path())
 			copyCommands = append(copyCommands, "ln -sfn "+pathOnDevice+" "+destPath)
 		} else {
-			copyCommands = append(copyCommands, "cp -f "+fi.builtFile.String()+" "+destPath)
+			if fi.class == appSet {
+				copyCommands = append(copyCommands,
+					fmt.Sprintf("unzip -q -d %s %s", destPathDir, fi.builtFile.String()))
+			} else {
+				copyCommands = append(copyCommands, "cp -f "+fi.builtFile.String()+" "+destPath)
+			}
 			implicitInputs = append(implicitInputs, fi.builtFile)
 		}
 		// create additional symlinks pointing the file inside the APEX
@@ -416,8 +428,10 @@
 		// files and dirs that will be created in APEX
 		var readOnlyPaths = []string{"apex_manifest.json", "apex_manifest.pb"}
 		var executablePaths []string // this also includes dirs
+		var extractedAppSetPaths android.Paths
+		var extractedAppSetDirs []string
 		for _, f := range a.filesInfo {
-			pathInApex := filepath.Join(f.installDir, f.builtFile.Base())
+			pathInApex := f.Path()
 			if f.installDir == "bin" || strings.HasPrefix(f.installDir, "bin/") {
 				executablePaths = append(executablePaths, pathInApex)
 				for _, d := range f.dataPaths {
@@ -426,6 +440,9 @@
 				for _, s := range f.symlinks {
 					executablePaths = append(executablePaths, filepath.Join(f.installDir, s))
 				}
+			} else if f.class == appSet {
+				extractedAppSetPaths = append(extractedAppSetPaths, f.builtFile)
+				extractedAppSetDirs = append(extractedAppSetDirs, f.installDir)
 			} else {
 				readOnlyPaths = append(readOnlyPaths, pathInApex)
 			}
@@ -446,9 +463,11 @@
 			Rule:        generateFsConfig,
 			Output:      cannedFsConfig,
 			Description: "generate fs config",
+			Inputs:      extractedAppSetPaths,
 			Args: map[string]string{
 				"ro_paths":   strings.Join(readOnlyPaths, " "),
 				"exec_paths": strings.Join(executablePaths, " "),
+				"apk_paths":  strings.Join(extractedAppSetDirs, " "),
 			},
 		})
 
@@ -582,19 +601,27 @@
 	}
 
 	a.outputFile = android.PathForModuleOut(ctx, a.Name()+suffix)
+	rule := java.Signapk
+	args := map[string]string{
+		"certificates": a.container_certificate_file.String() + " " + a.container_private_key_file.String(),
+		"flags":        "-a 4096", //alignment
+	}
+	implicits := android.Paths{
+		a.container_certificate_file,
+		a.container_private_key_file,
+	}
+	if ctx.Config().IsEnvTrue("RBE_SIGNAPK") {
+		rule = java.SignapkRE
+		args["implicits"] = strings.Join(implicits.Strings(), ",")
+		args["outCommaList"] = a.outputFile.String()
+	}
 	ctx.Build(pctx, android.BuildParams{
-		Rule:        java.Signapk,
+		Rule:        rule,
 		Description: "signapk",
 		Output:      a.outputFile,
 		Input:       unsignedOutputFile,
-		Implicits: []android.Path{
-			a.container_certificate_file,
-			a.container_private_key_file,
-		},
-		Args: map[string]string{
-			"certificates": a.container_certificate_file.String() + " " + a.container_private_key_file.String(),
-			"flags":        "-a 4096", //alignment
-		},
+		Implicits:   implicits,
+		Args:        args,
 	})
 
 	// Install to $OUT/soong/{target,host}/.../apex
@@ -659,7 +686,7 @@
 			apexBundleName := a.Name()
 			for _, fi := range a.filesInfo {
 				dir := filepath.Join("apex", apexBundleName, fi.installDir)
-				target := ctx.InstallFile(android.PathForModuleInstall(ctx, dir), fi.builtFile.Base(), fi.builtFile)
+				target := ctx.InstallFile(android.PathForModuleInstall(ctx, dir), fi.Stem(), fi.builtFile)
 				for _, sym := range fi.symlinks {
 					ctx.InstallSymlink(android.PathForModuleInstall(ctx, dir), sym, target)
 				}
diff --git a/cc/Android.bp b/cc/Android.bp
new file mode 100644
index 0000000..9ece05f
--- /dev/null
+++ b/cc/Android.bp
@@ -0,0 +1,87 @@
+bootstrap_go_package {
+    name: "soong-cc",
+    pkgPath: "android/soong/cc",
+    deps: [
+        "blueprint",
+        "blueprint-pathtools",
+        "soong",
+        "soong-android",
+        "soong-cc-config",
+        "soong-etc",
+        "soong-genrule",
+        "soong-tradefed",
+    ],
+    srcs: [
+        "androidmk.go",
+        "builder.go",
+        "cc.go",
+        "ccdeps.go",
+        "check.go",
+        "coverage.go",
+        "gen.go",
+        "linkable.go",
+        "lto.go",
+        "makevars.go",
+        "pgo.go",
+        "prebuilt.go",
+        "proto.go",
+        "rs.go",
+        "sanitize.go",
+        "sabi.go",
+        "sdk.go",
+        "snapshot_utils.go",
+        "stl.go",
+        "strip.go",
+        "sysprop.go",
+        "tidy.go",
+        "util.go",
+        "vendor_snapshot.go",
+        "vndk.go",
+        "vndk_prebuilt.go",
+
+        "cflag_artifacts.go",
+        "cmakelists.go",
+        "compdb.go",
+        "compiler.go",
+        "installer.go",
+        "linker.go",
+
+        "binary.go",
+        "binary_sdk_member.go",
+        "fuzz.go",
+        "library.go",
+        "library_headers.go",
+        "library_sdk_member.go",
+        "object.go",
+        "test.go",
+        "toolchain_library.go",
+
+        "ndk_prebuilt.go",
+        "ndk_headers.go",
+        "ndk_library.go",
+        "ndk_sysroot.go",
+
+        "llndk_library.go",
+
+        "kernel_headers.go",
+
+        "genrule.go",
+
+        "vendor_public_library.go",
+
+        "testing.go",
+    ],
+    testSrcs: [
+        "cc_test.go",
+        "compiler_test.go",
+        "gen_test.go",
+        "genrule_test.go",
+        "library_headers_test.go",
+        "library_test.go",
+        "object_test.go",
+        "prebuilt_test.go",
+        "proto_test.go",
+        "test_data_test.go",
+    ],
+    pluginFor: ["soong_build"],
+}
diff --git a/cc/androidmk.go b/cc/androidmk.go
index 3ce1a89..52480ea 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -548,6 +548,25 @@
 	})
 }
 
+func (c *vendorSnapshotObjectLinker) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
+	entries.Class = "STATIC_LIBRARIES"
+
+	if c.androidMkVendorSuffix {
+		entries.SubName = vendorSuffix
+	} else {
+		entries.SubName = ""
+	}
+
+	entries.ExtraFooters = append(entries.ExtraFooters,
+		func(w io.Writer, name, prefix, moduleDir string, entries *android.AndroidMkEntries) {
+			out := entries.OutputFile.Path()
+			varname := fmt.Sprintf("SOONG_%sOBJECT_%s%s", prefix, name, entries.SubName)
+
+			fmt.Fprintf(w, "\n%s := %s\n", varname, out.String())
+			fmt.Fprintln(w, ".KATI_READONLY: "+varname)
+		})
+}
+
 func (c *ndkPrebuiltStlLinker) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
 	entries.Class = "SHARED_LIBRARIES"
 }
diff --git a/cc/binary.go b/cc/binary.go
index 661264e..251b7f0 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -231,6 +231,10 @@
 	return binary.static()
 }
 
+func (binary *binaryDecorator) binary() bool {
+	return true
+}
+
 func (binary *binaryDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
 	flags = binary.baseLinker.linkerFlags(ctx, flags)
 
diff --git a/cc/binary_sdk_member.go b/cc/binary_sdk_member.go
index 88ac513..372a72e 100644
--- a/cc/binary_sdk_member.go
+++ b/cc/binary_sdk_member.go
@@ -20,6 +20,7 @@
 	"android/soong/android"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
 )
 
 func init() {
@@ -65,7 +66,15 @@
 }
 
 func (mt *binarySdkMemberType) AddPrebuiltModule(ctx android.SdkMemberContext, member android.SdkMember) android.BpModule {
-	return ctx.SnapshotBuilder().AddPrebuiltModule(member, "cc_prebuilt_binary")
+	pbm := ctx.SnapshotBuilder().AddPrebuiltModule(member, "cc_prebuilt_binary")
+
+	ccModule := member.Variants()[0].(*Module)
+
+	if stl := ccModule.stl.Properties.Stl; stl != nil {
+		pbm.AddProperty("stl", proptools.String(stl))
+	}
+
+	return pbm
 }
 
 func (mt *binarySdkMemberType) CreateVariantPropertiesStruct() android.SdkMemberProperties {
@@ -105,6 +114,10 @@
 	//
 	// This field is exported as its contents may not be arch specific.
 	SystemSharedLibs []string
+
+	// Arch specific flags.
+	StaticExecutable bool
+	Nocrt            bool
 }
 
 func (p *nativeBinaryInfoProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) {
@@ -113,6 +126,10 @@
 	p.archType = ccModule.Target().Arch.ArchType.String()
 	p.outputFile = getRequiredMemberOutputFile(ctx, ccModule)
 
+	binaryLinker := ccModule.linker.(*binaryDecorator)
+	p.StaticExecutable = binaryLinker.static()
+	p.Nocrt = Bool(binaryLinker.baseLinker.Properties.Nocrt)
+
 	if ccModule.linker != nil {
 		specifiedDeps := specifiedDeps{}
 		specifiedDeps = ccModule.linker.linkerSpecifiedDeps(specifiedDeps)
@@ -143,4 +160,11 @@
 	if p.SystemSharedLibs != nil {
 		propertySet.AddPropertyWithTag("system_shared_libs", p.SystemSharedLibs, builder.SdkMemberReferencePropertyTag(false))
 	}
+
+	if p.StaticExecutable {
+		propertySet.AddProperty("static_executable", p.StaticExecutable)
+	}
+	if p.Nocrt {
+		propertySet.AddProperty("nocrt", p.Nocrt)
+	}
 }
diff --git a/cc/cc.go b/cc/cc.go
index 94512d5..770391a 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -316,6 +316,8 @@
 	static() bool
 	staticBinary() bool
 	header() bool
+	binary() bool
+	object() bool
 	toolchain() config.Toolchain
 	canUseSdk() bool
 	useSdk() bool
@@ -823,15 +825,8 @@
 	}
 
 	c.Prefer32(func(ctx android.BaseModuleContext, base *android.ModuleBase, class android.OsClass) bool {
-		switch class {
-		case android.Device:
-			return ctx.Config().DevicePrefer32BitExecutables()
-		case android.HostCross:
-			// Windows builds always prefer 32-bit
-			return true
-		default:
-			return false
-		}
+		// Windows builds always prefer 32-bit
+		return class == android.HostCross
 	})
 	android.InitAndroidArchModule(c, c.hod, c.multilib)
 	android.InitApexModule(c)
@@ -1017,14 +1012,8 @@
 }
 
 func (c *Module) isSnapshotPrebuilt() bool {
-	if _, ok := c.linker.(*vndkPrebuiltLibraryDecorator); ok {
-		return true
-	}
-	if _, ok := c.linker.(*vendorSnapshotLibraryDecorator); ok {
-		return true
-	}
-	if _, ok := c.linker.(*vendorSnapshotBinaryDecorator); ok {
-		return true
+	if p, ok := c.linker.(interface{ isSnapshotPrebuilt() bool }); ok {
+		return p.isSnapshotPrebuilt()
 	}
 	return false
 }
@@ -1129,6 +1118,14 @@
 	return ctx.mod.header()
 }
 
+func (ctx *moduleContextImpl) binary() bool {
+	return ctx.mod.binary()
+}
+
+func (ctx *moduleContextImpl) object() bool {
+	return ctx.mod.object()
+}
+
 func (ctx *moduleContextImpl) canUseSdk() bool {
 	return ctx.mod.canUseSdk()
 }
@@ -1925,7 +1922,7 @@
 	if deps.StaticUnwinderIfLegacy {
 		actx.AddVariationDependencies([]blueprint.Variation{
 			{Mutator: "link", Variation: "static"},
-		}, staticUnwinderDepTag, staticUnwinder(actx))
+		}, staticUnwinderDepTag, rewriteSnapshotLibs(staticUnwinder(actx), vendorSnapshotStaticLibs))
 	}
 
 	for _, lib := range deps.LateStaticLibs {
@@ -2006,11 +2003,13 @@
 
 	actx.AddVariationDependencies(nil, objDepTag, deps.ObjFiles...)
 
+	vendorSnapshotObjects := vendorSnapshotObjects(actx.Config())
+
 	if deps.CrtBegin != "" {
-		actx.AddVariationDependencies(nil, CrtBeginDepTag, deps.CrtBegin)
+		actx.AddVariationDependencies(nil, CrtBeginDepTag, rewriteSnapshotLibs(deps.CrtBegin, vendorSnapshotObjects))
 	}
 	if deps.CrtEnd != "" {
-		actx.AddVariationDependencies(nil, CrtEndDepTag, deps.CrtEnd)
+		actx.AddVariationDependencies(nil, CrtEndDepTag, rewriteSnapshotLibs(deps.CrtEnd, vendorSnapshotObjects))
 	}
 	if deps.LinkerFlagsFile != "" {
 		actx.AddDependency(c, linkerFlagsDepTag, deps.LinkerFlagsFile)
@@ -2752,6 +2751,24 @@
 	return false
 }
 
+func (c *Module) binary() bool {
+	if b, ok := c.linker.(interface {
+		binary() bool
+	}); ok {
+		return b.binary()
+	}
+	return false
+}
+
+func (c *Module) object() bool {
+	if o, ok := c.linker.(interface {
+		object() bool
+	}); ok {
+		return o.object()
+	}
+	return false
+}
+
 func (c *Module) getMakeLinkType(actx android.ModuleContext) string {
 	if c.UseVndk() {
 		if lib, ok := c.linker.(*llndkStubDecorator); ok {
@@ -3064,20 +3081,32 @@
 		// This will be available in /system, /vendor and /product
 		// or a /system directory that is available to vendor and product.
 		coreVariantNeeded = true
-		vendorVariants = append(vendorVariants, platformVndkVersion)
-		productVariants = append(productVariants, platformVndkVersion)
-		// VNDK modules must not create BOARD_VNDK_VERSION variant because its
-		// code is PLATFORM_VNDK_VERSION.
-		// On the other hand, vendor_available modules which are not VNDK should
-		// also build BOARD_VNDK_VERSION because it's installed in /vendor.
-		// vendor_available modules are also available to /product.
-		if !m.IsVndk() {
+
+		// We assume that modules under proprietary paths are compatible for
+		// BOARD_VNDK_VERSION. The other modules are regarded as AOSP, or
+		// PLATFORM_VNDK_VERSION.
+		if isVendorProprietaryPath(mctx.ModuleDir()) {
 			vendorVariants = append(vendorVariants, boardVndkVersion)
+		} else {
+			vendorVariants = append(vendorVariants, platformVndkVersion)
+		}
+
+		// vendor_available modules are also available to /product.
+		productVariants = append(productVariants, platformVndkVersion)
+		// VNDK is always PLATFORM_VNDK_VERSION
+		if !m.IsVndk() {
 			productVariants = append(productVariants, productVndkVersion)
 		}
 	} else if vendorSpecific && String(m.Properties.Sdk_version) == "" {
 		// This will be available in /vendor (or /odm) only
-		vendorVariants = append(vendorVariants, boardVndkVersion)
+		// We assume that modules under proprietary paths are compatible for
+		// BOARD_VNDK_VERSION. The other modules are regarded as AOSP, or
+		// PLATFORM_VNDK_VERSION.
+		if isVendorProprietaryPath(mctx.ModuleDir()) {
+			vendorVariants = append(vendorVariants, boardVndkVersion)
+		} else {
+			vendorVariants = append(vendorVariants, platformVndkVersion)
+		}
 	} else {
 		// This is either in /system (or similar: /data), or is a
 		// modules built with the NDK. Modules built with the NDK
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 67eb248..76b4e38 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -258,9 +258,7 @@
 	}
 }
 
-func checkSnapshot(t *testing.T, ctx *android.TestContext, singletonName, moduleName, snapshotFilename, subDir, variant string) {
-	snapshotSingleton := ctx.SingletonForTests(singletonName)
-
+func checkSnapshot(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, moduleName, snapshotFilename, subDir, variant string) {
 	mod, ok := ctx.ModuleForTests(moduleName, variant).Module().(android.OutputFileProducer)
 	if !ok {
 		t.Errorf("%q must have output\n", moduleName)
@@ -273,7 +271,7 @@
 	}
 	snapshotPath := filepath.Join(subDir, snapshotFilename)
 
-	out := snapshotSingleton.Output(snapshotPath)
+	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])
 	}
@@ -398,16 +396,18 @@
 	variant := "android_vendor.VER_arm64_armv8-a_shared"
 	variant2nd := "android_vendor.VER_arm_armv7-a-neon_shared"
 
-	checkSnapshot(t, ctx, "vndk-snapshot", "libvndk", "libvndk.so", vndkCoreLibPath, variant)
-	checkSnapshot(t, ctx, "vndk-snapshot", "libvndk", "libvndk.so", vndkCoreLib2ndPath, variant2nd)
-	checkSnapshot(t, ctx, "vndk-snapshot", "libvndk_sp", "libvndk_sp-x.so", vndkSpLibPath, variant)
-	checkSnapshot(t, ctx, "vndk-snapshot", "libvndk_sp", "libvndk_sp-x.so", vndkSpLib2ndPath, variant2nd)
+	snapshotSingleton := ctx.SingletonForTests("vndk-snapshot")
+
+	checkSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.so", vndkCoreLibPath, variant)
+	checkSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.so", vndkCoreLib2ndPath, variant2nd)
+	checkSnapshot(t, ctx, snapshotSingleton, "libvndk_sp", "libvndk_sp-x.so", vndkSpLibPath, variant)
+	checkSnapshot(t, ctx, snapshotSingleton, "libvndk_sp", "libvndk_sp-x.so", vndkSpLib2ndPath, variant2nd)
 
 	snapshotConfigsPath := filepath.Join(snapshotVariantPath, "configs")
-	checkSnapshot(t, ctx, "vndk-snapshot", "llndk.libraries.txt", "llndk.libraries.txt", snapshotConfigsPath, "")
-	checkSnapshot(t, ctx, "vndk-snapshot", "vndkcore.libraries.txt", "vndkcore.libraries.txt", snapshotConfigsPath, "")
-	checkSnapshot(t, ctx, "vndk-snapshot", "vndksp.libraries.txt", "vndksp.libraries.txt", snapshotConfigsPath, "")
-	checkSnapshot(t, ctx, "vndk-snapshot", "vndkprivate.libraries.txt", "vndkprivate.libraries.txt", snapshotConfigsPath, "")
+	checkSnapshot(t, ctx, snapshotSingleton, "llndk.libraries.txt", "llndk.libraries.txt", snapshotConfigsPath, "")
+	checkSnapshot(t, ctx, snapshotSingleton, "vndkcore.libraries.txt", "vndkcore.libraries.txt", snapshotConfigsPath, "")
+	checkSnapshot(t, ctx, snapshotSingleton, "vndksp.libraries.txt", "vndksp.libraries.txt", snapshotConfigsPath, "")
+	checkSnapshot(t, ctx, snapshotSingleton, "vndkprivate.libraries.txt", "vndkprivate.libraries.txt", snapshotConfigsPath, "")
 
 	checkVndkOutput(t, ctx, "vndk/vndk.libraries.txt", []string{
 		"LLNDK: libc.so",
@@ -839,6 +839,17 @@
 		vendor_available: true,
 		nocrt: true,
 	}
+
+	toolchain_library {
+		name: "libb",
+		vendor_available: true,
+		src: "libb.a",
+	}
+
+	cc_object {
+		name: "obj",
+		vendor_available: true,
+	}
 `
 	config := TestConfig(buildDir, android.Android, nil, bp, nil)
 	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
@@ -849,6 +860,9 @@
 
 	snapshotDir := "vendor-snapshot"
 	snapshotVariantPath := filepath.Join(buildDir, snapshotDir, "arm64")
+	snapshotSingleton := ctx.SingletonForTests("vendor-snapshot")
+
+	var jsonFiles []string
 
 	for _, arch := range [][]string{
 		[]string{"arm64", "armv8-a"},
@@ -861,22 +875,51 @@
 		// For shared libraries, only non-VNDK vendor_available modules are captured
 		sharedVariant := fmt.Sprintf("android_vendor.VER_%s_%s_shared", archType, archVariant)
 		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
-		checkSnapshot(t, ctx, "vendor-snapshot", "libvendor", "libvendor.so", sharedDir, sharedVariant)
-		checkSnapshot(t, ctx, "vendor-snapshot", "libvendor_available", "libvendor_available.so", sharedDir, sharedVariant)
+		checkSnapshot(t, ctx, snapshotSingleton, "libvendor", "libvendor.so", sharedDir, sharedVariant)
+		checkSnapshot(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.so", sharedDir, sharedVariant)
+		jsonFiles = append(jsonFiles,
+			filepath.Join(sharedDir, "libvendor.so.json"),
+			filepath.Join(sharedDir, "libvendor_available.so.json"))
 
 		// For static libraries, all vendor:true and vendor_available modules (including VNDK) are captured.
 		staticVariant := fmt.Sprintf("android_vendor.VER_%s_%s_static", archType, archVariant)
 		staticDir := filepath.Join(snapshotVariantPath, archDir, "static")
-		checkSnapshot(t, ctx, "vendor-snapshot", "libvndk", "libvndk.a", staticDir, staticVariant)
-		checkSnapshot(t, ctx, "vendor-snapshot", "libvendor", "libvendor.a", staticDir, staticVariant)
-		checkSnapshot(t, ctx, "vendor-snapshot", "libvendor_available", "libvendor_available.a", staticDir, staticVariant)
+		checkSnapshot(t, ctx, snapshotSingleton, "libb", "libb.a", staticDir, staticVariant)
+		checkSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.a", staticDir, staticVariant)
+		checkSnapshot(t, ctx, snapshotSingleton, "libvendor", "libvendor.a", staticDir, staticVariant)
+		checkSnapshot(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.a", staticDir, staticVariant)
+		jsonFiles = append(jsonFiles,
+			filepath.Join(staticDir, "libb.a.json"),
+			filepath.Join(staticDir, "libvndk.a.json"),
+			filepath.Join(staticDir, "libvendor.a.json"),
+			filepath.Join(staticDir, "libvendor_available.a.json"))
 
-		// For binary libraries, all vendor:true and vendor_available modules are captured.
+		// For binary executables, all vendor:true and vendor_available modules are captured.
 		if archType == "arm64" {
 			binaryVariant := fmt.Sprintf("android_vendor.VER_%s_%s", archType, archVariant)
 			binaryDir := filepath.Join(snapshotVariantPath, archDir, "binary")
-			checkSnapshot(t, ctx, "vendor-snapshot", "vendor_bin", "vendor_bin", binaryDir, binaryVariant)
-			checkSnapshot(t, ctx, "vendor-snapshot", "vendor_available_bin", "vendor_available_bin", binaryDir, binaryVariant)
+			checkSnapshot(t, ctx, snapshotSingleton, "vendor_bin", "vendor_bin", binaryDir, binaryVariant)
+			checkSnapshot(t, ctx, snapshotSingleton, "vendor_available_bin", "vendor_available_bin", binaryDir, binaryVariant)
+			jsonFiles = append(jsonFiles,
+				filepath.Join(binaryDir, "vendor_bin.json"),
+				filepath.Join(binaryDir, "vendor_available_bin.json"))
+		}
+
+		// For header libraries, all vendor:true and vendor_available modules are captured.
+		headerDir := filepath.Join(snapshotVariantPath, archDir, "header")
+		jsonFiles = append(jsonFiles, filepath.Join(headerDir, "libvendor_headers.json"))
+
+		// For object modules, all vendor:true and vendor_available modules are captured.
+		objectVariant := fmt.Sprintf("android_vendor.VER_%s_%s", archType, archVariant)
+		objectDir := filepath.Join(snapshotVariantPath, archDir, "object")
+		checkSnapshot(t, ctx, snapshotSingleton, "obj", "obj.o", objectDir, objectVariant)
+		jsonFiles = append(jsonFiles, filepath.Join(objectDir, "obj.o.json"))
+	}
+
+	for _, jsonFile := range jsonFiles {
+		// verify all json files exist
+		if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
+			t.Errorf("%q expected but not found", jsonFile)
 		}
 	}
 }
diff --git a/cc/compiler.go b/cc/compiler.go
index 681b1ab..e7495da 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -300,8 +300,7 @@
 	if !(ctx.useSdk() || ctx.useVndk()) || ctx.Host() {
 		flags.SystemIncludeFlags = append(flags.SystemIncludeFlags,
 			"${config.CommonGlobalIncludes}",
-			tc.IncludeFlags(),
-			"${config.CommonNativehelperInclude}")
+			tc.IncludeFlags())
 	}
 
 	if ctx.useSdk() {
diff --git a/cc/config/Android.bp b/cc/config/Android.bp
new file mode 100644
index 0000000..6275064
--- /dev/null
+++ b/cc/config/Android.bp
@@ -0,0 +1,30 @@
+bootstrap_go_package {
+    name: "soong-cc-config",
+    pkgPath: "android/soong/cc/config",
+    deps: [
+        "soong-android",
+        "soong-remoteexec",
+    ],
+    srcs: [
+        "clang.go",
+        "global.go",
+        "tidy.go",
+        "toolchain.go",
+        "vndk.go",
+
+        "arm_device.go",
+        "arm64_device.go",
+        "arm64_fuchsia_device.go",
+        "x86_device.go",
+        "x86_64_device.go",
+        "x86_64_fuchsia_device.go",
+
+        "x86_darwin_host.go",
+        "x86_linux_host.go",
+        "x86_linux_bionic_host.go",
+        "x86_windows_host.go",
+    ],
+    testSrcs: [
+        "tidy_test.go",
+    ],
+}
diff --git a/cc/config/global.go b/cc/config/global.go
index 1dd8a2d..7b651bc 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -128,8 +128,8 @@
 
 	// prebuilts/clang default settings.
 	ClangDefaultBase         = "prebuilts/clang/host"
-	ClangDefaultVersion      = "clang-r383902"
-	ClangDefaultShortVersion = "11.0.1"
+	ClangDefaultVersion      = "clang-r383902b"
+	ClangDefaultShortVersion = "11.0.2"
 
 	// Directories with warnings from Android.bp files.
 	WarningAllowedProjects = []string{
diff --git a/cc/genrule.go b/cc/genrule.go
index 9331448..66d1784 100644
--- a/cc/genrule.go
+++ b/cc/genrule.go
@@ -79,8 +79,14 @@
 
 	var variants []string
 	if Bool(g.Vendor_available) || ctx.SocSpecific() || ctx.DeviceSpecific() {
-		variants = append(variants, VendorVariationPrefix+ctx.DeviceConfig().PlatformVndkVersion())
-		if vndkVersion := ctx.DeviceConfig().VndkVersion(); vndkVersion != "current" {
+		vndkVersion := ctx.DeviceConfig().VndkVersion()
+		// If vndkVersion is current, we can always use PlatformVndkVersion.
+		// If not, we assume modules under proprietary paths are compatible for
+		// BOARD_VNDK_VERSION. The other modules are regarded as AOSP, that is
+		// PLATFORM_VNDK_VERSION.
+		if vndkVersion == "current" || !isVendorProprietaryPath(ctx.ModuleDir()) {
+			variants = append(variants, VendorVariationPrefix+ctx.DeviceConfig().PlatformVndkVersion())
+		} else {
 			variants = append(variants, VendorVariationPrefix+vndkVersion)
 		}
 	}
diff --git a/cc/library.go b/cc/library.go
index 47485ce..ba8b0f4 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -407,6 +407,31 @@
 		if strings.HasPrefix(dir, android.PathForOutput(ctx).String()) {
 			continue
 		}
+		// libeigen wrongly exports the root directory "external/eigen". But only two
+		// subdirectories "Eigen" and "unsupported" contain exported header files. Even worse
+		// some of them have no extension. So we need special treatment for libeigen in order
+		// to glob correctly.
+		if dir == "external/eigen" {
+			// Only these two directories contains exported headers.
+			for _, subdir := range []string{"Eigen", "unsupported/Eigen"} {
+				glob, err := ctx.GlobWithDeps("external/eigen/"+subdir+"/**/*", nil)
+				if err != nil {
+					ctx.ModuleErrorf("glob failed: %#v", err)
+					return
+				}
+				for _, header := range glob {
+					if strings.HasSuffix(header, "/") {
+						continue
+					}
+					ext := filepath.Ext(header)
+					if ext != "" && ext != ".h" {
+						continue
+					}
+					ret = append(ret, android.PathForSource(ctx, header))
+				}
+			}
+			continue
+		}
 		exts := headerExts
 		// Glob all files under this special directory, because of C++ headers.
 		if strings.HasPrefix(dir, "external/libcxx/include") {
diff --git a/cc/linker.go b/cc/linker.go
index 57a0c01..c9cbd9b 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -177,7 +177,10 @@
 	Version_script *string `android:"path,arch_variant"`
 
 	// list of static libs that should not be used to build this module
-	Exclude_static_libs []string
+	Exclude_static_libs []string `android:"arch_variant"`
+
+	// list of shared libs that should not be used to build this module
+	Exclude_shared_libs []string `android:"arch_variant"`
 }
 
 func NewBaseLinker(sanitize *sanitize) *baseLinker {
@@ -223,6 +226,8 @@
 	deps.ReexportSharedLibHeaders = append(deps.ReexportSharedLibHeaders, linker.Properties.Export_shared_lib_headers...)
 	deps.ReexportGeneratedHeaders = append(deps.ReexportGeneratedHeaders, linker.Properties.Export_generated_headers...)
 
+	deps.SharedLibs = removeListFromList(deps.SharedLibs, linker.Properties.Exclude_shared_libs)
+	deps.StaticLibs = removeListFromList(deps.StaticLibs, linker.Properties.Exclude_static_libs)
 	deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Exclude_static_libs)
 
 	if Bool(linker.Properties.Use_version_lib) {
diff --git a/cc/object.go b/cc/object.go
index 19decec..15a529e 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -158,3 +158,7 @@
 func (object *objectLinker) coverageOutputFilePath() android.OptionalPath {
 	return android.OptionalPath{}
 }
+
+func (object *objectLinker) object() bool {
+	return true
+}
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index ac990f3..0751f1c 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -291,6 +291,10 @@
 	return nil
 }
 
+func (p *prebuiltObjectLinker) object() bool {
+	return true
+}
+
 func newPrebuiltObject() *Module {
 	module := newObject()
 	prebuilt := &prebuiltObjectLinker{
@@ -349,6 +353,10 @@
 	return nil
 }
 
+func (p *prebuiltBinaryLinker) binary() bool {
+	return true
+}
+
 // cc_prebuilt_binary installs a precompiled executable in srcs property in the
 // device's directory.
 func prebuiltBinaryFactory() android.Module {
diff --git a/cc/sdk.go b/cc/sdk.go
index d05a04a..a6f94af 100644
--- a/cc/sdk.go
+++ b/cc/sdk.go
@@ -43,6 +43,7 @@
 
 			if ctx.Config().UnbundledBuild() {
 				modules[0].(*Module).Properties.HideFromMake = true
+				modules[0].(*Module).Properties.PreventInstall = true
 			} else {
 				modules[1].(*Module).Properties.SdkAndPlatformVariantVisibleToMake = true
 				modules[1].(*Module).Properties.PreventInstall = true
diff --git a/cc/testing.go b/cc/testing.go
index be020c5..edbb24d 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -496,6 +496,7 @@
 		"my_include":  nil,
 		"foo.map.txt": nil,
 		"liba.so":     nil,
+		"libb.a":      nil,
 	}
 
 	GatherRequiredFilesForTest(mockFS)
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index 5801fc7..ea94544 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -30,6 +30,7 @@
 	vendorSnapshotSharedSuffix = ".vendor_shared."
 	vendorSnapshotStaticSuffix = ".vendor_static."
 	vendorSnapshotBinarySuffix = ".vendor_binary."
+	vendorSnapshotObjectSuffix = ".vendor_object."
 )
 
 var (
@@ -39,6 +40,7 @@
 	vendorSnapshotStaticLibsKey = android.NewOnceKey("vendorSnapshotStaticLibs")
 	vendorSnapshotSharedLibsKey = android.NewOnceKey("vendorSnapshotSharedLibs")
 	vendorSnapshotBinariesKey   = android.NewOnceKey("vendorSnapshotBinaries")
+	vendorSnapshotObjectsKey    = android.NewOnceKey("vendorSnapshotObjects")
 )
 
 // vendor snapshot maps hold names of vendor snapshot modules per arch
@@ -72,6 +74,12 @@
 	}).(*snapshotMap)
 }
 
+func vendorSnapshotObjects(config android.Config) *snapshotMap {
+	return config.Once(vendorSnapshotObjectsKey, func() interface{} {
+		return newSnapshotMap()
+	}).(*snapshotMap)
+}
+
 type vendorSnapshotLibraryProperties struct {
 	// snapshot version.
 	Version string
@@ -185,6 +193,10 @@
 	return false
 }
 
+func (p *vendorSnapshotLibraryDecorator) isSnapshotPrebuilt() bool {
+	return true
+}
+
 func (p *vendorSnapshotLibraryDecorator) install(ctx ModuleContext, file android.Path) {
 	if p.matchesWithDevice(ctx.DeviceConfig()) && (p.shared() || p.static()) {
 		p.baseInstaller.install(ctx, file)
@@ -337,6 +349,10 @@
 	return outputFile
 }
 
+func (p *vendorSnapshotBinaryDecorator) isSnapshotPrebuilt() bool {
+	return true
+}
+
 func VendorSnapshotBinaryFactory() android.Module {
 	module, binary := NewBinary(android.DeviceSupported)
 	binary.baseLinker.Properties.No_libcrt = BoolPtr(true)
@@ -364,12 +380,98 @@
 	return module.Init()
 }
 
+type vendorSnapshotObjectProperties struct {
+	// snapshot version.
+	Version string
+
+	// Target arch name of the snapshot (e.g. 'arm64' for variant 'aosp_arm64_ab')
+	Target_arch string
+
+	// Prebuilt file for each arch.
+	Src *string `android:"arch_variant"`
+}
+
+type vendorSnapshotObjectLinker struct {
+	objectLinker
+	properties            vendorSnapshotObjectProperties
+	androidMkVendorSuffix bool
+}
+
+func (p *vendorSnapshotObjectLinker) Name(name string) string {
+	return name + p.NameSuffix()
+}
+
+func (p *vendorSnapshotObjectLinker) NameSuffix() string {
+	versionSuffix := p.version()
+	if p.arch() != "" {
+		versionSuffix += "." + p.arch()
+	}
+	return vendorSnapshotObjectSuffix + versionSuffix
+}
+
+func (p *vendorSnapshotObjectLinker) version() string {
+	return p.properties.Version
+}
+
+func (p *vendorSnapshotObjectLinker) arch() string {
+	return p.properties.Target_arch
+}
+
+func (p *vendorSnapshotObjectLinker) matchesWithDevice(config android.DeviceConfig) bool {
+	if config.DeviceArch() != p.arch() {
+		return false
+	}
+	if p.properties.Src == nil {
+		return false
+	}
+	return true
+}
+
+func (p *vendorSnapshotObjectLinker) link(ctx ModuleContext,
+	flags Flags, deps PathDeps, objs Objects) android.Path {
+	if !p.matchesWithDevice(ctx.DeviceConfig()) {
+		return nil
+	}
+
+	m := ctx.Module().(*Module)
+	p.androidMkVendorSuffix = vendorSuffixModules(ctx.Config())[m.BaseModuleName()]
+
+	return android.PathForModuleSrc(ctx, *p.properties.Src)
+}
+
+func (p *vendorSnapshotObjectLinker) nativeCoverage() bool {
+	return false
+}
+
+func (p *vendorSnapshotObjectLinker) isSnapshotPrebuilt() bool {
+	return true
+}
+
+func VendorSnapshotObjectFactory() android.Module {
+	module := newObject()
+
+	prebuilt := &vendorSnapshotObjectLinker{
+		objectLinker: objectLinker{
+			baseLinker: NewBaseLinker(nil),
+		},
+	}
+	module.linker = prebuilt
+
+	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
+		vendorSnapshotLoadHook(ctx, prebuilt)
+	})
+
+	module.AddProperties(&prebuilt.properties)
+	return module.Init()
+}
+
 func init() {
 	android.RegisterSingletonType("vendor-snapshot", VendorSnapshotSingleton)
 	android.RegisterModuleType("vendor_snapshot_shared", VendorSnapshotSharedFactory)
 	android.RegisterModuleType("vendor_snapshot_static", VendorSnapshotStaticFactory)
 	android.RegisterModuleType("vendor_snapshot_header", VendorSnapshotHeaderFactory)
 	android.RegisterModuleType("vendor_snapshot_binary", VendorSnapshotBinaryFactory)
+	android.RegisterModuleType("vendor_snapshot_object", VendorSnapshotObjectFactory)
 }
 
 func VendorSnapshotSingleton() android.Singleton {
@@ -429,7 +531,7 @@
 // depend on newer VNDK) So they are captured as vendor snapshot To build older vendor
 // image and newer system image altogether.
 func isVendorSnapshotModule(m *Module, moduleDir string) bool {
-	if !m.Enabled() {
+	if !m.Enabled() || m.Properties.HideFromMake {
 		return false
 	}
 	// skip proprietary modules, but include all VNDK (static)
@@ -443,37 +545,37 @@
 		return false
 	}
 	// the module must be installed in /vendor
-	if !m.installable() || m.isSnapshotPrebuilt() || !m.inVendor() {
-		return false
-	}
-	// exclude test modules
-	if _, ok := m.linker.(interface{ gtest() bool }); ok {
-		return false
-	}
-	// TODO(b/65377115): add full support for sanitizer
-	if m.sanitize != nil && !m.sanitize.isUnsanitizedVariant() {
+	if !m.IsForPlatform() || m.isSnapshotPrebuilt() || !m.inVendor() {
 		return false
 	}
 
 	// Libraries
 	if l, ok := m.linker.(snapshotLibraryInterface); ok {
+		// TODO(b/65377115): add full support for sanitizer
+		if m.sanitize != nil {
+			// cfi, scs and hwasan export both sanitized and unsanitized variants for static and header
+			// Always use unsanitized variants of them.
+			for _, t := range []sanitizerType{cfi, scs, hwasan} {
+				if !l.shared() && m.sanitize.isSanitizerEnabled(t) {
+					return false
+				}
+			}
+		}
 		if l.static() {
-			return proptools.BoolDefault(m.VendorProperties.Vendor_available, true)
+			return m.outputFile.Valid() && proptools.BoolDefault(m.VendorProperties.Vendor_available, true)
 		}
 		if l.shared() {
-			return !m.IsVndk()
+			return m.outputFile.Valid() && !m.IsVndk()
 		}
 		return true
 	}
 
-	// Binaries
-	_, ok := m.linker.(*binaryDecorator)
-	if !ok {
-		if _, ok := m.linker.(*prebuiltBinaryLinker); !ok {
-			return false
-		}
+	// Binaries and Objects
+	if m.binary() || m.object() {
+		return m.outputFile.Valid() && proptools.BoolDefault(m.VendorProperties.Vendor_available, true)
 	}
-	return proptools.BoolDefault(m.VendorProperties.Vendor_available, true)
+
+	return false
 }
 
 func (c *vendorSnapshotSingleton) GenerateBuildActions(ctx android.SingletonContext) {
@@ -496,6 +598,8 @@
 					(header only libraries)
 				binary/
 					(executable binaries)
+				object/
+					(.o object files)
 			arch-{TARGET_2ND_ARCH}-{TARGET_2ND_ARCH_VARIANT}/
 				shared/
 					(.so shared libraries)
@@ -505,6 +609,8 @@
 					(header only libraries)
 				binary/
 					(executable binaries)
+				object/
+					(.o object files)
 			NOTICE_FILES/
 				(notice files, e.g. libbase.txt)
 			configs/
@@ -620,7 +726,7 @@
 			}
 
 			propOut = filepath.Join(snapshotArchDir, targetArch, libType, stem+".json")
-		} else {
+		} else if m.binary() {
 			// binary flags
 			prop.Symlinks = m.Symlinks()
 			prop.SharedLibs = m.Properties.SnapshotSharedLibs
@@ -630,6 +736,17 @@
 			snapshotBinOut := filepath.Join(snapshotArchDir, targetArch, "binary", binPath.Base())
 			ret = append(ret, copyFile(ctx, binPath, snapshotBinOut))
 			propOut = snapshotBinOut + ".json"
+		} else if m.object() {
+			// object files aren't installed to the device, so their names can conflict.
+			// Use module name as stem.
+			objPath := m.outputFile.Path()
+			snapshotObjOut := filepath.Join(snapshotArchDir, targetArch, "object",
+				ctx.ModuleName(m)+filepath.Ext(objPath.Base()))
+			ret = append(ret, copyFile(ctx, objPath, snapshotObjOut))
+			propOut = snapshotObjOut + ".json"
+		} else {
+			ctx.Errorf("unknown module %q in vendor snapshot", m.String())
+			return nil
 		}
 
 		j, err := json.Marshal(prop)
@@ -716,6 +833,7 @@
 var _ snapshotInterface = (*vndkPrebuiltLibraryDecorator)(nil)
 var _ snapshotInterface = (*vendorSnapshotLibraryDecorator)(nil)
 var _ snapshotInterface = (*vendorSnapshotBinaryDecorator)(nil)
+var _ snapshotInterface = (*vendorSnapshotObjectLinker)(nil)
 
 // gathers all snapshot modules for vendor, and disable unnecessary snapshots
 // TODO(b/145966707): remove mutator and utilize android.Prebuilt to override source modules
@@ -731,12 +849,12 @@
 		return
 	}
 
-	snapshot, ok := module.linker.(snapshotInterface)
-	if !ok {
+	if !module.isSnapshotPrebuilt() {
 		return
 	}
 
-	if !snapshot.matchesWithDevice(ctx.DeviceConfig()) {
+	// isSnapshotPrebuilt ensures snapshotInterface
+	if !module.linker.(snapshotInterface).matchesWithDevice(ctx.DeviceConfig()) {
 		// Disable unnecessary snapshot module, but do not disable
 		// vndk_prebuilt_shared because they might be packed into vndk APEX
 		if !module.IsVndk() {
@@ -758,6 +876,8 @@
 		}
 	} else if _, ok := module.linker.(*vendorSnapshotBinaryDecorator); ok {
 		snapshotMap = vendorSnapshotBinaries(ctx.Config())
+	} else if _, ok := module.linker.(*vendorSnapshotObjectLinker); ok {
+		snapshotMap = vendorSnapshotObjects(ctx.Config())
 	} else {
 		return
 	}
@@ -804,6 +924,11 @@
 		return
 	}
 
+	// .. and also filter out llndk library
+	if module.isLlndk(ctx.Config()) {
+		return
+	}
+
 	var snapshotMap *snapshotMap
 
 	if lib, ok := module.linker.(libraryInterface); ok {
@@ -815,10 +940,10 @@
 			// header
 			snapshotMap = vendorSnapshotHeaderLibs(ctx.Config())
 		}
-	} else if _, ok := module.linker.(*binaryDecorator); ok {
+	} else if module.binary() {
 		snapshotMap = vendorSnapshotBinaries(ctx.Config())
-	} else if _, ok := module.linker.(*prebuiltBinaryLinker); ok {
-		snapshotMap = vendorSnapshotBinaries(ctx.Config())
+	} else if module.object() {
+		snapshotMap = vendorSnapshotObjects(ctx.Config())
 	} else {
 		return
 	}
diff --git a/cc/vndk.go b/cc/vndk.go
index e5e4533..04b865f 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -25,6 +25,7 @@
 
 	"android/soong/android"
 	"android/soong/cc/config"
+	"android/soong/etc"
 )
 
 const (
@@ -416,7 +417,7 @@
 	outputFile android.OutputPath
 }
 
-var _ android.PrebuiltEtcModule = &vndkLibrariesTxt{}
+var _ etc.PrebuiltEtcModule = &vndkLibrariesTxt{}
 var _ android.OutputFileProducer = &vndkLibrariesTxt{}
 
 // vndk_libraries_txt is a special kind of module type in that it name is one of
diff --git a/cc/vndk_prebuilt.go b/cc/vndk_prebuilt.go
index 53b5181..5a44c46 100644
--- a/cc/vndk_prebuilt.go
+++ b/cc/vndk_prebuilt.go
@@ -186,6 +186,10 @@
 	return false
 }
 
+func (p *vndkPrebuiltLibraryDecorator) isSnapshotPrebuilt() bool {
+	return true
+}
+
 func (p *vndkPrebuiltLibraryDecorator) install(ctx ModuleContext, file android.Path) {
 	arches := ctx.DeviceConfig().Arches()
 	if len(arches) == 0 || arches[0].ArchType.String() != p.arch() {
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index 1d94f02..d0cad78 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -117,6 +117,8 @@
 // Command is the type of soong_ui execution. Only one type of
 // execution is specified. The args are specific to the command.
 func main() {
+	buildStartedMilli := time.Now().UnixNano() / int64(time.Millisecond)
+
 	c, args := getCommand(os.Args)
 	if c == nil {
 		fmt.Fprintf(os.Stderr, "The `soong` native UI is not yet available.\n")
@@ -166,12 +168,17 @@
 		logsDir = filepath.Join(config.DistDir(), "logs")
 	}
 
+	buildErrorFile := filepath.Join(logsDir, c.logsPrefix+"build_error")
+	rbeMetricsFile := filepath.Join(logsDir, c.logsPrefix+"rbe_metrics.pb")
+	soongMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_metrics")
+	defer build.UploadMetrics(buildCtx, config, buildStartedMilli, buildErrorFile, rbeMetricsFile, soongMetricsFile)
+
 	os.MkdirAll(logsDir, 0777)
 	log.SetOutput(filepath.Join(logsDir, c.logsPrefix+"soong.log"))
 	trace.SetOutput(filepath.Join(logsDir, c.logsPrefix+"build.trace"))
 	stat.AddOutput(status.NewVerboseLog(log, filepath.Join(logsDir, c.logsPrefix+"verbose.log")))
 	stat.AddOutput(status.NewErrorLog(log, filepath.Join(logsDir, c.logsPrefix+"error.log")))
-	stat.AddOutput(status.NewProtoErrorLog(log, filepath.Join(logsDir, c.logsPrefix+"build_error")))
+	stat.AddOutput(status.NewProtoErrorLog(log, buildErrorFile))
 	stat.AddOutput(status.NewCriticalPath(log))
 	stat.AddOutput(status.NewBuildProgressLog(log, filepath.Join(logsDir, c.logsPrefix+"build_progress.pb")))
 
@@ -179,7 +186,7 @@
 	buildCtx.Verbosef("Parallelism (local/remote/highmem): %v/%v/%v",
 		config.Parallel(), config.RemoteParallel(), config.HighmemParallel())
 
-	defer met.Dump(filepath.Join(logsDir, c.logsPrefix+"soong_metrics"))
+	defer met.Dump(soongMetricsFile)
 
 	if start, ok := os.LookupEnv("TRACE_BEGIN_SOONG"); ok {
 		if !strings.HasSuffix(start, "N") {
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
index 98850e5..3440f8e 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -50,6 +50,8 @@
 	UpdatableSystemServerJars []string // jars within apex that are loaded into system server
 	SpeedApps                 []string // apps that should be speed optimized
 
+	BrokenSuboptimalOrderOfSystemServerJars bool // if true, sub-optimal order does not cause a build error
+
 	PreoptFlags []string // global dex2oat flags that should be used if no module-specific dex2oat flags are specified
 
 	DefaultCompilerFilter      string // default compiler filter to pass to dex2oat, overridden by --compiler-filter= in module-specific dex2oat flags
@@ -98,6 +100,15 @@
 	ConstructContext android.Path
 }
 
+// LibraryPath contains paths to the library DEX jar on host and on device.
+type LibraryPath struct {
+	Host   android.Path
+	Device string
+}
+
+// LibraryPaths is a map from library name to on-host and on-device paths to its DEX jar.
+type LibraryPaths map[string]*LibraryPath
+
 type ModuleConfig struct {
 	Name            string
 	DexLocation     string // dex location on device
@@ -115,7 +126,7 @@
 	EnforceUsesLibraries         bool
 	PresentOptionalUsesLibraries []string
 	UsesLibraries                []string
-	LibraryPaths                 map[string]android.Path
+	LibraryPaths                 LibraryPaths
 
 	Archs                   []android.ArchType
 	DexPreoptImages         []android.Path
@@ -163,14 +174,6 @@
 	return ret
 }
 
-func constructPathMap(ctx android.PathContext, paths map[string]string) map[string]android.Path {
-	ret := map[string]android.Path{}
-	for key, path := range paths {
-		ret[key] = constructPath(ctx, path)
-	}
-	return ret
-}
-
 func constructWritablePath(ctx android.PathContext, path string) android.WritablePath {
 	if path == "" {
 		return nil
@@ -262,6 +265,13 @@
 // from Make to read the module dexpreopt.config written in the Make config
 // stage.
 func ParseModuleConfig(ctx android.PathContext, data []byte) (*ModuleConfig, error) {
+	type jsonLibraryPath struct {
+		Host   string
+		Device string
+	}
+
+	type jsonLibraryPaths map[string]jsonLibraryPath
+
 	type ModuleJSONConfig struct {
 		*ModuleConfig
 
@@ -271,12 +281,24 @@
 		DexPath                     string
 		ManifestPath                string
 		ProfileClassListing         string
-		LibraryPaths                map[string]string
+		LibraryPaths                jsonLibraryPaths
 		DexPreoptImages             []string
 		DexPreoptImageLocations     []string
 		PreoptBootClassPathDexFiles []string
 	}
 
+	// convert JSON map of library paths to LibraryPaths
+	constructLibraryPaths := func(ctx android.PathContext, paths jsonLibraryPaths) LibraryPaths {
+		m := LibraryPaths{}
+		for lib, path := range paths {
+			m[lib] = &LibraryPath{
+				constructPath(ctx, path.Host),
+				path.Device,
+			}
+		}
+		return m
+	}
+
 	config := ModuleJSONConfig{}
 
 	err := json.Unmarshal(data, &config)
@@ -289,7 +311,7 @@
 	config.ModuleConfig.DexPath = constructPath(ctx, config.DexPath)
 	config.ModuleConfig.ManifestPath = constructPath(ctx, config.ManifestPath)
 	config.ModuleConfig.ProfileClassListing = android.OptionalPathForPath(constructPath(ctx, config.ProfileClassListing))
-	config.ModuleConfig.LibraryPaths = constructPathMap(ctx, config.LibraryPaths)
+	config.ModuleConfig.LibraryPaths = constructLibraryPaths(ctx, config.LibraryPaths)
 	config.ModuleConfig.DexPreoptImages = constructPaths(ctx, config.DexPreoptImages)
 	config.ModuleConfig.DexPreoptImageLocations = config.DexPreoptImageLocations
 	config.ModuleConfig.PreoptBootClassPathDexFiles = constructPaths(ctx, config.PreoptBootClassPathDexFiles)
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index fc03563..57a9250 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -37,6 +37,7 @@
 	"fmt"
 	"path/filepath"
 	"runtime"
+	"sort"
 	"strings"
 
 	"android/soong/android"
@@ -192,6 +193,56 @@
 	return profilePath
 }
 
+type classLoaderContext struct {
+	// The class loader context using paths in the build.
+	Host android.Paths
+
+	// The class loader context using paths as they will be on the device.
+	Target []string
+}
+
+// A map of class loader contexts for each SDK version.
+// A map entry for "any" version contains libraries that are unconditionally added to class loader
+// context. Map entries for existing versions contains libraries that were in the default classpath
+// until that API version, and should be added to class loader context if and only if the
+// targetSdkVersion in the manifest or APK is less than that API version.
+type classLoaderContextMap map[int]*classLoaderContext
+
+const anySdkVersion int = -1
+
+func (m classLoaderContextMap) getSortedKeys() []int {
+	keys := make([]int, 0, len(m))
+	for k := range m {
+		keys = append(keys, k)
+	}
+	sort.Ints(keys)
+	return keys
+}
+
+func (m classLoaderContextMap) getValue(sdkVer int) *classLoaderContext {
+	if _, ok := m[sdkVer]; !ok {
+		m[sdkVer] = &classLoaderContext{}
+	}
+	return m[sdkVer]
+}
+
+func (m classLoaderContextMap) addLibs(sdkVer int, module *ModuleConfig, libs ...string) {
+	clc := m.getValue(sdkVer)
+	for _, lib := range libs {
+		p := pathForLibrary(module, lib)
+		clc.Host = append(clc.Host, p.Host)
+		clc.Target = append(clc.Target, p.Device)
+	}
+}
+
+func (m classLoaderContextMap) addSystemServerLibs(sdkVer int, ctx android.PathContext, module *ModuleConfig, libs ...string) {
+	clc := m.getValue(sdkVer)
+	for _, lib := range libs {
+		clc.Host = append(clc.Host, SystemServerDexJarHostPath(ctx, lib))
+		clc.Target = append(clc.Target, filepath.Join("/system/framework", lib+".jar"))
+	}
+}
+
 func dexpreoptCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, global *GlobalConfig,
 	module *ModuleConfig, rule *android.RuleBuilder, archIdx int, profile android.WritablePath,
 	appImage bool, generateDM bool) {
@@ -227,77 +278,45 @@
 
 	systemServerJars := NonUpdatableSystemServerJars(ctx, global)
 
-	// The class loader context using paths in the build
-	var classLoaderContextHost android.Paths
-
-	// The class loader context using paths as they will be on the device
-	var classLoaderContextTarget []string
-
-	// Extra paths that will be appended to the class loader if the APK manifest has targetSdkVersion < 28
-	var conditionalClassLoaderContextHost28 android.Paths
-	var conditionalClassLoaderContextTarget28 []string
-
-	// Extra paths that will be appended to the class loader if the APK manifest has targetSdkVersion < 29
-	var conditionalClassLoaderContextHost29 android.Paths
-	var conditionalClassLoaderContextTarget29 []string
+	classLoaderContexts := make(classLoaderContextMap)
 
 	// A flag indicating if the '&' class loader context is used.
 	unknownClassLoaderContext := false
 
 	if module.EnforceUsesLibraries {
+		// Unconditional class loader context.
 		usesLibs := append(copyOf(module.UsesLibraries), module.PresentOptionalUsesLibraries...)
+		classLoaderContexts.addLibs(anySdkVersion, module, usesLibs...)
 
-		// Create class loader context for dex2oat from uses libraries and filtered optional libraries
-		for _, l := range usesLibs {
-
-			classLoaderContextHost = append(classLoaderContextHost,
-				pathForLibrary(module, l))
-			classLoaderContextTarget = append(classLoaderContextTarget,
-				filepath.Join("/system/framework", l+".jar"))
-		}
-
+		// Conditional class loader context for API version < 28.
 		const httpLegacy = "org.apache.http.legacy"
-		const httpLegacyImpl = "org.apache.http.legacy.impl"
-
-		// org.apache.http.legacy contains classes that were in the default classpath until API 28.  If the
-		// targetSdkVersion in the manifest or APK is < 28, and the module does not explicitly depend on
-		// org.apache.http.legacy, then implicitly add the classes to the classpath for dexpreopt.  One the
-		// device the classes will be in a file called org.apache.http.legacy.impl.jar.
-		module.LibraryPaths[httpLegacyImpl] = module.LibraryPaths[httpLegacy]
-
-		if !contains(module.UsesLibraries, httpLegacy) && !contains(module.PresentOptionalUsesLibraries, httpLegacy) {
-			conditionalClassLoaderContextHost28 = append(conditionalClassLoaderContextHost28,
-				pathForLibrary(module, httpLegacyImpl))
-			conditionalClassLoaderContextTarget28 = append(conditionalClassLoaderContextTarget28,
-				filepath.Join("/system/framework", httpLegacyImpl+".jar"))
+		if !contains(usesLibs, httpLegacy) {
+			classLoaderContexts.addLibs(28, module, httpLegacy)
 		}
 
-		const hidlBase = "android.hidl.base-V1.0-java"
-		const hidlManager = "android.hidl.manager-V1.0-java"
+		// Conditional class loader context for API version < 29.
+		usesLibs29 := []string{
+			"android.hidl.base-V1.0-java",
+			"android.hidl.manager-V1.0-java",
+		}
+		classLoaderContexts.addLibs(29, module, usesLibs29...)
 
-		// android.hidl.base-V1.0-java and android.hidl.manager-V1.0 contain classes that were in the default
-		// classpath until API 29.  If the targetSdkVersion in the manifest or APK is < 29 then implicitly add
-		// the classes to the classpath for dexpreopt.
-		conditionalClassLoaderContextHost29 = append(conditionalClassLoaderContextHost29,
-			pathForLibrary(module, hidlManager))
-		conditionalClassLoaderContextTarget29 = append(conditionalClassLoaderContextTarget29,
-			filepath.Join("/system/framework", hidlManager+".jar"))
-		conditionalClassLoaderContextHost29 = append(conditionalClassLoaderContextHost29,
-			pathForLibrary(module, hidlBase))
-		conditionalClassLoaderContextTarget29 = append(conditionalClassLoaderContextTarget29,
-			filepath.Join("/system/framework", hidlBase+".jar"))
+		// Conditional class loader context for API version < 30.
+		const testBase = "android.test.base"
+		if !contains(usesLibs, testBase) {
+			classLoaderContexts.addLibs(30, module, testBase)
+		}
 	} else if jarIndex := android.IndexList(module.Name, systemServerJars); jarIndex >= 0 {
 		// System server jars should be dexpreopted together: class loader context of each jar
 		// should include all preceding jars on the system server classpath.
-		for _, otherJar := range systemServerJars[:jarIndex] {
-			classLoaderContextHost = append(classLoaderContextHost, SystemServerDexJarHostPath(ctx, otherJar))
-			classLoaderContextTarget = append(classLoaderContextTarget, "/system/framework/"+otherJar+".jar")
-		}
+		classLoaderContexts.addSystemServerLibs(anySdkVersion, ctx, module, systemServerJars[:jarIndex]...)
 
 		// Copy the system server jar to a predefined location where dex2oat will find it.
 		dexPathHost := SystemServerDexJarHostPath(ctx, module.Name)
 		rule.Command().Text("mkdir -p").Flag(filepath.Dir(dexPathHost.String()))
 		rule.Command().Text("cp -f").Input(module.DexPath).Output(dexPathHost)
+
+		checkSystemServerOrder(ctx, jarIndex)
 	} else {
 		// Pass special class loader context to skip the classpath and collision check.
 		// This will get removed once LOCAL_USES_LIBRARIES is enforced.
@@ -314,10 +333,11 @@
 			Text(`class_loader_context_arg=--class-loader-context=\&`).
 			Text(`stored_class_loader_context_arg=""`)
 	} else {
+		clc := classLoaderContexts[anySdkVersion]
 		rule.Command().
-			Text("class_loader_context_arg=--class-loader-context=PCL[" + strings.Join(classLoaderContextHost.Strings(), ":") + "]").
-			Implicits(classLoaderContextHost).
-			Text("stored_class_loader_context_arg=--stored-class-loader-context=PCL[" + strings.Join(classLoaderContextTarget, ":") + "]")
+			Text("class_loader_context_arg=--class-loader-context=PCL[" + strings.Join(clc.Host.Strings(), ":") + "]").
+			Implicits(clc.Host).
+			Text("stored_class_loader_context_arg=--stored-class-loader-context=PCL[" + strings.Join(clc.Target, ":") + "]")
 	}
 
 	if module.EnforceUsesLibraries {
@@ -336,21 +356,19 @@
 				Text(`| grep "targetSdkVersion" | sed -n "s/targetSdkVersion:'\(.*\)'/\1/p"`).
 				Text(`)"`)
 		}
-		rule.Command().Textf(`dex_preopt_host_libraries="%s"`,
-			strings.Join(classLoaderContextHost.Strings(), " ")).
-			Implicits(classLoaderContextHost)
-		rule.Command().Textf(`dex_preopt_target_libraries="%s"`,
-			strings.Join(classLoaderContextTarget, " "))
-		rule.Command().Textf(`conditional_host_libs_28="%s"`,
-			strings.Join(conditionalClassLoaderContextHost28.Strings(), " ")).
-			Implicits(conditionalClassLoaderContextHost28)
-		rule.Command().Textf(`conditional_target_libs_28="%s"`,
-			strings.Join(conditionalClassLoaderContextTarget28, " "))
-		rule.Command().Textf(`conditional_host_libs_29="%s"`,
-			strings.Join(conditionalClassLoaderContextHost29.Strings(), " ")).
-			Implicits(conditionalClassLoaderContextHost29)
-		rule.Command().Textf(`conditional_target_libs_29="%s"`,
-			strings.Join(conditionalClassLoaderContextTarget29, " "))
+		for _, ver := range classLoaderContexts.getSortedKeys() {
+			clc := classLoaderContexts.getValue(ver)
+			var varHost, varTarget string
+			if ver == anySdkVersion {
+				varHost = "dex_preopt_host_libraries"
+				varTarget = "dex_preopt_target_libraries"
+			} else {
+				varHost = fmt.Sprintf("conditional_host_libs_%d", ver)
+				varTarget = fmt.Sprintf("conditional_target_libs_%d", ver)
+			}
+			rule.Command().Textf(varHost+`="%s"`, strings.Join(clc.Host.Strings(), " ")).Implicits(clc.Host)
+			rule.Command().Textf(varTarget+`="%s"`, strings.Join(clc.Target, " "))
+		}
 		rule.Command().Text("source").Tool(globalSoong.ConstructContext).Input(module.DexPath)
 	}
 
@@ -540,7 +558,7 @@
 	return filepath.Join(filepath.Dir(filepath.Dir(path.String())), filepath.Base(path.String()))
 }
 
-func pathForLibrary(module *ModuleConfig, lib string) android.Path {
+func pathForLibrary(module *ModuleConfig, lib string) *LibraryPath {
 	path, ok := module.LibraryPaths[lib]
 	if !ok {
 		panic(fmt.Errorf("unknown library path for %q", lib))
@@ -592,6 +610,29 @@
 	}
 }
 
+// Check the order of jars on the system server classpath and give a warning/error if a jar precedes
+// one of its dependencies. This is not an error, but a missed optimization, as dexpreopt won't
+// have the dependency jar in the class loader context, and it won't be able to resolve any
+// references to its classes and methods.
+func checkSystemServerOrder(ctx android.PathContext, jarIndex int) {
+	mctx, isModule := ctx.(android.ModuleContext)
+	if isModule {
+		config := GetGlobalConfig(ctx)
+		jars := NonUpdatableSystemServerJars(ctx, config)
+		mctx.WalkDeps(func(dep android.Module, parent android.Module) bool {
+			depIndex := android.IndexList(dep.Name(), jars)
+			if jarIndex < depIndex && !config.BrokenSuboptimalOrderOfSystemServerJars {
+				jar := jars[jarIndex]
+				dep := jars[depIndex]
+				mctx.ModuleErrorf("non-optimal order of jars on the system server classpath:"+
+					" '%s' precedes its dependency '%s', so dexpreopt is unable to resolve any"+
+					" references from '%s' to '%s'.\n", jar, dep, jar, dep)
+			}
+			return true
+		})
+	}
+}
+
 func contains(l []string, s string) bool {
 	for _, e := range l {
 		if e == s {
diff --git a/env/Android.bp b/env/Android.bp
new file mode 100644
index 0000000..90c6047
--- /dev/null
+++ b/env/Android.bp
@@ -0,0 +1,7 @@
+bootstrap_go_package {
+    name: "soong-env",
+    pkgPath: "android/soong/env",
+    srcs: [
+        "env.go",
+    ],
+}
diff --git a/etc/Android.bp b/etc/Android.bp
new file mode 100644
index 0000000..cfd303e
--- /dev/null
+++ b/etc/Android.bp
@@ -0,0 +1,16 @@
+bootstrap_go_package {
+    name: "soong-etc",
+    pkgPath: "android/soong/etc",
+    deps: [
+        "blueprint",
+        "soong",
+        "soong-android",
+    ],
+    srcs: [
+        "prebuilt_etc.go",
+    ],
+    testSrcs: [
+        "prebuilt_etc_test.go",
+    ],
+    pluginFor: ["soong_build"],
+}
diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
new file mode 100644
index 0000000..d6eb008
--- /dev/null
+++ b/etc/prebuilt_etc.go
@@ -0,0 +1,295 @@
+// Copyright 2016 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package etc
+
+import (
+	"strconv"
+
+	"github.com/google/blueprint/proptools"
+
+	"android/soong/android"
+)
+
+var pctx = android.NewPackageContext("android/soong/etc")
+
+// TODO(jungw): Now that it handles more than the ones in etc/, consider renaming this file.
+
+func init() {
+	pctx.Import("android/soong/android")
+
+	android.RegisterModuleType("prebuilt_etc", PrebuiltEtcFactory)
+	android.RegisterModuleType("prebuilt_etc_host", PrebuiltEtcHostFactory)
+	android.RegisterModuleType("prebuilt_usr_share", PrebuiltUserShareFactory)
+	android.RegisterModuleType("prebuilt_usr_share_host", PrebuiltUserShareHostFactory)
+	android.RegisterModuleType("prebuilt_font", PrebuiltFontFactory)
+	android.RegisterModuleType("prebuilt_firmware", PrebuiltFirmwareFactory)
+}
+
+type prebuiltEtcProperties struct {
+	// Source file of this prebuilt.
+	Src *string `android:"path,arch_variant"`
+
+	// optional subdirectory under which this file is installed into
+	Sub_dir *string `android:"arch_variant"`
+
+	// optional name for the installed file. If unspecified, name of the module is used as the file name
+	Filename *string `android:"arch_variant"`
+
+	// when set to true, and filename property is not set, the name for the installed file
+	// is the same as the file name of the source file.
+	Filename_from_src *bool `android:"arch_variant"`
+
+	// Make this module available when building for ramdisk.
+	Ramdisk_available *bool
+
+	// Make this module available when building for recovery.
+	Recovery_available *bool
+
+	// Whether this module is directly installable to one of the partitions. Default: true.
+	Installable *bool
+
+	// Install symlinks to the installed file.
+	Symlinks []string `android:"arch_variant"`
+}
+
+type PrebuiltEtcModule interface {
+	android.Module
+	SubDir() string
+	OutputFile() android.OutputPath
+}
+
+type PrebuiltEtc struct {
+	android.ModuleBase
+
+	properties prebuiltEtcProperties
+
+	sourceFilePath android.Path
+	outputFilePath android.OutputPath
+	// The base install location, e.g. "etc" for prebuilt_etc, "usr/share" for prebuilt_usr_share.
+	installDirBase string
+	// The base install location when soc_specific property is set to true, e.g. "firmware" for prebuilt_firmware.
+	socInstallDirBase      string
+	installDirPath         android.InstallPath
+	additionalDependencies *android.Paths
+}
+
+func (p *PrebuiltEtc) inRamdisk() bool {
+	return p.ModuleBase.InRamdisk() || p.ModuleBase.InstallInRamdisk()
+}
+
+func (p *PrebuiltEtc) onlyInRamdisk() bool {
+	return p.ModuleBase.InstallInRamdisk()
+}
+
+func (p *PrebuiltEtc) InstallInRamdisk() bool {
+	return p.inRamdisk()
+}
+
+func (p *PrebuiltEtc) inRecovery() bool {
+	return p.ModuleBase.InRecovery() || p.ModuleBase.InstallInRecovery()
+}
+
+func (p *PrebuiltEtc) onlyInRecovery() bool {
+	return p.ModuleBase.InstallInRecovery()
+}
+
+func (p *PrebuiltEtc) InstallInRecovery() bool {
+	return p.inRecovery()
+}
+
+var _ android.ImageInterface = (*PrebuiltEtc)(nil)
+
+func (p *PrebuiltEtc) ImageMutatorBegin(ctx android.BaseModuleContext) {}
+
+func (p *PrebuiltEtc) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
+	return !p.ModuleBase.InstallInRecovery() && !p.ModuleBase.InstallInRamdisk()
+}
+
+func (p *PrebuiltEtc) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+	return proptools.Bool(p.properties.Ramdisk_available) || p.ModuleBase.InstallInRamdisk()
+}
+
+func (p *PrebuiltEtc) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
+	return proptools.Bool(p.properties.Recovery_available) || p.ModuleBase.InstallInRecovery()
+}
+
+func (p *PrebuiltEtc) ExtraImageVariations(ctx android.BaseModuleContext) []string {
+	return nil
+}
+
+func (p *PrebuiltEtc) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) {
+}
+
+func (p *PrebuiltEtc) DepsMutator(ctx android.BottomUpMutatorContext) {
+	if p.properties.Src == nil {
+		ctx.PropertyErrorf("src", "missing prebuilt source file")
+	}
+}
+
+func (p *PrebuiltEtc) SourceFilePath(ctx android.ModuleContext) android.Path {
+	return android.PathForModuleSrc(ctx, android.String(p.properties.Src))
+}
+
+func (p *PrebuiltEtc) InstallDirPath() android.InstallPath {
+	return p.installDirPath
+}
+
+// This allows other derivative modules (e.g. prebuilt_etc_xml) to perform
+// additional steps (like validating the src) before the file is installed.
+func (p *PrebuiltEtc) SetAdditionalDependencies(paths android.Paths) {
+	p.additionalDependencies = &paths
+}
+
+func (p *PrebuiltEtc) OutputFile() android.OutputPath {
+	return p.outputFilePath
+}
+
+func (p *PrebuiltEtc) SubDir() string {
+	return android.String(p.properties.Sub_dir)
+}
+
+func (p *PrebuiltEtc) Installable() bool {
+	return p.properties.Installable == nil || android.Bool(p.properties.Installable)
+}
+
+func (p *PrebuiltEtc) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	p.sourceFilePath = android.PathForModuleSrc(ctx, android.String(p.properties.Src))
+	filename := android.String(p.properties.Filename)
+	filename_from_src := android.Bool(p.properties.Filename_from_src)
+	if filename == "" {
+		if filename_from_src {
+			filename = p.sourceFilePath.Base()
+		} else {
+			filename = ctx.ModuleName()
+		}
+	} else if filename_from_src {
+		ctx.PropertyErrorf("filename_from_src", "filename is set. filename_from_src can't be true")
+		return
+	}
+	p.outputFilePath = android.PathForModuleOut(ctx, filename).OutputPath
+
+	// If soc install dir was specified and SOC specific is set, set the installDirPath to the specified
+	// socInstallDirBase.
+	installBaseDir := p.installDirBase
+	if ctx.SocSpecific() && p.socInstallDirBase != "" {
+		installBaseDir = p.socInstallDirBase
+	}
+	p.installDirPath = android.PathForModuleInstall(ctx, installBaseDir, proptools.String(p.properties.Sub_dir))
+
+	// This ensures that outputFilePath has the correct name for others to
+	// use, as the source file may have a different name.
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   android.Cp,
+		Output: p.outputFilePath,
+		Input:  p.sourceFilePath,
+	})
+}
+
+func (p *PrebuiltEtc) AndroidMkEntries() []android.AndroidMkEntries {
+	nameSuffix := ""
+	if p.inRamdisk() && !p.onlyInRamdisk() {
+		nameSuffix = ".ramdisk"
+	}
+	if p.inRecovery() && !p.onlyInRecovery() {
+		nameSuffix = ".recovery"
+	}
+	return []android.AndroidMkEntries{android.AndroidMkEntries{
+		Class:      "ETC",
+		SubName:    nameSuffix,
+		OutputFile: android.OptionalPathForPath(p.outputFilePath),
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(entries *android.AndroidMkEntries) {
+				entries.SetString("LOCAL_MODULE_TAGS", "optional")
+				entries.SetString("LOCAL_MODULE_PATH", p.installDirPath.ToMakePath().String())
+				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.outputFilePath.Base())
+				if len(p.properties.Symlinks) > 0 {
+					entries.AddStrings("LOCAL_MODULE_SYMLINKS", p.properties.Symlinks...)
+				}
+				entries.SetString("LOCAL_UNINSTALLABLE_MODULE", strconv.FormatBool(!p.Installable()))
+				if p.additionalDependencies != nil {
+					for _, path := range *p.additionalDependencies {
+						entries.AddStrings("LOCAL_ADDITIONAL_DEPENDENCIES", path.String())
+					}
+				}
+			},
+		},
+	}}
+}
+
+func InitPrebuiltEtcModule(p *PrebuiltEtc, dirBase string) {
+	p.installDirBase = dirBase
+	p.AddProperties(&p.properties)
+}
+
+// prebuilt_etc is for a prebuilt artifact that is installed in
+// <partition>/etc/<sub_dir> directory.
+func PrebuiltEtcFactory() android.Module {
+	module := &PrebuiltEtc{}
+	InitPrebuiltEtcModule(module, "etc")
+	// This module is device-only
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+	return module
+}
+
+// prebuilt_etc_host is for a host prebuilt artifact that is installed in
+// $(HOST_OUT)/etc/<sub_dir> directory.
+func PrebuiltEtcHostFactory() android.Module {
+	module := &PrebuiltEtc{}
+	InitPrebuiltEtcModule(module, "etc")
+	// This module is host-only
+	android.InitAndroidArchModule(module, android.HostSupported, android.MultilibCommon)
+	return module
+}
+
+// prebuilt_usr_share is for a prebuilt artifact that is installed in
+// <partition>/usr/share/<sub_dir> directory.
+func PrebuiltUserShareFactory() android.Module {
+	module := &PrebuiltEtc{}
+	InitPrebuiltEtcModule(module, "usr/share")
+	// This module is device-only
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+	return module
+}
+
+// prebuild_usr_share_host is for a host prebuilt artifact that is installed in
+// $(HOST_OUT)/usr/share/<sub_dir> directory.
+func PrebuiltUserShareHostFactory() android.Module {
+	module := &PrebuiltEtc{}
+	InitPrebuiltEtcModule(module, "usr/share")
+	// This module is host-only
+	android.InitAndroidArchModule(module, android.HostSupported, android.MultilibCommon)
+	return module
+}
+
+// prebuilt_font installs a font in <partition>/fonts directory.
+func PrebuiltFontFactory() android.Module {
+	module := &PrebuiltEtc{}
+	InitPrebuiltEtcModule(module, "fonts")
+	// This module is device-only
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+	return module
+}
+
+// prebuilt_firmware installs a firmware file to <partition>/etc/firmware directory for system image.
+// If soc_specific property is set to true, the firmware file is installed to the vendor <partition>/firmware
+// directory for vendor image.
+func PrebuiltFirmwareFactory() android.Module {
+	module := &PrebuiltEtc{}
+	module.socInstallDirBase = "firmware"
+	InitPrebuiltEtcModule(module, "etc/firmware")
+	// This module is device-only
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+	return module
+}
diff --git a/android/prebuilt_etc_test.go b/etc/prebuilt_etc_test.go
similarity index 89%
rename from android/prebuilt_etc_test.go
rename to etc/prebuilt_etc_test.go
index 6e751e7..e13cb3c 100644
--- a/android/prebuilt_etc_test.go
+++ b/etc/prebuilt_etc_test.go
@@ -12,24 +12,53 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package android
+package etc
 
 import (
+	"io/ioutil"
+	"os"
 	"path/filepath"
 	"reflect"
 	"testing"
+
+	"android/soong/android"
 )
 
-func testPrebuiltEtc(t *testing.T, bp string) (*TestContext, Config) {
+var buildDir string
+
+func setUp() {
+	var err error
+	buildDir, err = ioutil.TempDir("", "soong_etc_test")
+	if err != nil {
+		panic(err)
+	}
+}
+
+func tearDown() {
+	os.RemoveAll(buildDir)
+}
+
+func TestMain(m *testing.M) {
+	run := func() int {
+		setUp()
+		defer tearDown()
+
+		return m.Run()
+	}
+
+	os.Exit(run())
+}
+
+func testPrebuiltEtc(t *testing.T, bp string) (*android.TestContext, android.Config) {
 	fs := map[string][]byte{
 		"foo.conf": nil,
 		"bar.conf": nil,
 		"baz.conf": nil,
 	}
 
-	config := TestArchConfig(buildDir, nil, bp, fs)
+	config := android.TestArchConfig(buildDir, nil, bp, fs)
 
-	ctx := NewTestArchContext()
+	ctx := android.NewTestArchContext()
 	ctx.RegisterModuleType("prebuilt_etc", PrebuiltEtcFactory)
 	ctx.RegisterModuleType("prebuilt_etc_host", PrebuiltEtcHostFactory)
 	ctx.RegisterModuleType("prebuilt_usr_share", PrebuiltUserShareFactory)
@@ -38,9 +67,9 @@
 	ctx.RegisterModuleType("prebuilt_firmware", PrebuiltFirmwareFactory)
 	ctx.Register(config)
 	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
-	FailIfErrored(t, errs)
+	android.FailIfErrored(t, errs)
 	_, errs = ctx.PrepareBuildActions(config)
-	FailIfErrored(t, errs)
+	android.FailIfErrored(t, errs)
 
 	return ctx, config
 }
@@ -142,7 +171,7 @@
 	}
 
 	mod := ctx.ModuleForTests("foo", "android_arm64_armv8-a").Module().(*PrebuiltEtc)
-	entries := AndroidMkEntriesForTest(t, config, "", mod)[0]
+	entries := android.AndroidMkEntriesForTest(t, config, "", mod)[0]
 	for k, expectedValue := range expected {
 		if value, ok := entries.EntryMap[k]; ok {
 			if !reflect.DeepEqual(value, expectedValue) {
@@ -162,7 +191,7 @@
 		}
 	`)
 
-	buildOS := BuildOs.String()
+	buildOS := android.BuildOs.String()
 	p := ctx.ModuleForTests("foo.conf", buildOS+"_common").Module().(*PrebuiltEtc)
 	if !p.Host() {
 		t.Errorf("host bit is not set for a prebuilt_etc_host module.")
@@ -194,7 +223,7 @@
 		}
 	`)
 
-	buildOS := BuildOs.String()
+	buildOS := android.BuildOs.String()
 	p := ctx.ModuleForTests("foo.conf", buildOS+"_common").Module().(*PrebuiltEtc)
 	expected := filepath.Join(buildDir, "host", config.PrebuiltOS(), "usr", "share", "bar")
 	if p.installDirPath.String() != expected {
diff --git a/genrule/Android.bp b/genrule/Android.bp
new file mode 100644
index 0000000..ff543a6
--- /dev/null
+++ b/genrule/Android.bp
@@ -0,0 +1,18 @@
+bootstrap_go_package {
+    name: "soong-genrule",
+    pkgPath: "android/soong/genrule",
+    deps: [
+        "blueprint",
+        "blueprint-pathtools",
+        "soong",
+        "soong-android",
+        "soong-shared",
+    ],
+    srcs: [
+        "genrule.go",
+    ],
+    testSrcs: [
+        "genrule_test.go",
+    ],
+    pluginFor: ["soong_build"],
+}
diff --git a/java/Android.bp b/java/Android.bp
new file mode 100644
index 0000000..2de1b8e
--- /dev/null
+++ b/java/Android.bp
@@ -0,0 +1,66 @@
+bootstrap_go_package {
+    name: "soong-java",
+    pkgPath: "android/soong/java",
+    deps: [
+        "blueprint",
+        "blueprint-pathtools",
+        "soong",
+        "soong-android",
+        "soong-cc",
+        "soong-dexpreopt",
+        "soong-genrule",
+        "soong-java-config",
+        "soong-remoteexec",
+        "soong-tradefed",
+    ],
+    srcs: [
+        "aapt2.go",
+        "aar.go",
+        "android_manifest.go",
+        "android_resources.go",
+        "androidmk.go",
+        "app_builder.go",
+        "app.go",
+        "builder.go",
+        "device_host_converter.go",
+        "dex.go",
+        "dexpreopt.go",
+        "dexpreopt_bootjars.go",
+        "dexpreopt_config.go",
+        "droiddoc.go",
+        "gen.go",
+        "genrule.go",
+        "hiddenapi.go",
+        "hiddenapi_singleton.go",
+        "jacoco.go",
+        "java.go",
+        "jdeps.go",
+        "java_resources.go",
+        "kotlin.go",
+        "platform_compat_config.go",
+        "plugin.go",
+        "prebuilt_apis.go",
+        "proto.go",
+        "robolectric.go",
+        "sdk.go",
+        "sdk_library.go",
+        "support_libraries.go",
+        "sysprop.go",
+        "system_modules.go",
+        "testing.go",
+        "tradefed.go",
+    ],
+    testSrcs: [
+        "androidmk_test.go",
+        "app_test.go",
+        "device_host_converter_test.go",
+        "dexpreopt_test.go",
+        "dexpreopt_bootjars_test.go",
+        "java_test.go",
+        "jdeps_test.go",
+        "kotlin_test.go",
+        "plugin_test.go",
+        "sdk_test.go",
+    ],
+    pluginFor: ["soong_build"],
+}
diff --git a/java/aar.go b/java/aar.go
index c8daf83..28e388a 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -730,7 +730,11 @@
 	return android.Paths{a.classpathFile}
 }
 
-func (a *AARImport) DexJar() android.Path {
+func (a *AARImport) DexJarBuildPath() android.Path {
+	return nil
+}
+
+func (a *AARImport) DexJarInstallPath() android.Path {
 	return nil
 }
 
diff --git a/java/androidmk.go b/java/androidmk.go
index 5e3b7d2..62cf169 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -69,7 +69,26 @@
 	if !library.ApexModuleBase.AvailableFor(android.AvailableToPlatform) {
 		hideFromMake = true
 	}
-	if !hideFromMake {
+	if hideFromMake {
+		// May still need to add some additional dependencies. This will be called
+		// once for the platform variant (even if it is not being used) and once each
+		// for the APEX specific variants. In order to avoid adding the dependency
+		// multiple times only add it for the platform variant.
+		checkedModulePaths := library.additionalCheckedModules
+		if library.IsForPlatform() && len(checkedModulePaths) != 0 {
+			mainEntries = android.AndroidMkEntries{
+				Class: "FAKE",
+				// Need at least one output file in order for this to take effect.
+				OutputFile: android.OptionalPathForPath(checkedModulePaths[0]),
+				Include:    "$(BUILD_PHONY_PACKAGE)",
+				ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+					func(entries *android.AndroidMkEntries) {
+						entries.AddStrings("LOCAL_ADDITIONAL_CHECKED_MODULE", checkedModulePaths.Strings()...)
+					},
+				},
+			}
+		}
+	} else {
 		mainEntries = android.AndroidMkEntries{
 			Class:      "JAVA_LIBRARIES",
 			DistFile:   android.OptionalPathForPath(library.distFile),
diff --git a/java/app.go b/java/app.go
index 6aa8cd6..7a109be 100755
--- a/java/app.go
+++ b/java/app.go
@@ -28,6 +28,7 @@
 
 	"android/soong/android"
 	"android/soong/cc"
+	"android/soong/dexpreopt"
 	"android/soong/tradefed"
 )
 
@@ -96,6 +97,14 @@
 	return Bool(as.properties.Privileged)
 }
 
+func (as *AndroidAppSet) OutputFile() android.Path {
+	return as.packedOutput
+}
+
+func (as *AndroidAppSet) MasterFile() string {
+	return as.masterFile
+}
+
 var TargetCpuAbi = map[string]string{
 	"arm":    "ARMEABI_V7A",
 	"arm64":  "ARM64_V8A",
@@ -120,7 +129,7 @@
 }
 
 func (as *AndroidAppSet) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	as.packedOutput = android.PathForModuleOut(ctx, "extracted.zip")
+	as.packedOutput = android.PathForModuleOut(ctx, ctx.ModuleName()+".zip")
 	// We are assuming here that the master file in the APK
 	// set has `.apk` suffix. If it doesn't the build will fail.
 	// APK sets containing APEX files are handled elsewhere.
@@ -145,26 +154,17 @@
 				"stem":              ctx.ModuleName(),
 			},
 		})
-	// TODO(asmundak): add this (it's wrong now, will cause copying extracted.zip)
-	/*
-		var installDir android.InstallPath
-		if Bool(as.properties.Privileged) {
-			installDir = android.PathForModuleInstall(ctx, "priv-app", as.BaseModuleName())
-		} else if ctx.InstallInTestcases() {
-			installDir = android.PathForModuleInstall(ctx, as.BaseModuleName(), ctx.DeviceConfig().DeviceArch())
-		} else {
-			installDir = android.PathForModuleInstall(ctx, "app", as.BaseModuleName())
-		}
-		ctx.InstallFile(installDir, as.masterFile", as.packedOutput)
-	*/
 }
 
 // android_app_set extracts a set of APKs based on the target device
 // configuration and installs this set as "split APKs".
-// The set will always contain `base-master.apk` and every APK built
-// to the target device. All density-specific APK will be included, too,
-// unless PRODUCT_APPT_PREBUILT_DPI is defined (should contain comma-sepearated
-// list of density names (LDPI, MDPI, HDPI, etc.)
+// The extracted set always contains 'master' APK whose name is
+// _module_name_.apk and every split APK matching target device.
+// The extraction of the density-specific splits depends on
+// PRODUCT_AAPT_PREBUILT_DPI variable. If present (its value should
+// be a list density names: LDPI, MDPI, HDPI, etc.), only listed
+// splits will be extracted. Otherwise all density-specific splits
+// will be extracted.
 func AndroidApkSetFactory() android.Module {
 	module := &AndroidAppSet{}
 	module.AddProperties(&module.properties)
@@ -335,7 +335,7 @@
 	presigned bool
 }
 
-var presignedCertificate = Certificate{presigned: true}
+var PresignedCertificate = Certificate{presigned: true}
 
 func (c Certificate) AndroidMkString() string {
 	if c.presigned {
@@ -445,8 +445,11 @@
 			return
 		}
 		dep, _ := m.(*cc.Module)
-		jniSdkVersion, err := android.ApiStrToNum(ctx, dep.SdkVersion())
-		if err != nil || int(minSdkVersion) < jniSdkVersion {
+		// The domain of cc.sdk_version is "current" and <number>
+		// We can rely on sdkSpec to convert it to <number> so that "current" is handled
+		// properly regardless of sdk finalization.
+		jniSdkVersion, err := sdkSpecFrom(dep.SdkVersion()).effectiveVersion(ctx)
+		if err != nil || minSdkVersion < jniSdkVersion {
 			ctx.OtherModuleErrorf(dep, "sdk_version(%v) is higher than min_sdk_version(%v) of the containing android_app(%v)",
 				dep.SdkVersion(), minSdkVersion, ctx.ModuleName())
 			return
@@ -970,10 +973,6 @@
 		&module.overridableAppProperties,
 		&module.usesLibrary.usesLibraryProperties)
 
-	module.Prefer32(func(ctx android.BaseModuleContext, base *android.ModuleBase, class android.OsClass) bool {
-		return class == android.Device && ctx.Config().DevicePrefer32BitApps()
-	})
-
 	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
 	android.InitDefaultableModule(module)
 	android.InitOverridableModule(module, &module.appProperties.Overrides)
@@ -1186,7 +1185,7 @@
 	android.OverrideModuleBase
 }
 
-func (i *OverrideAndroidApp) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+func (i *OverrideAndroidApp) GenerateAndroidBuildActions(_ android.ModuleContext) {
 	// All the overrides happen in the base module.
 	// TODO(jungjw): Check the base module type.
 }
@@ -1207,7 +1206,7 @@
 	android.OverrideModuleBase
 }
 
-func (i *OverrideAndroidTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+func (i *OverrideAndroidTest) GenerateAndroidBuildActions(_ android.ModuleContext) {
 	// All the overrides happen in the base module.
 	// TODO(jungjw): Check the base module type.
 }
@@ -1229,7 +1228,7 @@
 	android.OverrideModuleBase
 }
 
-func (i *OverrideRuntimeResourceOverlay) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+func (i *OverrideRuntimeResourceOverlay) GenerateAndroidBuildActions(_ android.ModuleContext) {
 	// All the overrides happen in the base module.
 	// TODO(jungjw): Check the base module type.
 }
@@ -1474,7 +1473,7 @@
 	// Sign or align the package if package has not been preprocessed
 	if a.preprocessed {
 		a.outputFile = srcApk
-		a.certificate = presignedCertificate
+		a.certificate = PresignedCertificate
 	} else if !Bool(a.properties.Presigned) {
 		// If the certificate property is empty at this point, default_dev_cert must be set to true.
 		// Which makes processMainCert's behavior for the empty cert string WAI.
@@ -1494,7 +1493,7 @@
 		alignedApk := android.PathForModuleOut(ctx, "zip-aligned", apkFilename)
 		TransformZipAlign(ctx, alignedApk, dexOutput)
 		a.outputFile = alignedApk
-		a.certificate = presignedCertificate
+		a.certificate = PresignedCertificate
 	}
 
 	// TODO: Optionally compress the output apk.
@@ -1552,7 +1551,7 @@
 	return Bool(a.properties.Privileged)
 }
 
-func (a *AndroidAppImport) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
+func (a *AndroidAppImport) DepIsInSameApex(_ android.BaseModuleContext, _ android.Module) bool {
 	// android_app_import might have extra dependencies via uses_libs property.
 	// Don't track the dependency as we don't automatically add those libraries
 	// to the classpath. It should be explicitly added to java_libs property of APEX
@@ -1863,6 +1862,7 @@
 				"org.apache.http.legacy",
 				"android.hidl.base-V1.0-java",
 				"android.hidl.manager-V1.0-java")
+			ctx.AddVariationDependencies(nil, usesLibTag, optionalUsesLibs...)
 		}
 	}
 }
@@ -1874,24 +1874,36 @@
 	return optionalUsesLibs
 }
 
-// usesLibraryPaths returns a map of module names of shared library dependencies to the paths to their dex jars.
-func (u *usesLibrary) usesLibraryPaths(ctx android.ModuleContext) map[string]android.Path {
-	usesLibPaths := make(map[string]android.Path)
+// usesLibraryPaths returns a map of module names of shared library dependencies to the paths
+// to their dex jars on host and on device.
+func (u *usesLibrary) usesLibraryPaths(ctx android.ModuleContext) dexpreopt.LibraryPaths {
+	usesLibPaths := make(dexpreopt.LibraryPaths)
 
 	if !ctx.Config().UnbundledBuild() {
 		ctx.VisitDirectDepsWithTag(usesLibTag, func(m android.Module) {
+			dep := ctx.OtherModuleName(m)
 			if lib, ok := m.(Dependency); ok {
-				if dexJar := lib.DexJar(); dexJar != nil {
-					usesLibPaths[ctx.OtherModuleName(m)] = dexJar
-				} else {
-					ctx.ModuleErrorf("module %q in uses_libs or optional_uses_libs must produce a dex jar, does it have installable: true?",
-						ctx.OtherModuleName(m))
+				buildPath := lib.DexJarBuildPath()
+				if buildPath == nil {
+					ctx.ModuleErrorf("module %q in uses_libs or optional_uses_libs must"+
+						" produce a dex jar, does it have installable: true?", dep)
+					return
 				}
+
+				var devicePath string
+				installPath := lib.DexJarInstallPath()
+				if installPath == nil {
+					devicePath = filepath.Join("/system/framework", dep+".jar")
+				} else {
+					devicePath = android.InstallPathToOnDevicePath(ctx, installPath.(android.InstallPath))
+				}
+
+				usesLibPaths[dep] = &dexpreopt.LibraryPath{buildPath, devicePath}
 			} else if ctx.Config().AllowMissingDependencies() {
-				ctx.AddMissingDependencies([]string{ctx.OtherModuleName(m)})
+				ctx.AddMissingDependencies([]string{dep})
 			} else {
-				ctx.ModuleErrorf("module %q in uses_libs or optional_uses_libs must be a java library",
-					ctx.OtherModuleName(m))
+				ctx.ModuleErrorf("module %q in uses_libs or optional_uses_libs must be "+
+					"a java library", dep)
 			}
 		})
 	}
diff --git a/java/app_builder.go b/java/app_builder.go
index fb9ab42..014bd54 100644
--- a/java/app_builder.go
+++ b/java/app_builder.go
@@ -26,16 +26,23 @@
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
+	"android/soong/remoteexec"
 )
 
 var (
-	Signapk = pctx.AndroidStaticRule("signapk",
+	Signapk, SignapkRE = remoteexec.StaticRules(pctx, "signapk",
 		blueprint.RuleParams{
-			Command: `${config.JavaCmd} ${config.JavaVmFlags} -Djava.library.path=$$(dirname ${config.SignapkJniLibrary}) ` +
+			Command: `$reTemplate${config.JavaCmd} ${config.JavaVmFlags} -Djava.library.path=$$(dirname ${config.SignapkJniLibrary}) ` +
 				`-jar ${config.SignapkCmd} $flags $certificates $in $out`,
 			CommandDeps: []string{"${config.SignapkCmd}", "${config.SignapkJniLibrary}"},
 		},
-		"flags", "certificates")
+		&remoteexec.REParams{Labels: map[string]string{"type": "tool", "name": "signapk"},
+			ExecStrategy:    "${config.RESignApkExecStrategy}",
+			Inputs:          []string{"${config.SignapkCmd}", "$in", "$$(dirname ${config.SignapkJniLibrary})", "$implicits"},
+			OutputFiles:     []string{"$outCommaList"},
+			ToolchainInputs: []string{"${config.JavaCmd}"},
+			Platform:        map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
+		}, []string{"flags", "certificates"}, []string{"implicits", "outCommaList"})
 )
 
 var combineApk = pctx.AndroidStaticRule("combineApk",
@@ -78,22 +85,30 @@
 		deps = append(deps, c.Pem, c.Key)
 	}
 
+	outputFiles := android.WritablePaths{signedApk}
 	var flags []string
 	if lineageFile != nil {
 		flags = append(flags, "--lineage", lineageFile.String())
 		deps = append(deps, lineageFile)
 	}
 
+	rule := Signapk
+	args := map[string]string{
+		"certificates": strings.Join(certificateArgs, " "),
+		"flags":        strings.Join(flags, " "),
+	}
+	if ctx.Config().IsEnvTrue("RBE_SIGNAPK") {
+		rule = SignapkRE
+		args["implicits"] = strings.Join(deps.Strings(), ",")
+		args["outCommaList"] = strings.Join(outputFiles.Strings(), ",")
+	}
 	ctx.Build(pctx, android.BuildParams{
-		Rule:        Signapk,
+		Rule:        rule,
 		Description: "signapk",
-		Output:      signedApk,
+		Outputs:     outputFiles,
 		Input:       unsignedApk,
 		Implicits:   deps,
-		Args: map[string]string{
-			"certificates": strings.Join(certificateArgs, " "),
-			"flags":        strings.Join(flags, " "),
-		},
+		Args:        args,
 	})
 }
 
@@ -217,14 +232,20 @@
 			"-f", j.path.String())
 	}
 
+	rule := zip
+	args := map[string]string{
+		"jarArgs": strings.Join(proptools.NinjaAndShellEscapeList(jarArgs), " "),
+	}
+	if ctx.Config().IsEnvTrue("RBE_ZIP") {
+		rule = zipRE
+		args["implicits"] = strings.Join(deps.Strings(), ",")
+	}
 	ctx.Build(pctx, android.BuildParams{
-		Rule:        zip,
+		Rule:        rule,
 		Description: "zip jni libs",
 		Output:      outputFile,
 		Implicits:   deps,
-		Args: map[string]string{
-			"jarArgs": strings.Join(proptools.NinjaAndShellEscapeList(jarArgs), " "),
-		},
+		Args:        args,
 	})
 }
 
diff --git a/java/app_test.go b/java/app_test.go
index eb583be..eeba161 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -149,7 +149,7 @@
 			prerelease: true,
         }`)
 	module := ctx.ModuleForTests("foo", "android_common")
-	const packedSplitApks = "extracted.zip"
+	const packedSplitApks = "foo.zip"
 	params := module.Output(packedSplitApks)
 	if params.Rule == nil {
 		t.Errorf("expected output %s is missing", packedSplitApks)
@@ -218,7 +218,7 @@
 		ctx := testContext()
 		run(t, ctx, config)
 		module := ctx.ModuleForTests("foo", "android_common")
-		const packedSplitApks = "extracted.zip"
+		const packedSplitApks = "foo.zip"
 		params := module.Output(packedSplitApks)
 		for k, v := range test.expected {
 			if actual := params.Args[k]; actual != v {
diff --git a/java/builder.go b/java/builder.go
index b6cb2ae..a27e5c3 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -40,17 +40,17 @@
 	// (if the rule produces .class files) or a .srcjar file (if the rule produces .java files).
 	// .srcjar files are unzipped into a temporary directory when compiled with javac.
 	// TODO(b/143658984): goma can't handle the --system argument to javac.
-	javac, javacRE = remoteexec.StaticRules(pctx, "javac",
+	javac, javacRE = remoteexec.MultiCommandStaticRules(pctx, "javac",
 		blueprint.RuleParams{
 			Command: `rm -rf "$outDir" "$annoDir" "$srcJarDir" && mkdir -p "$outDir" "$annoDir" "$srcJarDir" && ` +
 				`${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
 				`(if [ -s $srcJarDir/list ] || [ -s $out.rsp ] ; then ` +
-				`${config.SoongJavacWrapper} $reTemplate${config.JavacCmd} ` +
+				`${config.SoongJavacWrapper} $javaTemplate${config.JavacCmd} ` +
 				`${config.JavacHeapFlags} ${config.JavacVmFlags} ${config.CommonJdkFlags} ` +
 				`$processorpath $processor $javacFlags $bootClasspath $classpath ` +
 				`-source $javaVersion -target $javaVersion ` +
 				`-d $outDir -s $annoDir @$out.rsp @$srcJarDir/list ; fi ) && ` +
-				`${config.SoongZipCmd} -jar -o $out -C $outDir -D $outDir && ` +
+				`$zipTemplate${config.SoongZipCmd} -jar -o $out -C $outDir -D $outDir && ` +
 				`rm -rf "$srcJarDir"`,
 			CommandDeps: []string{
 				"${config.JavacCmd}",
@@ -60,10 +60,19 @@
 			CommandOrderOnly: []string{"${config.SoongJavacWrapper}"},
 			Rspfile:          "$out.rsp",
 			RspfileContent:   "$in",
-		}, &remoteexec.REParams{
-			Labels:       map[string]string{"type": "compile", "lang": "java", "compiler": "javac"},
-			ExecStrategy: "${config.REJavacExecStrategy}",
-			Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
+		}, map[string]*remoteexec.REParams{
+			"$javaTemplate": &remoteexec.REParams{
+				Labels:       map[string]string{"type": "compile", "lang": "java", "compiler": "javac"},
+				ExecStrategy: "${config.REJavacExecStrategy}",
+				Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
+			},
+			"$zipTemplate": &remoteexec.REParams{
+				Labels:       map[string]string{"type": "tool", "name": "soong_zip"},
+				Inputs:       []string{"${config.SoongZipCmd}", "$outDir"},
+				OutputFiles:  []string{"$out"},
+				ExecStrategy: "${config.REJavacExecStrategy}",
+				Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
+			},
 		}, []string{"javacFlags", "bootClasspath", "classpath", "processorpath", "processor", "srcJars", "srcJarDir",
 			"outDir", "annoDir", "javaVersion"}, nil)
 
@@ -144,23 +153,35 @@
 			Platform:          map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
 		}, []string{"javacFlags", "bootClasspath", "classpath", "srcJars", "outDir", "javaVersion"}, []string{"implicits"})
 
-	jar = pctx.AndroidStaticRule("jar",
+	jar, jarRE = remoteexec.StaticRules(pctx, "jar",
 		blueprint.RuleParams{
-			Command:        `${config.SoongZipCmd} -jar -o $out @$out.rsp`,
+			Command:        `$reTemplate${config.SoongZipCmd} -jar -o $out @$out.rsp`,
 			CommandDeps:    []string{"${config.SoongZipCmd}"},
 			Rspfile:        "$out.rsp",
 			RspfileContent: "$jarArgs",
 		},
-		"jarArgs")
+		&remoteexec.REParams{
+			ExecStrategy: "${config.REJarExecStrategy}",
+			Inputs:       []string{"${config.SoongZipCmd}", "${out}.rsp"},
+			RSPFile:      "${out}.rsp",
+			OutputFiles:  []string{"$out"},
+			Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
+		}, []string{"jarArgs"}, nil)
 
-	zip = pctx.AndroidStaticRule("zip",
+	zip, zipRE = remoteexec.StaticRules(pctx, "zip",
 		blueprint.RuleParams{
 			Command:        `${config.SoongZipCmd} -o $out @$out.rsp`,
 			CommandDeps:    []string{"${config.SoongZipCmd}"},
 			Rspfile:        "$out.rsp",
 			RspfileContent: "$jarArgs",
 		},
-		"jarArgs")
+		&remoteexec.REParams{
+			ExecStrategy: "${config.REZipExecStrategy}",
+			Inputs:       []string{"${config.SoongZipCmd}", "${out}.rsp", "$implicits"},
+			RSPFile:      "${out}.rsp",
+			OutputFiles:  []string{"$out"},
+			Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
+		}, []string{"jarArgs"}, []string{"implicits"})
 
 	combineJar = pctx.AndroidStaticRule("combineJar",
 		blueprint.RuleParams{
@@ -457,8 +478,12 @@
 func TransformResourcesToJar(ctx android.ModuleContext, outputFile android.WritablePath,
 	jarArgs []string, deps android.Paths) {
 
+	rule := jar
+	if ctx.Config().IsEnvTrue("RBE_JAR") {
+		rule = jarRE
+	}
 	ctx.Build(pctx, android.BuildParams{
-		Rule:        jar,
+		Rule:        rule,
 		Description: "jar",
 		Output:      outputFile,
 		Implicits:   deps,
diff --git a/java/config/Android.bp b/java/config/Android.bp
new file mode 100644
index 0000000..1983521
--- /dev/null
+++ b/java/config/Android.bp
@@ -0,0 +1,15 @@
+bootstrap_go_package {
+    name: "soong-java-config",
+    pkgPath: "android/soong/java/config",
+    deps: [
+        "blueprint-proptools",
+        "soong-android",
+        "soong-remoteexec",
+    ],
+    srcs: [
+        "config.go",
+        "error_prone.go",
+        "kotlin.go",
+        "makevars.go",
+    ],
+}
diff --git a/java/config/config.go b/java/config/config.go
index fa19afb..bb5be3a 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -28,8 +28,9 @@
 var (
 	pctx = android.NewPackageContext("android/soong/java/config")
 
-	DefaultBootclasspathLibraries = []string{"core.platform.api.stubs", "core-lambda-stubs"}
-	DefaultSystemModules          = "core-platform-api-stubs-system-modules"
+	// TODO(b/157640067): Don't depend on the legacy API by default in the long term.
+	DefaultBootclasspathLibraries = []string{"legacy.core.platform.api.stubs", "core-lambda-stubs"}
+	DefaultSystemModules          = "legacy-core-platform-api-stubs-system-modules"
 	DefaultLibraries              = []string{"ext", "framework"}
 	DefaultLambdaStubsLibrary     = "core-lambda-stubs"
 	SdkLambdaStubsPath            = "prebuilts/sdk/tools/core-lambda-stubs.jar"
@@ -149,6 +150,9 @@
 	pctx.VariableFunc("RED8ExecStrategy", remoteexec.EnvOverrideFunc("RBE_D8_EXEC_STRATEGY", remoteexec.LocalExecStrategy))
 	pctx.VariableFunc("RER8ExecStrategy", remoteexec.EnvOverrideFunc("RBE_R8_EXEC_STRATEGY", remoteexec.LocalExecStrategy))
 	pctx.VariableFunc("RETurbineExecStrategy", remoteexec.EnvOverrideFunc("RBE_TURBINE_EXEC_STRATEGY", remoteexec.LocalExecStrategy))
+	pctx.VariableFunc("RESignApkExecStrategy", remoteexec.EnvOverrideFunc("RBE_SIGNAPK_EXEC_STRATEGY", remoteexec.LocalExecStrategy))
+	pctx.VariableFunc("REJarExecStrategy", remoteexec.EnvOverrideFunc("RBE_JAR_EXEC_STRATEGY", remoteexec.LocalExecStrategy))
+	pctx.VariableFunc("REZipExecStrategy", remoteexec.EnvOverrideFunc("RBE_ZIP_EXEC_STRATEGY", remoteexec.LocalExecStrategy))
 
 	pctx.HostJavaToolVariable("JacocoCLIJar", "jacoco-cli.jar")
 
diff --git a/java/device_host_converter.go b/java/device_host_converter.go
index b40ab93..877fd8a 100644
--- a/java/device_host_converter.go
+++ b/java/device_host_converter.go
@@ -150,7 +150,11 @@
 	return d.implementationAndResourceJars
 }
 
-func (d *DeviceHostConverter) DexJar() android.Path {
+func (d *DeviceHostConverter) DexJarBuildPath() android.Path {
+	return nil
+}
+
+func (d *DeviceHostConverter) DexJarInstallPath() android.Path {
 	return nil
 }
 
diff --git a/java/dex.go b/java/dex.go
index e2a8e7e..9e61e95 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -24,48 +24,66 @@
 	"android/soong/remoteexec"
 )
 
-var d8, d8RE = remoteexec.StaticRules(pctx, "d8",
+var d8, d8RE = remoteexec.MultiCommandStaticRules(pctx, "d8",
 	blueprint.RuleParams{
 		Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
-			`$reTemplate${config.D8Cmd} ${config.DexFlags} --output $outDir $d8Flags $in && ` +
-			`${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
+			`$d8Template${config.D8Cmd} ${config.DexFlags} --output $outDir $d8Flags $in && ` +
+			`$zipTemplate${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
 			`${config.MergeZipsCmd} -D -stripFile "**/*.class" $out $outDir/classes.dex.jar $in`,
 		CommandDeps: []string{
 			"${config.D8Cmd}",
 			"${config.SoongZipCmd}",
 			"${config.MergeZipsCmd}",
 		},
-	}, &remoteexec.REParams{
-		Labels:          map[string]string{"type": "compile", "compiler": "d8"},
-		Inputs:          []string{"${config.D8Jar}"},
-		ExecStrategy:    "${config.RED8ExecStrategy}",
-		ToolchainInputs: []string{"${config.JavaCmd}"},
-		Platform:        map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
+	}, map[string]*remoteexec.REParams{
+		"$d8Template": &remoteexec.REParams{
+			Labels:          map[string]string{"type": "compile", "compiler": "d8"},
+			Inputs:          []string{"${config.D8Jar}"},
+			ExecStrategy:    "${config.RED8ExecStrategy}",
+			ToolchainInputs: []string{"${config.JavaCmd}"},
+			Platform:        map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
+		},
+		"$zipTemplate": &remoteexec.REParams{
+			Labels:       map[string]string{"type": "tool", "name": "soong_zip"},
+			Inputs:       []string{"${config.SoongZipCmd}", "$outDir"},
+			OutputFiles:  []string{"$outDir/classes.dex.jar"},
+			ExecStrategy: "${config.RED8ExecStrategy}",
+			Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
+		},
 	}, []string{"outDir", "d8Flags", "zipFlags"}, nil)
 
-var r8, r8RE = remoteexec.StaticRules(pctx, "r8",
+var r8, r8RE = remoteexec.MultiCommandStaticRules(pctx, "r8",
 	blueprint.RuleParams{
 		Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
 			`rm -f "$outDict" && ` +
-			`$reTemplate${config.R8Cmd} ${config.DexFlags} -injars $in --output $outDir ` +
+			`$r8Template${config.R8Cmd} ${config.DexFlags} -injars $in --output $outDir ` +
 			`--force-proguard-compatibility ` +
 			`--no-data-resources ` +
 			`-printmapping $outDict ` +
 			`$r8Flags && ` +
 			`touch "$outDict" && ` +
-			`${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
+			`$zipTemplate${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
 			`${config.MergeZipsCmd} -D -stripFile "**/*.class" $out $outDir/classes.dex.jar $in`,
 		CommandDeps: []string{
 			"${config.R8Cmd}",
 			"${config.SoongZipCmd}",
 			"${config.MergeZipsCmd}",
 		},
-	}, &remoteexec.REParams{
-		Labels:          map[string]string{"type": "compile", "compiler": "r8"},
-		Inputs:          []string{"$implicits", "${config.R8Jar}"},
-		ExecStrategy:    "${config.RER8ExecStrategy}",
-		ToolchainInputs: []string{"${config.JavaCmd}"},
-		Platform:        map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
+	}, map[string]*remoteexec.REParams{
+		"$r8Template": &remoteexec.REParams{
+			Labels:          map[string]string{"type": "compile", "compiler": "r8"},
+			Inputs:          []string{"$implicits", "${config.R8Jar}"},
+			ExecStrategy:    "${config.RER8ExecStrategy}",
+			ToolchainInputs: []string{"${config.JavaCmd}"},
+			Platform:        map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
+		},
+		"$zipTemplate": &remoteexec.REParams{
+			Labels:       map[string]string{"type": "tool", "name": "soong_zip"},
+			Inputs:       []string{"${config.SoongZipCmd}", "$outDir"},
+			OutputFiles:  []string{"$outDir/classes.dex.jar"},
+			ExecStrategy: "${config.RER8ExecStrategy}",
+			Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
+		},
 	}, []string{"outDir", "outDict", "r8Flags", "zipFlags"}, []string{"implicits"})
 
 func (j *Module) dexCommonFlags(ctx android.ModuleContext) []string {
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index 4725b07..c33415e 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -37,7 +37,7 @@
 	usesLibs         []string
 	optionalUsesLibs []string
 	enforceUsesLibs  bool
-	libraryPaths     map[string]android.Path
+	libraryPaths     dexpreopt.LibraryPaths
 
 	builtInstalled string
 }
@@ -77,10 +77,6 @@
 		return true
 	}
 
-	if ctx.Config().UnbundledBuild() {
-		return true
-	}
-
 	if d.isTest {
 		return true
 	}
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index 90457d0..4120559 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -179,15 +179,7 @@
 }
 
 func skipDexpreoptBootJars(ctx android.PathContext) bool {
-	if dexpreopt.GetGlobalConfig(ctx).DisablePreopt {
-		return true
-	}
-
-	if ctx.Config().UnbundledBuild() {
-		return true
-	}
-
-	return false
+	return dexpreopt.GetGlobalConfig(ctx).DisablePreopt
 }
 
 type dexpreoptBootJars struct {
@@ -255,7 +247,7 @@
 		return -1, nil
 	}
 
-	jar, hasJar := module.(interface{ DexJar() android.Path })
+	jar, hasJar := module.(interface{ DexJarBuildPath() android.Path })
 	if !hasJar {
 		return -1, nil
 	}
@@ -296,7 +288,7 @@
 		panic("unknown boot image: " + image.name)
 	}
 
-	return index, jar.DexJar()
+	return index, jar.DexJarBuildPath()
 }
 
 // buildBootImage takes a bootImageConfig, creates rules to build it, and returns the image.
@@ -340,10 +332,12 @@
 	bootFrameworkProfileRule(ctx, image, missingDeps)
 	updatableBcpPackagesRule(ctx, image, missingDeps)
 
-	var allFiles android.Paths
+	var zipFiles android.Paths
 	for _, variant := range image.variants {
 		files := buildBootImageVariant(ctx, variant, profile, missingDeps)
-		allFiles = append(allFiles, files.Paths()...)
+		if variant.target.Os == android.Android {
+			zipFiles = append(zipFiles, files.Paths()...)
+		}
 	}
 
 	if image.zip != nil {
@@ -351,8 +345,8 @@
 		rule.Command().
 			BuiltTool(ctx, "soong_zip").
 			FlagWithOutput("-o ", image.zip).
-			FlagWithArg("-C ", image.dir.String()).
-			FlagWithInputList("-f ", allFiles, " -f ")
+			FlagWithArg("-C ", image.dir.Join(ctx, android.Android.String()).String()).
+			FlagWithInputList("-f ", zipFiles, " -f ")
 
 		rule.Build(pctx, ctx, "zip_"+image.name, "zip "+image.name+" image")
 	}
@@ -619,10 +613,10 @@
 		// Collect `permitted_packages` for updatable boot jars.
 		var updatablePackages []string
 		ctx.VisitAllModules(func(module android.Module) {
-			if j, ok := module.(*Library); ok {
+			if j, ok := module.(PermittedPackagesForUpdatableBootJars); ok {
 				name := ctx.ModuleName(module)
 				if i := android.IndexList(name, updatableModules); i != -1 {
-					pp := j.properties.Permitted_packages
+					pp := j.PermittedPackagesForUpdatableBootJars()
 					if len(pp) > 0 {
 						updatablePackages = append(updatablePackages, pp...)
 					} else {
diff --git a/java/dexpreopt_bootjars_test.go b/java/dexpreopt_bootjars_test.go
index e9704dc..9670c7f 100644
--- a/java/dexpreopt_bootjars_test.go
+++ b/java/dexpreopt_bootjars_test.go
@@ -118,7 +118,7 @@
 
 	ctx := android.PathContextForTesting(testConfig(nil, "", nil))
 	expectedInputs := []string{}
-	for _, target := range dexpreoptTargets(ctx) {
+	for _, target := range ctx.Config().Targets[android.Android] {
 		for _, ext := range []string{".art", ".oat", ".vdex"} {
 			for _, jar := range []string{"foo", "bar", "baz"} {
 				expectedInputs = append(expectedInputs,
diff --git a/java/droiddoc.go b/java/droiddoc.go
index b16c9cd..4c3e112 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -1334,13 +1334,10 @@
 		d.annotationsZip = android.PathForModuleOut(ctx, ctx.ModuleName()+"_annotations.zip")
 		cmd.FlagWithOutput("--extract-annotations ", d.annotationsZip)
 
-		if len(d.properties.Merge_annotations_dirs) == 0 {
-			ctx.PropertyErrorf("merge_annotations_dirs",
-				"has to be non-empty if annotations was enabled!")
+		if len(d.properties.Merge_annotations_dirs) != 0 {
+			d.mergeAnnoDirFlags(ctx, cmd)
 		}
 
-		d.mergeAnnoDirFlags(ctx, cmd)
-
 		// TODO(tnorbye): find owners to fix these warnings when annotation was enabled.
 		cmd.FlagWithArg("--hide ", "HiddenTypedefConstant").
 			FlagWithArg("--hide ", "SuperfluousPrefix").
@@ -1427,10 +1424,9 @@
 	rule.HighMem()
 	cmd := rule.Command()
 
-	rspFile := ""
+	var implicitsRsp android.WritablePath
 	if len(implicits) > 0 {
-		implicitsRsp := android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"implicits.rsp")
-		rspFile = implicitsRsp.String()
+		implicitsRsp = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"implicits.rsp")
 		impRule := android.NewRuleBuilder()
 		impCmd := impRule.Command()
 		// A dummy action that copies the ninja generated rsp file to a new location. This allows us to
@@ -1457,10 +1453,10 @@
 			inputs = append(inputs, strings.Split(v, ",")...)
 		}
 		cmd.Text((&remoteexec.REParams{
-			Labels:          map[string]string{"type": "compile", "lang": "java", "compiler": "metalava"},
+			Labels:          map[string]string{"type": "compile", "lang": "java", "compiler": "metalava", "shallow": "true"},
 			ExecStrategy:    execStrategy,
 			Inputs:          inputs,
-			RSPFile:         rspFile,
+			RSPFile:         implicitsRsp.String(),
 			ToolchainInputs: []string{config.JavaCmd(ctx).String()},
 			Platform:        map[string]string{remoteexec.PoolKey: pool},
 		}).NoVarTemplate(ctx.Config()))
@@ -1471,7 +1467,12 @@
 		FlagWithArg("-encoding ", "UTF-8").
 		FlagWithArg("-source ", javaVersion.String()).
 		FlagWithRspFileInputList("@", srcs).
-		FlagWithInput("@", srcJarList)
+		FlagWithInput("@", srcJarList).
+		FlagWithOutput("--strict-input-files:warn ", android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"violations.txt"))
+
+	if implicitsRsp.String() != "" {
+		cmd.FlagWithArg("--strict-input-files-exempt ", "@"+implicitsRsp.String())
+	}
 
 	if len(bootclasspath) > 0 {
 		cmd.FlagWithInputList("-bootclasspath ", bootclasspath.Paths(), ":")
diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go
index c7f7cbd..bff591c 100644
--- a/java/hiddenapi_singleton.go
+++ b/java/hiddenapi_singleton.go
@@ -116,7 +116,7 @@
 
 	// Core Platform API stubs
 	corePlatformStubModules := []string{
-		"core.platform.api.stubs",
+		"legacy.core.platform.api.stubs",
 	}
 
 	// Allow products to define their own stubs for custom product jars that apps can use.
@@ -147,7 +147,7 @@
 			name := ctx.ModuleName(module)
 			for moduleList, pathList := range moduleListToPathList {
 				if i := android.IndexList(name, *moduleList); i != -1 {
-					pathList[i] = j.DexJar()
+					pathList[i] = j.DexJarBuildPath()
 				}
 			}
 		}
diff --git a/java/java.go b/java/java.go
index 466132e..5f8ad03 100644
--- a/java/java.go
+++ b/java/java.go
@@ -501,7 +501,8 @@
 	ImplementationJars() android.Paths
 	ResourceJars() android.Paths
 	ImplementationAndResourcesJars() android.Paths
-	DexJar() android.Path
+	DexJarBuildPath() android.Path
+	DexJarInstallPath() android.Path
 	AidlIncludeDirs() android.Paths
 	ExportedSdkLibs() []string
 	ExportedPlugins() (android.Paths, []string)
@@ -836,41 +837,47 @@
 }
 
 func (m *Module) getLinkType(name string) (ret linkType, stubs bool) {
-	ver := m.sdkVersion()
-	switch {
-	case name == "core.current.stubs" || name == "core.platform.api.stubs" ||
-		name == "stub-annotations" || name == "private-stub-annotations-jar" ||
-		name == "core-lambda-stubs" || name == "core-generated-annotation-stubs":
+	switch name {
+	case "core.current.stubs", "legacy.core.platform.api.stubs", "stable.core.platform.api.stubs",
+		"stub-annotations", "private-stub-annotations-jar",
+		"core-lambda-stubs", "core-generated-annotation-stubs":
 		return javaCore, true
-	case ver.kind == sdkCore:
-		return javaCore, false
-	case name == "android_system_stubs_current":
-		return javaSystem, true
-	case ver.kind == sdkSystem:
-		return javaSystem, false
-	case name == "android_test_stubs_current":
-		return javaSystem, true
-	case ver.kind == sdkTest:
-		return javaPlatform, false
-	case name == "android_stubs_current":
+	case "android_stubs_current":
 		return javaSdk, true
-	case ver.kind == sdkPublic:
-		return javaSdk, false
-	case name == "android_module_lib_stubs_current":
+	case "android_system_stubs_current":
+		return javaSystem, true
+	case "android_module_lib_stubs_current":
 		return javaModule, true
-	case ver.kind == sdkModule:
-		return javaModule, false
-	case name == "android_system_server_stubs_current":
+	case "android_system_server_stubs_current":
 		return javaSystemServer, true
-	case ver.kind == sdkSystemServer:
-		return javaSystemServer, false
-	case ver.kind == sdkPrivate || ver.kind == sdkNone || ver.kind == sdkCorePlatform:
-		return javaPlatform, false
-	case !ver.valid():
-		panic(fmt.Errorf("sdk_version is invalid. got %q", ver.raw))
-	default:
-		return javaSdk, false
+	case "android_test_stubs_current":
+		return javaSystem, true
 	}
+
+	if stub, linkType := moduleStubLinkType(name); stub {
+		return linkType, true
+	}
+
+	ver := m.sdkVersion()
+	switch ver.kind {
+	case sdkCore:
+		return javaCore, false
+	case sdkSystem:
+		return javaSystem, false
+	case sdkPublic:
+		return javaSdk, false
+	case sdkModule:
+		return javaModule, false
+	case sdkSystemServer:
+		return javaSystemServer, false
+	case sdkPrivate, sdkNone, sdkCorePlatform, sdkTest:
+		return javaPlatform, false
+	}
+
+	if !ver.valid() {
+		panic(fmt.Errorf("sdk_version is invalid. got %q", ver.raw))
+	}
+	return javaSdk, false
 }
 
 func checkLinkType(ctx android.ModuleContext, from *Module, to linkTypeContext, tag dependencyTag) {
@@ -1456,13 +1463,19 @@
 			serviceFile := file.String()
 			zipargs = append(zipargs, "-C", filepath.Dir(serviceFile), "-f", serviceFile)
 		}
+		rule := zip
+		args := map[string]string{
+			"jarArgs": "-P META-INF/services/ " + strings.Join(proptools.NinjaAndShellEscapeList(zipargs), " "),
+		}
+		if ctx.Config().IsEnvTrue("RBE_ZIP") {
+			rule = zipRE
+			args["implicits"] = strings.Join(services.Strings(), ",")
+		}
 		ctx.Build(pctx, android.BuildParams{
-			Rule:      zip,
+			Rule:      rule,
 			Output:    servicesJar,
 			Implicits: services,
-			Args: map[string]string{
-				"jarArgs": "-P META-INF/services/ " + strings.Join(proptools.NinjaAndShellEscapeList(zipargs), " "),
-			},
+			Args:      args,
 		})
 		jars = append(jars, servicesJar)
 	}
@@ -1733,10 +1746,14 @@
 	return android.Paths{j.implementationJarFile}
 }
 
-func (j *Module) DexJar() android.Path {
+func (j *Module) DexJarBuildPath() android.Path {
 	return j.dexJarFile
 }
 
+func (j *Module) DexJarInstallPath() android.Path {
+	return j.installFile
+}
+
 func (j *Module) ResourceJars() android.Paths {
 	if j.resourceJar == nil {
 		return nil
@@ -1834,6 +1851,17 @@
 	InstallMixin func(ctx android.ModuleContext, installPath android.Path) (extraInstallDeps android.Paths)
 }
 
+// Provides access to the list of permitted packages from updatable boot jars.
+type PermittedPackagesForUpdatableBootJars interface {
+	PermittedPackagesForUpdatableBootJars() []string
+}
+
+var _ PermittedPackagesForUpdatableBootJars = (*Library)(nil)
+
+func (j *Library) PermittedPackagesForUpdatableBootJars() []string {
+	return j.properties.Permitted_packages
+}
+
 func shouldUncompressDex(ctx android.ModuleContext, dexpreopter *dexpreopter) bool {
 	// Store uncompressed (and aligned) any dex files from jars in APEXes.
 	if am, ok := ctx.Module().(android.ApexModule); ok && !am.IsForPlatform() {
@@ -1878,7 +1906,7 @@
 			extraInstallDeps = j.InstallMixin(ctx, j.outputFile)
 		}
 		j.installFile = ctx.InstallFile(android.PathForModuleInstall(ctx, "framework"),
-			ctx.ModuleName()+".jar", j.outputFile, extraInstallDeps...)
+			j.Stem()+".jar", j.outputFile, extraInstallDeps...)
 	}
 
 	// Verify Dist.Tag is set to a supported output
@@ -2548,7 +2576,11 @@
 	return android.Paths{j.combinedClasspathFile}
 }
 
-func (j *Import) DexJar() android.Path {
+func (j *Import) DexJarBuildPath() android.Path {
+	return nil
+}
+
+func (j *Import) DexJarInstallPath() android.Path {
 	return nil
 }
 
@@ -2674,6 +2706,10 @@
 	return proptools.StringDefault(j.properties.Stem, j.ModuleBase.Name())
 }
 
+func (a *DexImport) JacocoReportClassesFile() android.Path {
+	return nil
+}
+
 func (j *DexImport) IsInstallable() bool {
 	return true
 }
@@ -2727,11 +2763,13 @@
 
 	j.maybeStrippedDexJarFile = dexOutputFile
 
-	ctx.InstallFile(android.PathForModuleInstall(ctx, "framework"),
-		ctx.ModuleName()+".jar", dexOutputFile)
+	if j.IsForPlatform() {
+		ctx.InstallFile(android.PathForModuleInstall(ctx, "framework"),
+			j.Stem()+".jar", dexOutputFile)
+	}
 }
 
-func (j *DexImport) DexJar() android.Path {
+func (j *DexImport) DexJarBuildPath() android.Path {
 	return j.dexJarFile
 }
 
diff --git a/java/sdk.go b/java/sdk.go
index 9310f78..2a08f32 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -35,6 +35,7 @@
 
 var sdkVersionsKey = android.NewOnceKey("sdkVersionsKey")
 var sdkFrameworkAidlPathKey = android.NewOnceKey("sdkFrameworkAidlPathKey")
+var nonUpdatableFrameworkAidlPathKey = android.NewOnceKey("nonUpdatableFrameworkAidlPathKey")
 var apiFingerprintPathKey = android.NewOnceKey("apiFingerprintPathKey")
 
 type sdkContext interface {
@@ -447,7 +448,7 @@
 		return toModule([]string{"core.current.stubs"}, "", nil)
 	case sdkModule:
 		// TODO(146757305): provide .apk and .aidl that have more APIs for modules
-		return toModule([]string{"android_module_lib_stubs_current"}, "framework-res", sdkFrameworkAidlPath(ctx))
+		return toModule([]string{"android_module_lib_stubs_current"}, "framework-res", nonUpdatableFrameworkAidlPath(ctx))
 	case sdkSystemServer:
 		// TODO(146757305): provide .apk and .aidl that have more APIs for modules
 		return toModule([]string{"android_system_server_stubs_current"}, "framework-res", sdkFrameworkAidlPath(ctx))
@@ -506,6 +507,7 @@
 	}
 
 	createSdkFrameworkAidl(ctx)
+	createNonUpdatableFrameworkAidl(ctx)
 	createAPIFingerprint(ctx)
 }
 
@@ -517,6 +519,31 @@
 		"android_system_stubs_current",
 	}
 
+	combinedAidl := sdkFrameworkAidlPath(ctx)
+	tempPath := combinedAidl.ReplaceExtension(ctx, "aidl.tmp")
+
+	rule := createFrameworkAidl(stubsModules, tempPath, ctx)
+
+	commitChangeForRestat(rule, tempPath, combinedAidl)
+
+	rule.Build(pctx, ctx, "framework_aidl", "generate framework.aidl")
+}
+
+// Creates a version of framework.aidl for the non-updatable part of the platform.
+func createNonUpdatableFrameworkAidl(ctx android.SingletonContext) {
+	stubsModules := []string{"android_module_lib_stubs_current"}
+
+	combinedAidl := nonUpdatableFrameworkAidlPath(ctx)
+	tempPath := combinedAidl.ReplaceExtension(ctx, "aidl.tmp")
+
+	rule := createFrameworkAidl(stubsModules, tempPath, ctx)
+
+	commitChangeForRestat(rule, tempPath, combinedAidl)
+
+	rule.Build(pctx, ctx, "framework_non_updatable_aidl", "generate framework_non_updatable.aidl")
+}
+
+func createFrameworkAidl(stubsModules []string, path android.OutputPath, ctx android.SingletonContext) *android.RuleBuilder {
 	stubsJars := make([]android.Paths, len(stubsModules))
 
 	ctx.VisitAllModules(func(module android.Module) {
@@ -536,8 +563,7 @@
 			if ctx.Config().AllowMissingDependencies() {
 				missingDeps = append(missingDeps, stubsModules[i])
 			} else {
-				ctx.Errorf("failed to find dex jar path for module %q",
-					stubsModules[i])
+				ctx.Errorf("failed to find dex jar path for module %q", stubsModules[i])
 			}
 		}
 	}
@@ -561,20 +587,15 @@
 		}
 	}
 
-	combinedAidl := sdkFrameworkAidlPath(ctx)
-	tempPath := combinedAidl.ReplaceExtension(ctx, "aidl.tmp")
-
 	rule.Command().
-		Text("rm -f").Output(tempPath)
+		Text("rm -f").Output(path)
 	rule.Command().
 		Text("cat").
 		Inputs(aidls).
 		Text("| sort -u >").
-		Output(tempPath)
+		Output(path)
 
-	commitChangeForRestat(rule, tempPath, combinedAidl)
-
-	rule.Build(pctx, ctx, "framework_aidl", "generate framework.aidl")
+	return rule
 }
 
 func sdkFrameworkAidlPath(ctx android.PathContext) android.OutputPath {
@@ -583,6 +604,12 @@
 	}).(android.OutputPath)
 }
 
+func nonUpdatableFrameworkAidlPath(ctx android.PathContext) android.OutputPath {
+	return ctx.Config().Once(nonUpdatableFrameworkAidlPathKey, func() interface{} {
+		return android.PathForOutput(ctx, "framework_non_updatable.aidl")
+	}).(android.OutputPath)
+}
+
 // Create api_fingerprint.txt
 func createAPIFingerprint(ctx android.SingletonContext) {
 	out := ApiFingerprintPath(ctx)
diff --git a/java/sdk_library.go b/java/sdk_library.go
index bf0d3d6..de5ee03 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -1113,7 +1113,9 @@
 	props.Product_variables.Pdk.Enabled = proptools.BoolPtr(false)
 	props.Openjdk9.Srcs = module.properties.Openjdk9.Srcs
 	props.Openjdk9.Javacflags = module.properties.Openjdk9.Javacflags
-	props.Java_version = module.properties.Java_version
+	// We compile the stubs for 1.8 in line with the main android.jar stubs, and potential
+	// interop with older developer tools that don't support 1.9.
+	props.Java_version = proptools.StringPtr("1.8")
 	if module.deviceProperties.Compile_dex != nil {
 		props.Compile_dex = module.deviceProperties.Compile_dex
 	}
@@ -1575,6 +1577,24 @@
 
 var _ sdkLibraryComponentNamingScheme = (*frameworkModulesNamingScheme)(nil)
 
+func moduleStubLinkType(name string) (stub bool, ret linkType) {
+	// This suffix-based approach is fragile and could potentially mis-trigger.
+	// TODO(b/155164730): Clean this up when modules no longer reference sdk_lib stubs directly.
+	if strings.HasSuffix(name, ".stubs.public") || strings.HasSuffix(name, "-stubs-publicapi") {
+		return true, javaSdk
+	}
+	if strings.HasSuffix(name, ".stubs.system") || strings.HasSuffix(name, "-stubs-systemapi") {
+		return true, javaSystem
+	}
+	if strings.HasSuffix(name, ".stubs.module_lib") || strings.HasSuffix(name, "-stubs-module_libs_api") {
+		return true, javaModule
+	}
+	if strings.HasSuffix(name, ".stubs.test") {
+		return true, javaSystem
+	}
+	return false, javaPlatform
+}
+
 // java_sdk_library is a special Java library that provides optional platform APIs to apps.
 // In practice, it can be viewed as a combination of several modules: 1) stubs library that clients
 // are linked against to, 2) droiddoc module that internally generates API stubs source files,
@@ -2006,6 +2026,10 @@
 
 	// The naming scheme.
 	Naming_scheme *string
+
+	// True if the java_sdk_library_import is for a shared library, false
+	// otherwise.
+	Shared_library *bool
 }
 
 type scopeProperties struct {
@@ -2040,12 +2064,16 @@
 
 	s.Libs = sdk.properties.Libs
 	s.Naming_scheme = sdk.commonSdkLibraryProperties.Naming_scheme
+	s.Shared_library = proptools.BoolPtr(sdk.sharedLibrary())
 }
 
 func (s *sdkLibrarySdkMemberProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) {
 	if s.Naming_scheme != nil {
 		propertySet.AddProperty("naming_scheme", proptools.String(s.Naming_scheme))
 	}
+	if s.Shared_library != nil {
+		propertySet.AddProperty("shared_library", *s.Shared_library)
+	}
 
 	for _, apiScope := range allApiScopes {
 		if properties, ok := s.Scopes[apiScope]; ok {
@@ -2075,7 +2103,7 @@
 
 			if properties.RemovedApiFile != nil {
 				removedApiSnapshotPath := filepath.Join(scopeDir, ctx.Name()+"-removed.txt")
-				ctx.SnapshotBuilder().CopyToSnapshot(properties.CurrentApiFile, removedApiSnapshotPath)
+				ctx.SnapshotBuilder().CopyToSnapshot(properties.RemovedApiFile, removedApiSnapshotPath)
 				scopeSet.AddProperty("removed_api", removedApiSnapshotPath)
 			}
 
diff --git a/java/sdk_test.go b/java/sdk_test.go
index fb86463..e5d322c 100644
--- a/java/sdk_test.go
+++ b/java/sdk_test.go
@@ -156,9 +156,9 @@
 		{
 
 			name:           "nostdlib system_modules",
-			properties:     `sdk_version: "none", system_modules: "core-platform-api-stubs-system-modules"`,
-			system:         "core-platform-api-stubs-system-modules",
-			bootclasspath:  []string{"core-platform-api-stubs-system-modules-lib"},
+			properties:     `sdk_version: "none", system_modules: "legacy-core-platform-api-stubs-system-modules"`,
+			system:         "legacy-core-platform-api-stubs-system-modules",
+			bootclasspath:  []string{"legacy-core-platform-api-stubs-system-modules-lib"},
 			java8classpath: []string{},
 		},
 		{
@@ -252,7 +252,7 @@
 			bootclasspath:  []string{"android_module_lib_stubs_current", "core-lambda-stubs"},
 			system:         "core-current-stubs-system-modules",
 			java9classpath: []string{"android_module_lib_stubs_current"},
-			aidl:           "-p" + buildDir + "/framework.aidl",
+			aidl:           "-p" + buildDir + "/framework_non_updatable.aidl",
 		},
 		{
 			name:           "system_server_current",
diff --git a/java/testing.go b/java/testing.go
index 1967148..f993f56 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -173,7 +173,8 @@
 		"android_module_lib_stubs_current",
 		"android_system_server_stubs_current",
 		"core.current.stubs",
-		"core.platform.api.stubs",
+		"legacy.core.platform.api.stubs",
+		"stable.core.platform.api.stubs",
 		"kotlin-stdlib",
 		"kotlin-stdlib-jdk7",
 		"kotlin-stdlib-jdk8",
@@ -186,7 +187,7 @@
 				name: "%s",
 				srcs: ["a.java"],
 				sdk_version: "none",
-				system_modules: "core-platform-api-stubs-system-modules",
+				system_modules: "legacy-core-platform-api-stubs-system-modules",
 			}
 		`, extra)
 	}
@@ -196,7 +197,7 @@
 			name: "framework",
 			srcs: ["a.java"],
 			sdk_version: "none",
-			system_modules: "core-platform-api-stubs-system-modules",
+			system_modules: "legacy-core-platform-api-stubs-system-modules",
 			aidl: {
 				export_include_dirs: ["framework/aidl"],
 			},
@@ -211,7 +212,7 @@
 			name: "android.hidl.base-V1.0-java",
 			srcs: ["a.java"],
 			sdk_version: "none",
-			system_modules: "core-platform-api-stubs-system-modules",
+			system_modules: "legacy-core-platform-api-stubs-system-modules",
 			installable: true,
 		}
 
@@ -219,7 +220,7 @@
 			name: "android.hidl.manager-V1.0-java",
 			srcs: ["a.java"],
 			sdk_version: "none",
-			system_modules: "core-platform-api-stubs-system-modules",
+			system_modules: "legacy-core-platform-api-stubs-system-modules",
 			installable: true,
 		}
 
@@ -227,14 +228,30 @@
 			name: "org.apache.http.legacy",
 			srcs: ["a.java"],
 			sdk_version: "none",
-			system_modules: "core-platform-api-stubs-system-modules",
+			system_modules: "legacy-core-platform-api-stubs-system-modules",
+			installable: true,
+		}
+
+		java_library {
+			name: "android.test.base",
+			srcs: ["a.java"],
+			sdk_version: "none",
+			system_modules: "legacy-core-platform-api-stubs-system-modules",
+			installable: true,
+		}
+  
+		java_library {
+			name: "android.test.mock",
+			srcs: ["a.java"],
+			sdk_version: "none",
+			system_modules: "legacy-core-platform-api-stubs-system-modules",
 			installable: true,
 		}
 	`
 
 	systemModules := []string{
 		"core-current-stubs-system-modules",
-		"core-platform-api-stubs-system-modules",
+		"legacy-core-platform-api-stubs-system-modules",
 	}
 
 	for _, extra := range systemModules {
diff --git a/phony/Android.bp b/phony/Android.bp
new file mode 100644
index 0000000..2c423ef
--- /dev/null
+++ b/phony/Android.bp
@@ -0,0 +1,12 @@
+bootstrap_go_package {
+    name: "soong-phony",
+    pkgPath: "android/soong/phony",
+    deps: [
+        "blueprint",
+        "soong-android",
+    ],
+    srcs: [
+        "phony.go",
+    ],
+    pluginFor: ["soong_build"],
+}
diff --git a/python/Android.bp b/python/Android.bp
new file mode 100644
index 0000000..ffd03fe
--- /dev/null
+++ b/python/Android.bp
@@ -0,0 +1,24 @@
+bootstrap_go_package {
+    name: "soong-python",
+    pkgPath: "android/soong/python",
+    deps: [
+        "blueprint",
+        "soong-android",
+        "soong-tradefed",
+    ],
+    srcs: [
+        "androidmk.go",
+        "binary.go",
+        "builder.go",
+        "defaults.go",
+        "installer.go",
+        "library.go",
+        "proto.go",
+        "python.go",
+        "test.go",
+    ],
+    testSrcs: [
+        "python_test.go",
+    ],
+    pluginFor: ["soong_build"],
+}
diff --git a/python/androidmk.go b/python/androidmk.go
index d293d52..247b80d 100644
--- a/python/androidmk.go
+++ b/python/androidmk.go
@@ -66,15 +66,9 @@
 			fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE :=",
 				strings.Join(p.binaryDecorator.binaryProperties.Test_suites, " "))
 		}
-		// If the test config has an explicit config specified use it.
-		if p.testProperties.Test_config != nil {
-			fmt.Fprintln(w, "LOCAL_TEST_CONFIG :=",
-				*p.testProperties.Test_config)
-		} else {
-			if p.testConfig != nil {
-				fmt.Fprintln(w, "LOCAL_FULL_TEST_CONFIG :=",
-					p.testConfig.String())
-			}
+		if p.testConfig != nil {
+			fmt.Fprintln(w, "LOCAL_FULL_TEST_CONFIG :=",
+				p.testConfig.String())
 		}
 
 		if !BoolDefault(p.binaryProperties.Auto_gen_config, true) {
diff --git a/python/test.go b/python/test.go
index f684fd5..a669c73 100644
--- a/python/test.go
+++ b/python/test.go
@@ -29,11 +29,11 @@
 type TestProperties struct {
 	// the name of the test configuration (for example "AndroidTest.xml") that should be
 	// installed with the module.
-	Test_config *string `android:"arch_variant"`
+	Test_config *string `android:"path,arch_variant"`
 
 	// the name of the test configuration template (for example "AndroidTestTemplate.xml") that
 	// should be installed with the module.
-	Test_config_template *string `android:"arch_variant"`
+	Test_config_template *string `android:"path,arch_variant"`
 }
 
 type testDecorator struct {
diff --git a/remoteexec/Android.bp b/remoteexec/Android.bp
new file mode 100644
index 0000000..fc2c0e3
--- /dev/null
+++ b/remoteexec/Android.bp
@@ -0,0 +1,15 @@
+bootstrap_go_package {
+    name: "soong-remoteexec",
+    pkgPath: "android/soong/remoteexec",
+    deps: [
+        "blueprint",
+        "soong-android",
+    ],
+    srcs: [
+        "remoteexec.go",
+    ],
+    testSrcs: [
+        "remoteexec_test.go",
+    ],
+    pluginFor: ["soong_build"],
+}
diff --git a/remoteexec/remoteexec.go b/remoteexec/remoteexec.go
index 11fc282..d6e2c0a 100644
--- a/remoteexec/remoteexec.go
+++ b/remoteexec/remoteexec.go
@@ -178,6 +178,22 @@
 		ctx.AndroidRemoteStaticRule(name+"RE", android.RemoteRuleSupports{RBE: true}, ruleParamsRE, append(commonArgs, reArgs...)...)
 }
 
+// MultiCommandStaticRules returns a pair of rules based on the given RuleParams, where the first
+// rule is a locally executable rule and the second rule is a remotely executable rule. This
+// function supports multiple remote execution wrappers placed in the template when commands are
+// chained together with &&. commonArgs are args used for both the local and remotely executable
+// rules. reArgs are args used only for remote execution.
+func MultiCommandStaticRules(ctx android.PackageContext, name string, ruleParams blueprint.RuleParams, reParams map[string]*REParams, commonArgs []string, reArgs []string) (blueprint.Rule, blueprint.Rule) {
+	ruleParamsRE := ruleParams
+	for k, v := range reParams {
+		ruleParams.Command = strings.ReplaceAll(ruleParams.Command, k, "")
+		ruleParamsRE.Command = strings.ReplaceAll(ruleParamsRE.Command, k, v.Template())
+	}
+
+	return ctx.AndroidStaticRule(name, ruleParams, commonArgs...),
+		ctx.AndroidRemoteStaticRule(name+"RE", android.RemoteRuleSupports{RBE: true}, ruleParamsRE, append(commonArgs, reArgs...)...)
+}
+
 // EnvOverrideFunc retrieves a variable func that evaluates to the value of the given environment
 // variable if set, otherwise the given default.
 func EnvOverrideFunc(envVar, defaultVal string) func(ctx android.PackageVarContext) string {
diff --git a/rust/Android.bp b/rust/Android.bp
new file mode 100644
index 0000000..b06ea8e
--- /dev/null
+++ b/rust/Android.bp
@@ -0,0 +1,34 @@
+bootstrap_go_package {
+    name: "soong-rust",
+    pkgPath: "android/soong/rust",
+    deps: [
+        "soong",
+        "soong-android",
+        "soong-cc",
+        "soong-rust-config",
+    ],
+    srcs: [
+        "androidmk.go",
+        "compiler.go",
+        "coverage.go",
+        "binary.go",
+        "builder.go",
+        "library.go",
+        "prebuilt.go",
+        "proc_macro.go",
+	"project_json.go",
+        "rust.go",
+        "test.go",
+        "testing.go",
+    ],
+    testSrcs: [
+        "binary_test.go",
+        "compiler_test.go",
+        "coverage_test.go",
+        "library_test.go",
+	"project_json_test.go",
+        "rust_test.go",
+        "test_test.go",
+    ],
+    pluginFor: ["soong_build"],
+}
diff --git a/rust/config/Android.bp b/rust/config/Android.bp
new file mode 100644
index 0000000..1a10312
--- /dev/null
+++ b/rust/config/Android.bp
@@ -0,0 +1,19 @@
+bootstrap_go_package {
+    name: "soong-rust-config",
+    pkgPath: "android/soong/rust/config",
+    deps: [
+        "soong-android",
+        "soong-cc-config",
+    ],
+    srcs: [
+        "arm_device.go",
+        "arm64_device.go",
+        "global.go",
+        "toolchain.go",
+        "whitelist.go",
+        "x86_darwin_host.go",
+        "x86_linux_host.go",
+        "x86_device.go",
+        "x86_64_device.go",
+    ],
+}
diff --git a/rust/config/x86_darwin_host.go b/rust/config/x86_darwin_host.go
index 7cfc59c..4c16693 100644
--- a/rust/config/x86_darwin_host.go
+++ b/rust/config/x86_darwin_host.go
@@ -62,7 +62,11 @@
 	return "x86_64-apple-darwin"
 }
 
-func (t *toolchainDarwin) ShlibSuffix() string {
+func (t *toolchainDarwin) SharedLibSuffix() string {
+	return ".dylib"
+}
+
+func (t *toolchainDarwin) ProcMacroSuffix() string {
 	return ".dylib"
 }
 
diff --git a/rust/library.go b/rust/library.go
index 8aa033c..2e51266 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -329,12 +329,21 @@
 
 	return deps
 }
+
+func (library *libraryDecorator) sharedLibFilename(ctx ModuleContext) string {
+	return library.getStem(ctx) + ctx.toolchain().SharedLibSuffix()
+}
+
 func (library *libraryDecorator) compilerFlags(ctx ModuleContext, flags Flags) Flags {
 	flags.RustFlags = append(flags.RustFlags, "-C metadata="+ctx.baseModuleName())
 	flags = library.baseCompiler.compilerFlags(ctx, flags)
 	if library.shared() || library.static() {
 		library.includeDirs = append(library.includeDirs, android.PathsForModuleSrc(ctx, library.Properties.Include_dirs)...)
 	}
+	if library.shared() {
+		flags.LinkFlags = append(flags.LinkFlags, "-Wl,-soname="+library.sharedLibFilename(ctx))
+	}
+
 	return flags
 }
 
@@ -371,7 +380,7 @@
 		outputs := TransformSrctoStatic(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
 		library.coverageFile = outputs.coverageFile
 	} else if library.shared() {
-		fileName := library.getStem(ctx) + ctx.toolchain().SharedLibSuffix()
+		fileName := library.sharedLibFilename(ctx)
 		outputFile = android.PathForModuleOut(ctx, fileName)
 
 		outputs := TransformSrctoShared(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
diff --git a/rust/library_test.go b/rust/library_test.go
index 9f9f374..37dd541 100644
--- a/rust/library_test.go
+++ b/rust/library_test.go
@@ -114,3 +114,17 @@
 			}`)
 
 }
+
+func TestSharedLibraryFlags(t *testing.T) {
+	ctx := testRust(t, `
+		rust_library_host {
+			name: "libfoo",
+			srcs: ["foo.rs"],
+			crate_name: "foo",
+		}`)
+
+	libfooShared := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_shared").Output("libfoo.so")
+	if !strings.Contains(libfooShared.Args["linkFlags"], "-Wl,-soname=libfoo.so") {
+		t.Errorf("missing expected -Wl,-soname linker flag for libfoo shared lib, linkFlags: %#v", libfooShared.Args["linkFlags"])
+	}
+}
diff --git a/rust/project_json.go b/rust/project_json.go
new file mode 100644
index 0000000..909aebc
--- /dev/null
+++ b/rust/project_json.go
@@ -0,0 +1,161 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rust
+
+import (
+	"encoding/json"
+	"fmt"
+	"path"
+
+	"android/soong/android"
+)
+
+// This singleton collects Rust crate definitions and generates a JSON file
+// (${OUT_DIR}/soong/rust-project.json) which can be use by external tools,
+// such as rust-analyzer. It does so when either make, mm, mma, mmm or mmma is
+// called.  This singleton is enabled only if SOONG_GEN_RUST_PROJECT is set.
+// For example,
+//
+//   $ SOONG_GEN_RUST_PROJECT=1 m nothing
+
+func init() {
+	android.RegisterSingletonType("rust_project_generator", rustProjectGeneratorSingleton)
+}
+
+func rustProjectGeneratorSingleton() android.Singleton {
+	return &projectGeneratorSingleton{}
+}
+
+type projectGeneratorSingleton struct{}
+
+const (
+	// Environment variables used to control the behavior of this singleton.
+	envVariableCollectRustDeps = "SOONG_GEN_RUST_PROJECT"
+	rustProjectJsonFileName    = "rust-project.json"
+)
+
+// The format of rust-project.json is not yet finalized. A current description is available at:
+// https://github.com/rust-analyzer/rust-analyzer/blob/master/docs/user/manual.adoc#non-cargo-based-projects
+type rustProjectDep struct {
+	Crate int    `json:"crate"`
+	Name  string `json:"name"`
+}
+
+type rustProjectCrate struct {
+	RootModule string           `json:"root_module"`
+	Edition    string           `json:"edition,omitempty"`
+	Deps       []rustProjectDep `json:"deps"`
+	Cfgs       []string         `json:"cfgs"`
+}
+
+type rustProjectJson struct {
+	Roots  []string           `json:"roots"`
+	Crates []rustProjectCrate `json:"crates"`
+}
+
+// crateInfo is used during the processing to keep track of the known crates.
+type crateInfo struct {
+	ID   int
+	Deps map[string]int
+}
+
+func mergeDependencies(ctx android.SingletonContext, project *rustProjectJson,
+	knownCrates map[string]crateInfo, module android.Module,
+	crate *rustProjectCrate, deps map[string]int) {
+
+	//TODO(tweek): The stdlib dependencies do not appear here. We need to manually add them.
+	ctx.VisitDirectDeps(module, func(child android.Module) {
+		childId, childName, ok := appendLibraryAndDeps(ctx, project, knownCrates, child)
+		if !ok {
+			return
+		}
+		if _, ok = deps[childName]; ok {
+			return
+		}
+		crate.Deps = append(crate.Deps, rustProjectDep{Crate: childId, Name: childName})
+		deps[childName] = childId
+	})
+}
+
+// appendLibraryAndDeps creates a rustProjectCrate for the module argument and
+// appends it to the rustProjectJson struct.  It visits the dependencies of the
+// module depth-first. If the current module is already in knownCrates, its
+// its dependencies are merged. Returns a tuple (id, crate_name, ok).
+func appendLibraryAndDeps(ctx android.SingletonContext, project *rustProjectJson,
+	knownCrates map[string]crateInfo, module android.Module) (int, string, bool) {
+	rModule, ok := module.(*Module)
+	if !ok {
+		return 0, "", false
+	}
+	if rModule.compiler == nil {
+		return 0, "", false
+	}
+	rustLib, ok := rModule.compiler.(*libraryDecorator)
+	if !ok {
+		return 0, "", false
+	}
+	crateName := rModule.CrateName()
+	if cInfo, ok := knownCrates[crateName]; ok {
+		// We have seen this crate already; merge any new dependencies.
+		crate := project.Crates[cInfo.ID]
+		mergeDependencies(ctx, project, knownCrates, module, &crate, cInfo.Deps)
+		return cInfo.ID, crateName, true
+	}
+	crate := rustProjectCrate{Deps: make([]rustProjectDep, 0), Cfgs: make([]string, 0)}
+	src := rustLib.Properties.Srcs[0]
+	crate.RootModule = path.Join(ctx.ModuleDir(rModule), src)
+	crate.Edition = getEdition(rustLib.baseCompiler)
+
+	deps := make(map[string]int)
+	mergeDependencies(ctx, project, knownCrates, module, &crate, deps)
+
+	id := len(project.Crates)
+	knownCrates[crateName] = crateInfo{ID: id, Deps: deps}
+	project.Crates = append(project.Crates, crate)
+	// rust-analyzer requires that all crates belong to at least one root:
+	// https://github.com/rust-analyzer/rust-analyzer/issues/4735.
+	project.Roots = append(project.Roots, path.Dir(crate.RootModule))
+	return id, crateName, true
+}
+
+func (r *projectGeneratorSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+	if !ctx.Config().IsEnvTrue(envVariableCollectRustDeps) {
+		return
+	}
+
+	project := rustProjectJson{}
+	knownCrates := make(map[string]crateInfo)
+	ctx.VisitAllModules(func(module android.Module) {
+		appendLibraryAndDeps(ctx, &project, knownCrates, module)
+	})
+
+	path := android.PathForOutput(ctx, rustProjectJsonFileName)
+	err := createJsonFile(project, path)
+	if err != nil {
+		ctx.Errorf(err.Error())
+	}
+}
+
+func createJsonFile(project rustProjectJson, rustProjectPath android.WritablePath) error {
+	buf, err := json.MarshalIndent(project, "", "  ")
+	if err != nil {
+		return fmt.Errorf("JSON marshal of rustProjectJson failed: %s", err)
+	}
+	err = android.WriteFileToOutputDir(rustProjectPath, buf, 0666)
+	if err != nil {
+		return fmt.Errorf("Writing rust-project to %s failed: %s", rustProjectPath.String(), err)
+	}
+	return nil
+}
diff --git a/rust/project_json_test.go b/rust/project_json_test.go
new file mode 100644
index 0000000..6786e72
--- /dev/null
+++ b/rust/project_json_test.go
@@ -0,0 +1,55 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rust
+
+import (
+	"io/ioutil"
+	"path/filepath"
+	"testing"
+
+	"android/soong/android"
+	"android/soong/cc"
+)
+
+func TestProjectJson(t *testing.T) {
+	bp := `rust_library {
+		  name: "liba",
+		  srcs: ["src/lib.rs"],
+		  crate_name: "a"
+		}` + GatherRequiredDepsForTest()
+	env := map[string]string{"SOONG_GEN_RUST_PROJECT": "1"}
+	fs := map[string][]byte{
+		"foo.rs":     nil,
+		"src/lib.rs": nil,
+	}
+
+	cc.GatherRequiredFilesForTest(fs)
+
+	config := android.TestArchConfig(buildDir, env, bp, fs)
+	ctx := CreateTestContext()
+	ctx.Register(config)
+	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+	android.FailIfErrored(t, errs)
+	_, errs = ctx.PrepareBuildActions(config)
+	android.FailIfErrored(t, errs)
+
+	// The JSON file is generated via WriteFileToOutputDir. Therefore, it
+	// won't appear in the Output of the TestingSingleton. Manually verify
+	// it exists.
+	_, err := ioutil.ReadFile(filepath.Join(buildDir, "rust-project.json"))
+	if err != nil {
+		t.Errorf("rust-project.json has not been generated")
+	}
+}
diff --git a/rust/test.go b/rust/test.go
index 94568c1..10c2785 100644
--- a/rust/test.go
+++ b/rust/test.go
@@ -25,11 +25,11 @@
 type TestProperties struct {
 	// the name of the test configuration (for example "AndroidTest.xml") that should be
 	// installed with the module.
-	Test_config *string `android:"arch_variant"`
+	Test_config *string `android:"path,arch_variant"`
 
 	// the name of the test configuration template (for example "AndroidTestTemplate.xml") that
 	// should be installed with the module.
-	Test_config_template *string `android:"arch_variant"`
+	Test_config_template *string `android:"path,arch_variant"`
 
 	// list of compatibility suites (for example "cts", "vts") that the module should be
 	// installed into.
diff --git a/rust/testing.go b/rust/testing.go
index 09008a8..f94af71 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -100,6 +100,7 @@
 		ctx.BottomUp("rust_unit_tests", TestPerSrcMutator).Parallel()
 		ctx.BottomUp("rust_begin", BeginMutator).Parallel()
 	})
+	ctx.RegisterSingletonType("rust_project_generator", rustProjectGeneratorSingleton)
 
 	return ctx
 }
diff --git a/scripts/package-check.sh b/scripts/package-check.sh
index f982e82..d7e602f 100755
--- a/scripts/package-check.sh
+++ b/scripts/package-check.sh
@@ -52,6 +52,7 @@
 # Check all class file names against the expected prefixes.
 old_ifs=${IFS}
 IFS=$'\n'
+failed=false
 for zip_entry in ${zip_contents}; do
   # Check the suffix.
   if [[ "${zip_entry}" = *.class ]]; then
@@ -65,8 +66,11 @@
     done
     if [[ "${found}" == "false" ]]; then
       echo "Class file ${zip_entry} is outside specified packages."
-      exit 1
+      failed=true
     fi
   fi
 done
+if [[ "${failed}" == "true" ]]; then
+  exit 1
+fi
 IFS=${old_ifs}
diff --git a/sdk/Android.bp b/sdk/Android.bp
new file mode 100644
index 0000000..cb93351
--- /dev/null
+++ b/sdk/Android.bp
@@ -0,0 +1,27 @@
+bootstrap_go_package {
+    name: "soong-sdk",
+    pkgPath: "android/soong/sdk",
+    deps: [
+        "blueprint",
+        "soong",
+        "soong-android",
+        "soong-apex",
+        "soong-cc",
+        "soong-java",
+    ],
+    srcs: [
+        "bp.go",
+        "exports.go",
+        "sdk.go",
+        "update.go",
+    ],
+    testSrcs: [
+        "bp_test.go",
+        "cc_sdk_test.go",
+        "exports_test.go",
+        "java_sdk_test.go",
+        "sdk_test.go",
+        "testing.go",
+    ],
+    pluginFor: ["soong_build"],
+}
diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go
index dded153..4a09081 100644
--- a/sdk/cc_sdk_test.go
+++ b/sdk/cc_sdk_test.go
@@ -401,7 +401,6 @@
 				"Test.cpp",
 			],
 			compile_multilib: "both",
-			stl: "none",
 		}
 	`)
 
@@ -494,6 +493,7 @@
     device_supported: false,
     host_supported: true,
     installable: false,
+    stl: "none",
     target: {
         linux_glibc: {
             compile_multilib: "both",
@@ -518,6 +518,7 @@
     prefer: false,
     device_supported: false,
     host_supported: true,
+    stl: "none",
     target: {
         linux_glibc: {
             compile_multilib: "both",
@@ -557,6 +558,90 @@
 	)
 }
 
+// Test that we support the necessary flags for the linker binary, which is
+// special in several ways.
+func TestSnapshotWithCcStaticNocrtBinary(t *testing.T) {
+	// b/145598135 - Generating host snapshots for anything other than linux is not supported.
+	SkipIfNotLinux(t)
+
+	result := testSdkWithCc(t, `
+		module_exports {
+			name: "mymodule_exports",
+			host_supported: true,
+			device_supported: false,
+			native_binaries: ["linker"],
+		}
+
+		cc_binary {
+			name: "linker",
+			host_supported: true,
+			static_executable: true,
+			nocrt: true,
+			stl: "none",
+			srcs: [
+				"Test.cpp",
+			],
+			compile_multilib: "both",
+		}
+	`)
+
+	result.CheckSnapshot("mymodule_exports", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_binary {
+    name: "mymodule_exports_linker@current",
+    sdk_member_name: "linker",
+    device_supported: false,
+    host_supported: true,
+    installable: false,
+    stl: "none",
+    static_executable: true,
+    nocrt: true,
+    compile_multilib: "both",
+    arch: {
+        x86_64: {
+            srcs: ["x86_64/bin/linker"],
+        },
+        x86: {
+            srcs: ["x86/bin/linker"],
+        },
+    },
+}
+
+cc_prebuilt_binary {
+    name: "linker",
+    prefer: false,
+    device_supported: false,
+    host_supported: true,
+    stl: "none",
+    static_executable: true,
+    nocrt: true,
+    compile_multilib: "both",
+    arch: {
+        x86_64: {
+            srcs: ["x86_64/bin/linker"],
+        },
+        x86: {
+            srcs: ["x86/bin/linker"],
+        },
+    },
+}
+
+module_exports_snapshot {
+    name: "mymodule_exports@current",
+    device_supported: false,
+    host_supported: true,
+    native_binaries: ["mymodule_exports_linker@current"],
+}
+`),
+		checkAllCopyRules(`
+.intermediates/linker/linux_glibc_x86_64/linker -> x86_64/bin/linker
+.intermediates/linker/linux_glibc_x86/linker -> x86/bin/linker
+`),
+	)
+}
+
 func TestSnapshotWithCcSharedLibrary(t *testing.T) {
 	result := testSdkWithCc(t, `
 		sdk {
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index bbd6384..56706c7 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -43,10 +43,10 @@
 	name: "core-current-stubs-system-modules",
 }
 java_system_modules_import {
-	name: "core-platform-api-stubs-system-modules",
+	name: "legacy-core-platform-api-stubs-system-modules",
 }
 java_import {
-	name: "core.platform.api.stubs",
+	name: "legacy.core.platform.api.stubs",
 }
 java_import {
 	name: "android_stubs_current",
@@ -989,6 +989,7 @@
 			apex_available: ["//apex_available:anyapex"],
 			srcs: ["Test.java"],
 			sdk_version: "current",
+			shared_library: false,
 			stubs_library_visibility: ["//other"],
 			stubs_source_visibility: ["//another"],
 		}
@@ -1002,6 +1003,7 @@
     name: "mysdk_myjavalib@current",
     sdk_member_name: "myjavalib",
     apex_available: ["//apex_available:anyapex"],
+    shared_library: false,
     public: {
         jars: ["sdk_library/public/myjavalib-stubs.jar"],
         stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
@@ -1029,6 +1031,7 @@
     name: "myjavalib",
     prefer: false,
     apex_available: ["//apex_available:anyapex"],
+    shared_library: false,
     public: {
         jars: ["sdk_library/public/myjavalib-stubs.jar"],
         stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
@@ -1060,13 +1063,13 @@
 		checkAllCopyRules(`
 .intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
 .intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
-.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
 .intermediates/myjavalib.stubs.system/android_common/javac/myjavalib.stubs.system.jar -> sdk_library/system/myjavalib-stubs.jar
 .intermediates/myjavalib.stubs.source.system/android_common/myjavalib.stubs.source.system_api.txt -> sdk_library/system/myjavalib.txt
-.intermediates/myjavalib.stubs.source.system/android_common/myjavalib.stubs.source.system_api.txt -> sdk_library/system/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.source.system/android_common/myjavalib.stubs.source.system_removed.txt -> sdk_library/system/myjavalib-removed.txt
 .intermediates/myjavalib.stubs.test/android_common/javac/myjavalib.stubs.test.jar -> sdk_library/test/myjavalib-stubs.jar
 .intermediates/myjavalib.stubs.source.test/android_common/myjavalib.stubs.source.test_api.txt -> sdk_library/test/myjavalib.txt
-.intermediates/myjavalib.stubs.source.test/android_common/myjavalib.stubs.source.test_api.txt -> sdk_library/test/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.source.test/android_common/myjavalib.stubs.source.test_removed.txt -> sdk_library/test/myjavalib-removed.txt
 `),
 		checkMergeZips(
 			".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
@@ -1097,6 +1100,7 @@
 java_sdk_library_import {
     name: "mysdk_myjavalib@current",
     sdk_member_name: "myjavalib",
+    shared_library: true,
     public: {
         jars: ["sdk_library/public/myjavalib-stubs.jar"],
         stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
@@ -1109,6 +1113,7 @@
 java_sdk_library_import {
     name: "myjavalib",
     prefer: false,
+    shared_library: true,
     public: {
         jars: ["sdk_library/public/myjavalib-stubs.jar"],
         stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
@@ -1126,7 +1131,7 @@
 		checkAllCopyRules(`
 .intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
 .intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
-.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
 `),
 		checkMergeZips(
 			".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
@@ -1159,6 +1164,7 @@
 java_sdk_library_import {
     name: "mysdk_myjavalib@current",
     sdk_member_name: "myjavalib",
+    shared_library: true,
     public: {
         jars: ["sdk_library/public/myjavalib-stubs.jar"],
         stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
@@ -1171,6 +1177,7 @@
 java_sdk_library_import {
     name: "myjavalib",
     prefer: false,
+    shared_library: true,
     public: {
         jars: ["sdk_library/public/myjavalib-stubs.jar"],
         stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
@@ -1188,7 +1195,7 @@
 		checkAllCopyRules(`
 .intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
 .intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
-.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
 `),
 		checkMergeZips(
 			".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
@@ -1225,6 +1232,7 @@
     name: "mysdk_myjavalib@current",
     sdk_member_name: "myjavalib",
     apex_available: ["//apex_available:anyapex"],
+    shared_library: true,
     public: {
         jars: ["sdk_library/public/myjavalib-stubs.jar"],
         stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
@@ -1245,6 +1253,7 @@
     name: "myjavalib",
     prefer: false,
     apex_available: ["//apex_available:anyapex"],
+    shared_library: true,
     public: {
         jars: ["sdk_library/public/myjavalib-stubs.jar"],
         stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
@@ -1269,10 +1278,10 @@
 		checkAllCopyRules(`
 .intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
 .intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
-.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
 .intermediates/myjavalib.stubs.system/android_common/javac/myjavalib.stubs.system.jar -> sdk_library/system/myjavalib-stubs.jar
 .intermediates/myjavalib.stubs.source.system/android_common/myjavalib.stubs.source.system_api.txt -> sdk_library/system/myjavalib.txt
-.intermediates/myjavalib.stubs.source.system/android_common/myjavalib.stubs.source.system_api.txt -> sdk_library/system/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.source.system/android_common/myjavalib.stubs.source.system_removed.txt -> sdk_library/system/myjavalib-removed.txt
 `),
 		checkMergeZips(
 			".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
@@ -1313,6 +1322,7 @@
     name: "mysdk_myjavalib@current",
     sdk_member_name: "myjavalib",
     apex_available: ["//apex_available:anyapex"],
+    shared_library: true,
     public: {
         jars: ["sdk_library/public/myjavalib-stubs.jar"],
         stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
@@ -1340,6 +1350,7 @@
     name: "myjavalib",
     prefer: false,
     apex_available: ["//apex_available:anyapex"],
+    shared_library: true,
     public: {
         jars: ["sdk_library/public/myjavalib-stubs.jar"],
         stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
@@ -1371,13 +1382,13 @@
 		checkAllCopyRules(`
 .intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
 .intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
-.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
 .intermediates/myjavalib.stubs.system/android_common/javac/myjavalib.stubs.system.jar -> sdk_library/system/myjavalib-stubs.jar
 .intermediates/myjavalib.stubs.source.system/android_common/myjavalib.stubs.source.system_api.txt -> sdk_library/system/myjavalib.txt
-.intermediates/myjavalib.stubs.source.system/android_common/myjavalib.stubs.source.system_api.txt -> sdk_library/system/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.source.system/android_common/myjavalib.stubs.source.system_removed.txt -> sdk_library/system/myjavalib-removed.txt
 .intermediates/myjavalib.stubs.module_lib/android_common/javac/myjavalib.stubs.module_lib.jar -> sdk_library/module-lib/myjavalib-stubs.jar
 .intermediates/myjavalib.api.module_lib/android_common/myjavalib.api.module_lib_api.txt -> sdk_library/module-lib/myjavalib.txt
-.intermediates/myjavalib.api.module_lib/android_common/myjavalib.api.module_lib_api.txt -> sdk_library/module-lib/myjavalib-removed.txt
+.intermediates/myjavalib.api.module_lib/android_common/myjavalib.api.module_lib_removed.txt -> sdk_library/module-lib/myjavalib-removed.txt
 `),
 		checkMergeZips(
 			".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
@@ -1415,6 +1426,7 @@
     sdk_member_name: "myjavalib",
     apex_available: ["//apex_available:anyapex"],
     naming_scheme: "framework-modules",
+    shared_library: true,
     public: {
         jars: ["sdk_library/public/myjavalib-stubs.jar"],
         stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
@@ -1429,6 +1441,7 @@
     prefer: false,
     apex_available: ["//apex_available:anyapex"],
     naming_scheme: "framework-modules",
+    shared_library: true,
     public: {
         jars: ["sdk_library/public/myjavalib-stubs.jar"],
         stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
@@ -1446,7 +1459,7 @@
 		checkAllCopyRules(`
 .intermediates/myjavalib-stubs-publicapi/android_common/javac/myjavalib-stubs-publicapi.jar -> sdk_library/public/myjavalib-stubs.jar
 .intermediates/myjavalib-stubs-srcs-publicapi/android_common/myjavalib-stubs-srcs-publicapi_api.txt -> sdk_library/public/myjavalib.txt
-.intermediates/myjavalib-stubs-srcs-publicapi/android_common/myjavalib-stubs-srcs-publicapi_api.txt -> sdk_library/public/myjavalib-removed.txt
+.intermediates/myjavalib-stubs-srcs-publicapi/android_common/myjavalib-stubs-srcs-publicapi_removed.txt -> sdk_library/public/myjavalib-removed.txt
 `),
 		checkMergeZips(
 			".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
diff --git a/sh/Android.bp b/sh/Android.bp
new file mode 100644
index 0000000..0f40c5f
--- /dev/null
+++ b/sh/Android.bp
@@ -0,0 +1,17 @@
+bootstrap_go_package {
+    name: "soong-sh",
+    pkgPath: "android/soong/sh",
+    deps: [
+        "blueprint",
+        "soong",
+        "soong-android",
+        "soong-tradefed",
+    ],
+    srcs: [
+        "sh_binary.go",
+    ],
+    testSrcs: [
+        "sh_binary_test.go",
+    ],
+    pluginFor: ["soong_build"],
+}
diff --git a/sh/sh_binary.go b/sh/sh_binary.go
new file mode 100644
index 0000000..ae653fd
--- /dev/null
+++ b/sh/sh_binary.go
@@ -0,0 +1,296 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package sh
+
+import (
+	"fmt"
+	"path/filepath"
+	"strings"
+
+	"github.com/google/blueprint/proptools"
+
+	"android/soong/android"
+	"android/soong/tradefed"
+)
+
+// sh_binary is for shell scripts (and batch files) that are installed as
+// executable files into .../bin/
+//
+// Do not use them for prebuilt C/C++/etc files.  Use cc_prebuilt_binary
+// instead.
+
+var pctx = android.NewPackageContext("android/soong/sh")
+
+func init() {
+	pctx.Import("android/soong/android")
+
+	android.RegisterModuleType("sh_binary", ShBinaryFactory)
+	android.RegisterModuleType("sh_binary_host", ShBinaryHostFactory)
+	android.RegisterModuleType("sh_test", ShTestFactory)
+	android.RegisterModuleType("sh_test_host", ShTestHostFactory)
+}
+
+type shBinaryProperties struct {
+	// Source file of this prebuilt.
+	Src *string `android:"path,arch_variant"`
+
+	// optional subdirectory under which this file is installed into
+	Sub_dir *string `android:"arch_variant"`
+
+	// optional name for the installed file. If unspecified, name of the module is used as the file name
+	Filename *string `android:"arch_variant"`
+
+	// when set to true, and filename property is not set, the name for the installed file
+	// is the same as the file name of the source file.
+	Filename_from_src *bool `android:"arch_variant"`
+
+	// Whether this module is directly installable to one of the partitions. Default: true.
+	Installable *bool
+
+	// install symlinks to the binary
+	Symlinks []string `android:"arch_variant"`
+}
+
+type TestProperties struct {
+	// list of compatibility suites (for example "cts", "vts") that the module should be
+	// installed into.
+	Test_suites []string `android:"arch_variant"`
+
+	// the name of the test configuration (for example "AndroidTest.xml") that should be
+	// installed with the module.
+	Test_config *string `android:"path,arch_variant"`
+
+	// list of files or filegroup modules that provide data that should be installed alongside
+	// the test.
+	Data []string `android:"path,arch_variant"`
+
+	// Add RootTargetPreparer to auto generated test config. This guarantees the test to run
+	// with root permission.
+	Require_root *bool
+
+	// the name of the test configuration template (for example "AndroidTestTemplate.xml") that
+	// should be installed with the module.
+	Test_config_template *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
+	// explicitly.
+	Auto_gen_config *bool
+}
+
+type ShBinary struct {
+	android.ModuleBase
+
+	properties shBinaryProperties
+
+	sourceFilePath android.Path
+	outputFilePath android.OutputPath
+	installedFile  android.InstallPath
+}
+
+var _ android.HostToolProvider = (*ShBinary)(nil)
+
+type ShTest struct {
+	ShBinary
+
+	testProperties TestProperties
+
+	installDir android.InstallPath
+
+	data       android.Paths
+	testConfig android.Path
+}
+
+func (s *ShBinary) HostToolPath() android.OptionalPath {
+	return android.OptionalPathForPath(s.installedFile)
+}
+
+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 {
+	return s.outputFilePath
+}
+
+func (s *ShBinary) SubDir() string {
+	return proptools.String(s.properties.Sub_dir)
+}
+
+func (s *ShBinary) Installable() bool {
+	return s.properties.Installable == nil || proptools.Bool(s.properties.Installable)
+}
+
+func (s *ShBinary) Symlinks() []string {
+	return s.properties.Symlinks
+}
+
+func (s *ShBinary) generateAndroidBuildActions(ctx android.ModuleContext) {
+	s.sourceFilePath = android.PathForModuleSrc(ctx, proptools.String(s.properties.Src))
+	filename := proptools.String(s.properties.Filename)
+	filename_from_src := proptools.Bool(s.properties.Filename_from_src)
+	if filename == "" {
+		if filename_from_src {
+			filename = s.sourceFilePath.Base()
+		} else {
+			filename = ctx.ModuleName()
+		}
+	} else if filename_from_src {
+		ctx.PropertyErrorf("filename_from_src", "filename is set. filename_from_src can't be true")
+		return
+	}
+	s.outputFilePath = android.PathForModuleOut(ctx, filename).OutputPath
+
+	// This ensures that outputFilePath has the correct name for others to
+	// use, as the source file may have a different name.
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   android.CpExecutable,
+		Output: s.outputFilePath,
+		Input:  s.sourceFilePath,
+	})
+}
+
+func (s *ShBinary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	s.generateAndroidBuildActions(ctx)
+	installDir := android.PathForModuleInstall(ctx, "bin", proptools.String(s.properties.Sub_dir))
+	s.installedFile = ctx.InstallExecutable(installDir, s.outputFilePath.Base(), s.outputFilePath)
+}
+
+func (s *ShBinary) AndroidMkEntries() []android.AndroidMkEntries {
+	return []android.AndroidMkEntries{android.AndroidMkEntries{
+		Class:      "EXECUTABLES",
+		OutputFile: android.OptionalPathForPath(s.outputFilePath),
+		Include:    "$(BUILD_SYSTEM)/soong_cc_prebuilt.mk",
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(entries *android.AndroidMkEntries) {
+				s.customAndroidMkEntries(entries)
+			},
+		},
+	}}
+}
+
+func (s *ShBinary) customAndroidMkEntries(entries *android.AndroidMkEntries) {
+	entries.SetString("LOCAL_MODULE_RELATIVE_PATH", proptools.String(s.properties.Sub_dir))
+	entries.SetString("LOCAL_MODULE_SUFFIX", "")
+	entries.SetString("LOCAL_MODULE_STEM", s.outputFilePath.Rel())
+	if len(s.properties.Symlinks) > 0 {
+		entries.SetString("LOCAL_MODULE_SYMLINKS", strings.Join(s.properties.Symlinks, " "))
+	}
+}
+
+func (s *ShTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	s.ShBinary.generateAndroidBuildActions(ctx)
+	testDir := "nativetest"
+	if ctx.Target().Arch.ArchType.Multilib == "lib64" {
+		testDir = "nativetest64"
+	}
+	if ctx.Target().NativeBridge == android.NativeBridgeEnabled {
+		testDir = filepath.Join(testDir, ctx.Target().NativeBridgeRelativePath)
+	} else if !ctx.Host() && ctx.Config().HasMultilibConflict(ctx.Arch().ArchType) {
+		testDir = filepath.Join(testDir, ctx.Arch().ArchType.String())
+	}
+	s.installDir = android.PathForModuleInstall(ctx, testDir, proptools.String(s.properties.Sub_dir), s.Name())
+	s.installedFile = ctx.InstallExecutable(s.installDir, s.outputFilePath.Base(), s.outputFilePath)
+
+	s.data = android.PathsForModuleSrc(ctx, s.testProperties.Data)
+
+	var configs []tradefed.Config
+	if Bool(s.testProperties.Require_root) {
+		configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.RootTargetPreparer", nil})
+	} else {
+		options := []tradefed.Option{{Name: "force-root", Value: "false"}}
+		configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.RootTargetPreparer", options})
+	}
+	s.testConfig = tradefed.AutoGenShellTestConfig(ctx, s.testProperties.Test_config,
+		s.testProperties.Test_config_template, s.testProperties.Test_suites, configs, s.testProperties.Auto_gen_config, s.outputFilePath.Base())
+}
+
+func (s *ShTest) InstallInData() bool {
+	return true
+}
+
+func (s *ShTest) AndroidMkEntries() []android.AndroidMkEntries {
+	return []android.AndroidMkEntries{android.AndroidMkEntries{
+		Class:      "NATIVE_TESTS",
+		OutputFile: android.OptionalPathForPath(s.outputFilePath),
+		Include:    "$(BUILD_SYSTEM)/soong_cc_prebuilt.mk",
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(entries *android.AndroidMkEntries) {
+				s.customAndroidMkEntries(entries)
+
+				entries.SetPath("LOCAL_MODULE_PATH", s.installDir.ToMakePath())
+				entries.AddStrings("LOCAL_COMPATIBILITY_SUITE", s.testProperties.Test_suites...)
+				if s.testConfig != nil {
+					entries.SetPath("LOCAL_FULL_TEST_CONFIG", s.testConfig)
+				}
+				for _, d := range s.data {
+					rel := d.Rel()
+					path := d.String()
+					if !strings.HasSuffix(path, rel) {
+						panic(fmt.Errorf("path %q does not end with %q", path, rel))
+					}
+					path = strings.TrimSuffix(path, rel)
+					entries.AddStrings("LOCAL_TEST_DATA", path+":"+rel)
+				}
+			},
+		},
+	}}
+}
+
+func InitShBinaryModule(s *ShBinary) {
+	s.AddProperties(&s.properties)
+}
+
+// sh_binary is for a shell script or batch file to be installed as an
+// executable binary to <partition>/bin.
+func ShBinaryFactory() android.Module {
+	module := &ShBinary{}
+	InitShBinaryModule(module)
+	android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibFirst)
+	return module
+}
+
+// sh_binary_host is for a shell script to be installed as an executable binary
+// to $(HOST_OUT)/bin.
+func ShBinaryHostFactory() android.Module {
+	module := &ShBinary{}
+	InitShBinaryModule(module)
+	android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
+	return module
+}
+
+// sh_test defines a shell script based test module.
+func ShTestFactory() android.Module {
+	module := &ShTest{}
+	InitShBinaryModule(&module.ShBinary)
+	module.AddProperties(&module.testProperties)
+
+	android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibFirst)
+	return module
+}
+
+// sh_test_host defines a shell script based test module that runs on a host.
+func ShTestHostFactory() android.Module {
+	module := &ShTest{}
+	InitShBinaryModule(&module.ShBinary)
+	module.AddProperties(&module.testProperties)
+
+	android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
+	return module
+}
+
+var Bool = proptools.Bool
diff --git a/sh/sh_binary_test.go b/sh/sh_binary_test.go
new file mode 100644
index 0000000..6ab22c5
--- /dev/null
+++ b/sh/sh_binary_test.go
@@ -0,0 +1,106 @@
+package sh
+
+import (
+	"io/ioutil"
+	"os"
+	"reflect"
+	"testing"
+
+	"android/soong/android"
+)
+
+var buildDir string
+
+func setUp() {
+	var err error
+	buildDir, err = ioutil.TempDir("", "soong_sh_test")
+	if err != nil {
+		panic(err)
+	}
+}
+
+func tearDown() {
+	os.RemoveAll(buildDir)
+}
+
+func TestMain(m *testing.M) {
+	run := func() int {
+		setUp()
+		defer tearDown()
+
+		return m.Run()
+	}
+
+	os.Exit(run())
+}
+
+func testShBinary(t *testing.T, bp string) (*android.TestContext, android.Config) {
+	fs := map[string][]byte{
+		"test.sh":            nil,
+		"testdata/data1":     nil,
+		"testdata/sub/data2": nil,
+	}
+
+	config := android.TestArchConfig(buildDir, nil, bp, fs)
+
+	ctx := android.NewTestArchContext()
+	ctx.RegisterModuleType("sh_test", ShTestFactory)
+	ctx.RegisterModuleType("sh_test_host", ShTestHostFactory)
+	ctx.Register(config)
+	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+	android.FailIfErrored(t, errs)
+	_, errs = ctx.PrepareBuildActions(config)
+	android.FailIfErrored(t, errs)
+
+	return ctx, config
+}
+
+func TestShTest(t *testing.T) {
+	ctx, config := testShBinary(t, `
+		sh_test {
+			name: "foo",
+			src: "test.sh",
+			filename: "test.sh",
+			data: [
+				"testdata/data1",
+				"testdata/sub/data2",
+			],
+		}
+	`)
+
+	mod := ctx.ModuleForTests("foo", "android_arm64_armv8-a").Module().(*ShTest)
+
+	entries := android.AndroidMkEntriesForTest(t, config, "", mod)[0]
+
+	expectedPath := "/tmp/target/product/test_device/data/nativetest64/foo"
+	actualPath := entries.EntryMap["LOCAL_MODULE_PATH"][0]
+	if expectedPath != actualPath {
+		t.Errorf("Unexpected LOCAL_MODULE_PATH expected: %q, actual: %q", expectedPath, actualPath)
+	}
+
+	expectedData := []string{":testdata/data1", ":testdata/sub/data2"}
+	actualData := entries.EntryMap["LOCAL_TEST_DATA"]
+	if !reflect.DeepEqual(expectedData, actualData) {
+		t.Errorf("Unexpected test data expected: %q, actual: %q", expectedData, actualData)
+	}
+}
+
+func TestShTestHost(t *testing.T) {
+	ctx, _ := testShBinary(t, `
+		sh_test_host {
+			name: "foo",
+			src: "test.sh",
+			filename: "test.sh",
+			data: [
+				"testdata/data1",
+				"testdata/sub/data2",
+			],
+		}
+	`)
+
+	buildOS := android.BuildOs.String()
+	mod := ctx.ModuleForTests("foo", buildOS+"_x86_64").Module().(*ShTest)
+	if !mod.Host() {
+		t.Errorf("host bit is not set for a sh_test_host module.")
+	}
+}
diff --git a/shared/Android.bp b/shared/Android.bp
new file mode 100644
index 0000000..07dfe11
--- /dev/null
+++ b/shared/Android.bp
@@ -0,0 +1,7 @@
+bootstrap_go_package {
+    name: "soong-shared",
+    pkgPath: "android/soong/shared",
+    srcs: [
+        "paths.go",
+    ],
+}
diff --git a/sysprop/Android.bp b/sysprop/Android.bp
new file mode 100644
index 0000000..48094f1
--- /dev/null
+++ b/sysprop/Android.bp
@@ -0,0 +1,18 @@
+bootstrap_go_package {
+    name: "soong-sysprop",
+    pkgPath: "android/soong/sysprop",
+    deps: [
+        "blueprint",
+        "soong",
+        "soong-android",
+        "soong-cc",
+        "soong-java",
+    ],
+    srcs: [
+        "sysprop_library.go",
+    ],
+    testSrcs: [
+        "sysprop_test.go",
+    ],
+    pluginFor: ["soong_build"],
+}
diff --git a/tradefed/Android.bp b/tradefed/Android.bp
new file mode 100644
index 0000000..6e5e533
--- /dev/null
+++ b/tradefed/Android.bp
@@ -0,0 +1,14 @@
+bootstrap_go_package {
+    name: "soong-tradefed",
+    pkgPath: "android/soong/tradefed",
+    deps: [
+        "blueprint",
+        "soong-android",
+    ],
+    srcs: [
+        "autogen.go",
+        "config.go",
+        "makevars.go",
+    ],
+    pluginFor: ["soong_build"],
+}
diff --git a/tradefed/autogen.go b/tradefed/autogen.go
index be44cac..2829146 100644
--- a/tradefed/autogen.go
+++ b/tradefed/autogen.go
@@ -40,9 +40,9 @@
 }
 
 var autogenTestConfig = pctx.StaticRule("autogenTestConfig", blueprint.RuleParams{
-	Command:     "sed 's&{MODULE}&${name}&g;s&{EXTRA_CONFIGS}&'${extraConfigs}'&g' $template > $out",
+	Command:     "sed 's&{MODULE}&${name}&g;s&{EXTRA_CONFIGS}&'${extraConfigs}'&g;s&{OUTPUT_FILENAME}&'${outputFileName}'&g' $template > $out",
 	CommandDeps: []string{"$template"},
-}, "name", "template", "extraConfigs")
+}, "name", "template", "extraConfigs", "outputFileName")
 
 func testConfigPath(ctx android.ModuleContext, prop *string, testSuites []string, autoGenConfig *bool, testConfigTemplateProp *string) (path android.Path, autogenPath android.WritablePath) {
 	p := getTestConfig(ctx, prop)
@@ -108,10 +108,14 @@
 }
 
 func autogenTemplate(ctx android.ModuleContext, output android.WritablePath, template string, configs []Config) {
-	autogenTemplateWithName(ctx, ctx.ModuleName(), output, template, configs)
+	autogenTemplateWithNameAndOutputFile(ctx, ctx.ModuleName(), output, template, configs, "")
 }
 
 func autogenTemplateWithName(ctx android.ModuleContext, name string, output android.WritablePath, template string, configs []Config) {
+	autogenTemplateWithNameAndOutputFile(ctx, ctx.ModuleName(), output, template, configs, "")
+}
+
+func autogenTemplateWithNameAndOutputFile(ctx android.ModuleContext, name string, output android.WritablePath, template string, configs []Config, outputFileName string) {
 	var configStrings []string
 	for _, config := range configs {
 		configStrings = append(configStrings, config.Config())
@@ -124,9 +128,10 @@
 		Description: "test config",
 		Output:      output,
 		Args: map[string]string{
-			"name":         name,
-			"template":     template,
-			"extraConfigs": extraConfigs,
+			"name":           name,
+			"template":       template,
+			"extraConfigs":   extraConfigs,
+			"outputFileName": outputFileName,
 		},
 	})
 }
@@ -150,6 +155,21 @@
 	return path
 }
 
+func AutoGenShellTestConfig(ctx android.ModuleContext, testConfigProp *string,
+	testConfigTemplateProp *string, testSuites []string, config []Config, autoGenConfig *bool, outputFileName string) android.Path {
+	path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp)
+	if autogenPath != nil {
+		templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp)
+		if templatePath.Valid() {
+			autogenTemplateWithNameAndOutputFile(ctx, ctx.ModuleName(), autogenPath, templatePath.String(), config, outputFileName)
+		} else {
+			autogenTemplateWithNameAndOutputFile(ctx, ctx.ModuleName(), autogenPath, "${ShellTestConfigTemplate}", config, outputFileName)
+		}
+		return autogenPath
+	}
+	return path
+}
+
 func AutoGenNativeBenchmarkTestConfig(ctx android.ModuleContext, testConfigProp *string,
 	testConfigTemplateProp *string, testSuites []string, configs []Config, autoGenConfig *bool) android.Path {
 	path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp)
diff --git a/tradefed/config.go b/tradefed/config.go
index a289073..34195c3 100644
--- a/tradefed/config.go
+++ b/tradefed/config.go
@@ -33,6 +33,7 @@
 	pctx.SourcePathVariable("PythonBinaryHostTestConfigTemplate", "build/make/core/python_binary_host_test_config_template.xml")
 	pctx.SourcePathVariable("RustDeviceTestConfigTemplate", "build/make/core/rust_device_test_config_template.xml")
 	pctx.SourcePathVariable("RustHostTestConfigTemplate", "build/make/core/rust_host_test_config_template.xml")
+	pctx.SourcePathVariable("ShellTestConfigTemplate", "build/make/core/shell_test_config_template.xml")
 
 	pctx.SourcePathVariable("EmptyTestConfig", "build/make/core/empty_test_config.xml")
 }
diff --git a/tradefed/makevars.go b/tradefed/makevars.go
index d4cf7a8..f9682e4 100644
--- a/tradefed/makevars.go
+++ b/tradefed/makevars.go
@@ -33,6 +33,7 @@
 	ctx.Strict("PYTHON_BINARY_HOST_TEST_CONFIG_TEMPLATE", "${PythonBinaryHostTestConfigTemplate}")
 	ctx.Strict("RUST_DEVICE_TEST_CONFIG_TEMPLATE", "${RustDeviceTestConfigTemplate}")
 	ctx.Strict("RUST_HOST_TEST_CONFIG_TEMPLATE", "${RustHostTestConfigTemplate}")
+	ctx.Strict("SHELL_TEST_CONFIG_TEMPLATE", "${ShellTestConfigTemplate}")
 
 	ctx.Strict("EMPTY_TEST_CONFIG", "${EmptyTestConfig}")
 }
diff --git a/ui/build/Android.bp b/ui/build/Android.bp
index 2a5a51a..0a0bb16 100644
--- a/ui/build/Android.bp
+++ b/ui/build/Android.bp
@@ -56,12 +56,14 @@
         "signal.go",
         "soong.go",
         "test_build.go",
+        "upload.go",
         "util.go",
     ],
     testSrcs: [
         "cleanbuild_test.go",
         "config_test.go",
         "environment_test.go",
+        "upload_test.go",
         "util_test.go",
         "proc_sync_test.go",
     ],
diff --git a/ui/build/config.go b/ui/build/config.go
index d66a86c..c4bbad7 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -592,6 +592,7 @@
 	c.environ.Set("TARGET_BUILD_VARIANT", variant)
 	c.environ.Set("TARGET_BUILD_TYPE", "release")
 	c.environ.Unset("TARGET_BUILD_APPS")
+	c.environ.Unset("TARGET_BUILD_UNBUNDLED")
 }
 
 // Tapas configures the environment to build one or more unbundled apps,
@@ -961,3 +962,14 @@
 func (c *configImpl) IsPdkBuild() bool {
 	return c.pdkBuild
 }
+
+func (c *configImpl) BuildDateTime() string {
+	return c.buildDateTime
+}
+
+func (c *configImpl) MetricsUploaderApp() string {
+	if p, ok := c.environ.Get("ANDROID_ENABLE_METRICS_UPLOAD"); ok {
+		return p
+	}
+	return ""
+}
diff --git a/ui/build/config_test.go b/ui/build/config_test.go
index df618c4..7b14c47 100644
--- a/ui/build/config_test.go
+++ b/ui/build/config_test.go
@@ -26,6 +26,7 @@
 	"testing"
 
 	"android/soong/ui/logger"
+	"android/soong/ui/status"
 )
 
 func testContext() Context {
@@ -33,6 +34,7 @@
 		Context: context.Background(),
 		Logger:  logger.New(&bytes.Buffer{}),
 		Writer:  &bytes.Buffer{},
+		Status:  &status.Status{},
 	}}
 }
 
diff --git a/ui/build/dumpvars.go b/ui/build/dumpvars.go
index a559330..e229856 100644
--- a/ui/build/dumpvars.go
+++ b/ui/build/dumpvars.go
@@ -54,18 +54,16 @@
 
 	var ret map[string]string
 	if len(makeVars) > 0 {
+		// It's not safe to use the same TMPDIR as the build, as that can be removed.
 		tmpDir, err := ioutil.TempDir("", "dumpvars")
 		if err != nil {
 			return nil, err
 		}
 		defer os.RemoveAll(tmpDir)
 
-		// It's not safe to use the same TMPDIR as the build, as that can be removed.
-		config.Environment().Set("TMPDIR", tmpDir)
+		SetupLitePath(ctx, config, tmpDir)
 
-		SetupLitePath(ctx, config)
-
-		ret, err = dumpMakeVars(ctx, config, goals, makeVars, false)
+		ret, err = dumpMakeVars(ctx, config, goals, makeVars, false, tmpDir)
 		if err != nil {
 			return ret, err
 		}
@@ -82,7 +80,7 @@
 	return ret, nil
 }
 
-func dumpMakeVars(ctx Context, config Config, goals, vars []string, write_soong_vars bool) (map[string]string, error) {
+func dumpMakeVars(ctx Context, config Config, goals, vars []string, write_soong_vars bool, tmpDir string) (map[string]string, error) {
 	ctx.BeginTrace(metrics.RunKati, "dumpvars")
 	defer ctx.EndTrace()
 
@@ -98,6 +96,9 @@
 		cmd.Environment.Set("WRITE_SOONG_VARIABLES", "true")
 	}
 	cmd.Environment.Set("DUMP_MANY_VARS", strings.Join(vars, " "))
+	if tmpDir != "" {
+		cmd.Environment.Set("TMPDIR", tmpDir)
+	}
 	cmd.Sandbox = dumpvarsSandbox
 	output := bytes.Buffer{}
 	cmd.Stdout = &output
@@ -142,6 +143,7 @@
 	"TARGET_BUILD_VARIANT",
 	"TARGET_BUILD_TYPE",
 	"TARGET_BUILD_APPS",
+	"TARGET_BUILD_UNBUNDLED",
 	"TARGET_ARCH",
 	"TARGET_ARCH_VARIANT",
 	"TARGET_CPU_VARIANT",
@@ -186,6 +188,7 @@
 		"TARGET_PRODUCT",
 		"TARGET_BUILD_VARIANT",
 		"TARGET_BUILD_APPS",
+		"TARGET_BUILD_UNBUNDLED",
 
 		// compiler wrappers set up by make
 		"CC_WRAPPER",
@@ -253,7 +256,7 @@
 		"BUILD_BROKEN_USES_BUILD_STATIC_LIBRARY",
 	}, exportEnvVars...), BannerVars...)
 
-	make_vars, err := dumpMakeVars(ctx, config, config.Arguments(), allVars, true)
+	make_vars, err := dumpMakeVars(ctx, config, config.Arguments(), allVars, true, "")
 	if err != nil {
 		ctx.Fatalln("Error dumping make vars:", err)
 	}
diff --git a/ui/build/path.go b/ui/build/path.go
index 7122927..6f5cf78 100644
--- a/ui/build/path.go
+++ b/ui/build/path.go
@@ -55,8 +55,9 @@
 }
 
 // A "lite" version of SetupPath used for dumpvars, or other places that need
-// minimal overhead (but at the expense of logging).
-func SetupLitePath(ctx Context, config Config) {
+// minimal overhead (but at the expense of logging). If tmpDir is empty, the
+// default TMPDIR is used from config.
+func SetupLitePath(ctx Context, config Config, tmpDir string) {
 	if config.pathReplaced {
 		return
 	}
@@ -65,8 +66,11 @@
 	defer ctx.EndTrace()
 
 	origPath, _ := config.Environment().Get("PATH")
-	myPath, _ := config.Environment().Get("TMPDIR")
-	myPath = filepath.Join(myPath, "path")
+
+	if tmpDir == "" {
+		tmpDir, _ = config.Environment().Get("TMPDIR")
+	}
+	myPath := filepath.Join(tmpDir, "path")
 	ensureEmptyDirectoriesExist(ctx, myPath)
 
 	os.Setenv("PATH", origPath)
diff --git a/ui/build/upload.go b/ui/build/upload.go
new file mode 100644
index 0000000..75a5e2f
--- /dev/null
+++ b/ui/build/upload.go
@@ -0,0 +1,80 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package build
+
+// This file contains the functionality to upload data from one location to
+// another.
+
+import (
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"time"
+
+	"github.com/golang/protobuf/proto"
+
+	upload_proto "android/soong/ui/metrics/upload_proto"
+)
+
+const (
+	uploadPbFilename = ".uploader.pb"
+)
+
+// UploadMetrics uploads a set of metrics files to a server for analysis. An
+// uploader full path is required to be specified in order to upload the set
+// of metrics files. This is accomplished by defining the ANDROID_ENABLE_METRICS_UPLOAD
+// environment variable.
+func UploadMetrics(ctx Context, config Config, buildStartedMilli int64, files ...string) {
+	uploader := config.MetricsUploaderApp()
+	// No metrics to upload if the path to the uploader was not specified.
+	if uploader == "" {
+		return
+	}
+
+	// Some files may not exist. For example, build errors protobuf file
+	// may not exist since the build was successful.
+	var metricsFiles []string
+	for _, f := range files {
+		if _, err := os.Stat(f); err == nil {
+			metricsFiles = append(metricsFiles, f)
+		}
+	}
+
+	if len(metricsFiles) == 0 {
+		return
+	}
+
+	// For platform builds, the branch and target name is hardcoded to specific
+	// values for later extraction of the metrics in the data metrics pipeline.
+	data, err := proto.Marshal(&upload_proto.Upload{
+		CreationTimestampMs:   proto.Uint64(uint64(buildStartedMilli)),
+		CompletionTimestampMs: proto.Uint64(uint64(time.Now().UnixNano() / int64(time.Millisecond))),
+		BranchName:            proto.String("developer-metrics"),
+		TargetName:            proto.String("platform-build-systems-metrics"),
+		MetricsFiles:          metricsFiles,
+	})
+	if err != nil {
+		ctx.Fatalf("failed to marshal metrics upload proto buffer message: %v\n", err)
+	}
+
+	pbFile := filepath.Join(config.OutDir(), uploadPbFilename)
+	if err := ioutil.WriteFile(pbFile, data, 0644); err != nil {
+		ctx.Fatalf("failed to write the marshaled metrics upload protobuf to %q: %v\n", pbFile, err)
+	}
+	// Remove the upload file as it's not longer needed after it has been processed by the uploader.
+	defer os.Remove(pbFile)
+
+	Command(ctx, config, "upload metrics", uploader, "--upload-metrics", pbFile).RunAndStreamOrFatal()
+}
diff --git a/ui/build/upload_test.go b/ui/build/upload_test.go
new file mode 100644
index 0000000..adaa08d
--- /dev/null
+++ b/ui/build/upload_test.go
@@ -0,0 +1,120 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package build
+
+import (
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"strconv"
+	"strings"
+	"testing"
+	"time"
+
+	"android/soong/ui/logger"
+)
+
+func TestUploadMetrics(t *testing.T) {
+	ctx := testContext()
+	tests := []struct {
+		description string
+		uploader    string
+		createFiles bool
+		files       []string
+	}{{
+		description: "ANDROID_ENABLE_METRICS_UPLOAD not set",
+	}, {
+		description: "no metrics files to upload",
+		uploader:    "fake",
+	}, {
+		description: "non-existent metrics files no upload",
+		uploader:    "fake",
+		files:       []string{"metrics_file_1", "metrics_file_2", "metrics_file_3"},
+	}, {
+		description: "trigger upload",
+		uploader:    "echo",
+		createFiles: true,
+		files:       []string{"metrics_file_1", "metrics_file_2"},
+	}}
+
+	for _, tt := range tests {
+		t.Run(tt.description, func(t *testing.T) {
+			defer logger.Recover(func(err error) {
+				t.Fatalf("got unexpected error: %v", err)
+			})
+
+			outDir, err := ioutil.TempDir("", "")
+			if err != nil {
+				t.Fatalf("failed to create out directory: %v", outDir)
+			}
+			defer os.RemoveAll(outDir)
+
+			var metricsFiles []string
+			if tt.createFiles {
+				for _, f := range tt.files {
+					filename := filepath.Join(outDir, f)
+					metricsFiles = append(metricsFiles, filename)
+					if err := ioutil.WriteFile(filename, []byte("test file"), 0644); err != nil {
+						t.Fatalf("failed to create a fake metrics file %q for uploading: %v", filename, err)
+					}
+				}
+			}
+
+			config := Config{&configImpl{
+				environ: &Environment{
+					"OUT_DIR=" + outDir,
+					"ANDROID_ENABLE_METRICS_UPLOAD=" + tt.uploader,
+				},
+				buildDateTime: strconv.FormatInt(time.Now().UnixNano()/int64(time.Millisecond), 10),
+			}}
+
+			UploadMetrics(ctx, config, 1591031903, metricsFiles...)
+
+			if _, err := os.Stat(filepath.Join(outDir, uploadPbFilename)); err == nil {
+				t.Error("got true, want false for upload protobuf file to exist")
+			}
+		})
+	}
+}
+
+func TestUploadMetricsErrors(t *testing.T) {
+	expectedErr := "failed to write the marshaled"
+	defer logger.Recover(func(err error) {
+		got := err.Error()
+		if !strings.Contains(got, expectedErr) {
+			t.Errorf("got %q, want %q to be contained in error", got, expectedErr)
+		}
+	})
+
+	outDir, err := ioutil.TempDir("", "")
+	if err != nil {
+		t.Fatalf("failed to create out directory: %v", outDir)
+	}
+	defer os.RemoveAll(outDir)
+
+	metricsFile := filepath.Join(outDir, "metrics_file_1")
+	if err := ioutil.WriteFile(metricsFile, []byte("test file"), 0644); err != nil {
+		t.Fatalf("failed to create a fake metrics file %q for uploading: %v", metricsFile, err)
+	}
+
+	config := Config{&configImpl{
+		environ: &Environment{
+			"ANDROID_ENABLE_METRICS_UPLOAD=fake",
+			"OUT_DIR=/bad",
+		}}}
+
+	UploadMetrics(testContext(), config, 1591031903, metricsFile)
+	t.Errorf("got nil, expecting %q as a failure", expectedErr)
+}
diff --git a/xml/Android.bp b/xml/Android.bp
new file mode 100644
index 0000000..cd25cff
--- /dev/null
+++ b/xml/Android.bp
@@ -0,0 +1,18 @@
+bootstrap_go_package {
+    name: "soong-xml",
+    pkgPath: "android/soong/xml",
+    deps: [
+        "blueprint",
+        "blueprint-pathtools",
+        "soong",
+        "soong-android",
+        "soong-etc",
+    ],
+    srcs: [
+        "xml.go",
+    ],
+    testSrcs: [
+        "xml_test.go",
+    ],
+    pluginFor: ["soong_build"],
+}
diff --git a/xml/xml.go b/xml/xml.go
index 3a680ec..8810ae4 100644
--- a/xml/xml.go
+++ b/xml/xml.go
@@ -16,6 +16,7 @@
 
 import (
 	"android/soong/android"
+	"android/soong/etc"
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
@@ -62,7 +63,7 @@
 }
 
 type prebuiltEtcXml struct {
-	android.PrebuiltEtc
+	etc.PrebuiltEtc
 
 	properties prebuiltEtcXmlProperties
 }
@@ -121,7 +122,7 @@
 func PrebuiltEtcXmlFactory() android.Module {
 	module := &prebuiltEtcXml{}
 	module.AddProperties(&module.properties)
-	android.InitPrebuiltEtcModule(&module.PrebuiltEtc, "etc")
+	etc.InitPrebuiltEtcModule(&module.PrebuiltEtc, "etc")
 	// This module is device-only
 	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
 	return module
diff --git a/xml/xml_test.go b/xml/xml_test.go
index f8ec823..abcb108 100644
--- a/xml/xml_test.go
+++ b/xml/xml_test.go
@@ -20,6 +20,7 @@
 	"testing"
 
 	"android/soong/android"
+	"android/soong/etc"
 )
 
 var buildDir string
@@ -57,7 +58,7 @@
 	}
 	config := android.TestArchConfig(buildDir, nil, bp, fs)
 	ctx := android.NewTestArchContext()
-	ctx.RegisterModuleType("prebuilt_etc", android.PrebuiltEtcFactory)
+	ctx.RegisterModuleType("prebuilt_etc", etc.PrebuiltEtcFactory)
 	ctx.RegisterModuleType("prebuilt_etc_xml", PrebuiltEtcXmlFactory)
 	ctx.Register(config)
 	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})