Merge "Disallow system m4"
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..a09c56d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+/.idea
diff --git a/Android.bp b/Android.bp
index fff17ef..0382ee2 100644
--- a/Android.bp
+++ b/Android.bp
@@ -45,18 +45,22 @@
         "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/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",
@@ -65,6 +69,8 @@
         "android/proto.go",
         "android/register.go",
         "android/rule_builder.go",
+        "android/sandbox.go",
+        "android/sdk.go",
         "android/sh_binary.go",
         "android/singleton.go",
         "android/testing.go",
@@ -79,14 +85,17 @@
     ],
     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",
@@ -147,9 +156,11 @@
         "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",
@@ -167,6 +178,7 @@
         "cc/vndk_prebuilt.go",
         "cc/xom.go",
 
+        "cc/cflag_artifacts.go",
         "cc/cmakelists.go",
         "cc/compdb.go",
         "cc/compiler.go",
@@ -174,7 +186,9 @@
         "cc/linker.go",
 
         "cc/binary.go",
+        "cc/fuzz.go",
         "cc/library.go",
+        "cc/library_sdk_member.go",
         "cc/object.go",
         "cc/test.go",
         "cc/toolchain_library.go",
@@ -196,13 +210,14 @@
     ],
     testSrcs: [
         "cc/cc_test.go",
+        "cc/compiler_test.go",
         "cc/gen_test.go",
         "cc/genrule_test.go",
         "cc/library_test.go",
+        "cc/object_test.go",
         "cc/prebuilt_test.go",
         "cc/proto_test.go",
         "cc/test_data_test.go",
-        "cc/util_test.go",
     ],
     pluginFor: ["soong_build"],
 }
@@ -277,6 +292,7 @@
         "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",
@@ -284,10 +300,13 @@
         "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",
@@ -296,7 +315,6 @@
         "java/jdeps_test.go",
         "java/kotlin_test.go",
         "java/plugin_test.go",
-        "java/robolectric_test.go",
         "java/sdk_test.go",
     ],
     pluginFor: ["soong_build"],
@@ -318,6 +336,57 @@
 }
 
 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/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/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: [
@@ -395,8 +464,12 @@
         "soong-python",
     ],
     srcs: [
+        "apex/androidmk.go",
         "apex/apex.go",
+        "apex/builder.go",
         "apex/key.go",
+        "apex/prebuilt.go",
+        "apex/vndk.go",
     ],
     testSrcs: [
         "apex/apex_test.go",
@@ -423,6 +496,33 @@
     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/cc_sdk_test.go",
+        "sdk/exports_test.go",
+        "sdk/java_sdk_test.go",
+        "sdk/sdk_test.go",
+        "sdk/testing.go",
+    ],
+    pluginFor: ["soong_build"],
+}
+
 //
 // Defaults to enable various configurations of host bionic
 //
@@ -500,106 +600,21 @@
     arch: {
         arm: {
             src: "prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9/lib/gcc/arm-linux-androideabi/4.9.x/libgcc.a",
-            strip: {
-                keep_symbols_list: [
-                    // unwind-arm.o
-                    "_Unwind_Complete",
-                    "_Unwind_DeleteException",
-                    "_Unwind_GetCFA",
-                    "_Unwind_VRS_Get",
-                    "_Unwind_VRS_Pop",
-                    "_Unwind_VRS_Set",
-                    "__aeabi_unwind_cpp_pr0",
-                    "__aeabi_unwind_cpp_pr1",
-                    "__aeabi_unwind_cpp_pr2",
-                    "__gnu_Unwind_Backtrace",
-                    "__gnu_Unwind_ForcedUnwind",
-                    "__gnu_Unwind_RaiseException",
-                    "__gnu_Unwind_Resume",
-                    "__gnu_Unwind_Resume_or_Rethrow",
-
-                    // libunwind.o
-                    "_Unwind_Backtrace",
-                    "_Unwind_ForcedUnwind",
-                    "_Unwind_RaiseException",
-                    "_Unwind_Resume",
-                    "_Unwind_Resume_or_Rethrow",
-                    "___Unwind_Backtrace",
-                    "___Unwind_ForcedUnwind",
-                    "___Unwind_RaiseException",
-                    "___Unwind_Resume",
-                    "___Unwind_Resume_or_Rethrow",
-                    "__gnu_Unwind_Restore_VFP",
-                    "__gnu_Unwind_Restore_VFP_D",
-                    "__gnu_Unwind_Restore_VFP_D_16_to_31",
-                    "__gnu_Unwind_Restore_WMMXC",
-                    "__gnu_Unwind_Restore_WMMXD",
-                    "__gnu_Unwind_Save_VFP",
-                    "__gnu_Unwind_Save_VFP_D",
-                    "__gnu_Unwind_Save_VFP_D_16_to_31",
-                    "__gnu_Unwind_Save_WMMXC",
-                    "__gnu_Unwind_Save_WMMXD",
-                    "__restore_core_regs",
-                    "restore_core_regs",
-
-                    // pr-support.o
-                    "_Unwind_GetDataRelBase",
-                    "_Unwind_GetLanguageSpecificData",
-                    "_Unwind_GetRegionStart",
-                    "_Unwind_GetTextRelBase",
-                    "__gnu_unwind_execute",
-                    "__gnu_unwind_frame",
-                ],
-                use_gnu_strip: true,
-            },
+            repack_objects_to_keep: ["unwind-arm.o", "libunwind.o", "pr-support.o"],
         },
         arm64: {
             src: "prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/lib/gcc/aarch64-linux-android/4.9.x/libgcc.a",
+            repack_objects_to_keep: ["unwind-dw2.o", "unwind-dw2-fde-dip.o"],
         },
         x86: {
             src: "prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9/lib/gcc/x86_64-linux-android/4.9.x/32/libgcc.a",
-
+            repack_objects_to_keep: ["unwind-dw2.o", "unwind-dw2-fde-dip.o"],
         },
         x86_64: {
             src: "prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9/lib/gcc/x86_64-linux-android/4.9.x/libgcc.a",
+            repack_objects_to_keep: ["unwind-dw2.o", "unwind-dw2-fde-dip.o"],
         },
     },
-    strip: {
-        keep_symbols_list: [
-            // unwind-dw2.o
-            "_Unwind_Backtrace",
-            "_Unwind_DeleteException",
-            "_Unwind_FindEnclosingFunction",
-            "_Unwind_ForcedUnwind",
-            "_Unwind_GetCFA",
-            "_Unwind_GetDataRelBase",
-            "_Unwind_GetGR",
-            "_Unwind_GetIP",
-            "_Unwind_GetIPInfo",
-            "_Unwind_GetLanguageSpecificData",
-            "_Unwind_GetRegionStart",
-            "_Unwind_GetTextRelBase",
-            "_Unwind_RaiseException",
-            "_Unwind_Resume",
-            "_Unwind_Resume_or_Rethrow",
-            "_Unwind_SetGR",
-            "_Unwind_SetIP",
-            "__frame_state_for",
-
-            // unwind-dw2-fde-dip.o
-            "_Unwind_Find_FDE",
-            "__deregister_frame",
-            "__deregister_frame_info",
-            "__deregister_frame_info_bases",
-            "__register_frame",
-            "__register_frame_info",
-            "__register_frame_info_bases",
-            "__register_frame_info_table",
-            "__register_frame_info_table_bases",
-            "__register_frame_table",
-        ],
-        use_gnu_strip: true,
-    },
 }
 
 toolchain_library {
diff --git a/README.md b/README.md
index 8fdce4b..b6fda50 100644
--- a/README.md
+++ b/README.md
@@ -36,13 +36,28 @@
 For a list of valid module types and their properties see
 [$OUT_DIR/soong/docs/soong_build.html](https://ci.android.com/builds/latest/branches/aosp-build-tools/targets/linux/view/soong_build.html).
 
-### Globs
+### File lists
 
-Properties that take a list of files can also take glob patterns.  Glob
-patterns can contain the normal Unix wildcard `*`, for example "*.java". Glob
-patterns can also contain a single `**` wildcard as a path element, which will
-match zero or more path elements.  For example, `java/**/*.java` will match
-`java/Main.java` and `java/com/android/Main.java`.
+Properties that take a list of files can also take glob patterns and output path
+expansions.
+
+* Glob patterns can contain the normal Unix wildcard `*`, for example `"*.java"`.
+
+  Glob patterns can also contain a single `**` wildcard as a path element, which
+  will match zero or more path elements. For example, `java/**/*.java` will match
+  `java/Main.java` and `java/com/android/Main.java`.
+
+* Output path expansions take the format `:module` or `:module{.tag}`, where
+  `module` is the name of a module that produces output files, and it expands to
+  a list of those output files. With the optional `{.tag}` suffix, the module
+  may produce a different list of outputs according to `tag`.
+
+  For example, a `droiddoc` module with the name "my-docs" would return its
+  `.stubs.srcjar` output with `":my-docs"`, and its `.doc.zip` file with
+  `":my-docs{.doc.zip}"`.
+
+  This is commonly used to reference `filegroup` modules, whose output files
+  consist of their `srcs`.
 
 ### Variables
 
@@ -59,11 +74,12 @@
 ```
 
 Variables are scoped to the remainder of the file they are declared in, as well
-as any child blueprint files.  Variables are immutable with one exception - they
+as any child Android.bp files.  Variables are immutable with one exception - they
 can be appended to with a += assignment, but only before they have been
 referenced.
 
 ### Comments
+
 Android.bp files can contain C-style multiline `/* */` and C++ style single-line
 `//` comments.
 
@@ -133,37 +149,133 @@
 
 This is based on the Bazel package concept.
 
-### Name resolution
+The `package` module type allows information to be specified about a package. Only a single
+`package` module can be specified per package and in the case where there are multiple `.bp` files
+in the same package directory it is highly recommended that the `package` module (if required) is
+specified in the `Android.bp` file.
 
-Soong provides the ability for modules in different directories to specify
-the same name, as long as each module is declared within a separate namespace.
-A namespace can be declared like this:
+Unlike most module type `package` does not have a `name` property. Instead the name is set to the
+name of the package, e.g. if the package is in `top/intermediate/package` then the package name is
+`//top/intermediate/package`.
+
+E.g. The following will set the default visibility for all the modules defined in the package and
+any subpackages that do not set their own default visibility (irrespective of whether they are in
+the same `.bp` file as the `package` module) to be visible to all the subpackages by default.
 
 ```
-soong_namespace {
-    imports: ["path/to/otherNamespace1", "path/to/otherNamespace2"],
+package {
+    default_visibility: [":__subpackages"]
 }
 ```
 
-Each Soong module is assigned a namespace based on its location in the tree.
-Each Soong module is considered to be in the namespace defined by the
-soong_namespace found in an Android.bp in the current directory or closest
-ancestor directory, unless no such soong_namespace module is found, in which
-case the module is considered to be in the implicit root namespace.
+### Referencing Modules
 
-When Soong attempts to resolve dependency D declared my module M in namespace
-N which imports namespaces I1, I2, I3..., then if D is a fully-qualified name
-of the form "//namespace:module", only the specified namespace will be searched
-for the specified module name. Otherwise, Soong will first look for a module
-named D declared in namespace N. If that module does not exist, Soong will look
-for a module named D in namespaces I1, I2, I3... Lastly, Soong will look in the
-root namespace.
+A module `libfoo` can be referenced by its name
 
-Until we have fully converted from Make to Soong, it will be necessary for the
-Make product config to specify a value of PRODUCT_SOONG_NAMESPACES. Its value
-should be a space-separated list of namespaces that Soong export to Make to be
-built by the `m` command. After we have fully converted from Make to Soong, the
-details of enabling namespaces could potentially change.
+```
+cc_binary {
+    name: "app",
+    shared_libs: ["libfoo"],
+}
+```
+
+Obviously, this works only if there is only one `libfoo` module in the source
+tree. Ensuring such name uniqueness for larger trees may become problematic. We
+might also want to use the same name in multiple mutually exclusive subtrees
+(for example, implementing different devices) deliberately in order to describe
+a functionally equivalent module. Enter Soong namespaces.
+
+#### Namespaces
+
+A presense of the `soong_namespace {..}` in an Android.bp file defines a
+**namespace**. For instance, having
+
+```
+soong_namespace {
+    ...
+}
+...
+```
+
+in `device/google/bonito/Android.bp` informs Soong that within the
+`device/google/bonito` package the module names are unique, that is, all the
+modules defined in the Android.bp files in the `device/google/bonito/` tree have
+unique names. However, there may be modules with the same names outside
+`device/google/bonito` tree. Indeed, there is a module `"pixelstats-vendor"`
+both in `device/google/bonito/pixelstats` and in
+`device/google/coral/pixelstats`.
+
+The name of a namespace is the path of its directory. The name of the namespace
+in the example above is thus `device/google/bonito`.
+
+An implicit **global namespace** corresponds to the source tree as a whole. It
+has empty name.
+
+A module name's **scope** is the smallest namespace containing it. Suppose a
+source tree has `device/my` and `device/my/display` namespaces. If `libfoo`
+module is defined in `device/co/display/lib/Android.bp`, its namespace is
+`device/co/display`.
+
+The name uniqueness thus means that module's name is unique within its scope. In
+other words, "//_scope_:_name_" is globally unique module reference, e.g,
+`"//device/google/bonito:pixelstats-vendor"`. _Note_ that the name of the
+namespace for a module may be different from module's package name: `libfoo`
+belongs to `device/my/display` namespace but is contained in
+`device/my/display/lib` package.
+
+#### Name Resolution
+
+The form of a module reference determines how Soong locates the module.
+
+For a **global reference** of the "//_scope_:_name_" form, Soong verifies there
+is a namespace called "_scope_", then verifies it contains a "_name_" module and
+uses it. Soong verifies there is only one "_name_" in "_scope_" at the beginning
+when it parses Android.bp files.
+
+A **local reference** has "_name_" form, and resolving it involves looking for a
+module "_name_" in one or more namespaces. By default only the global namespace
+is searched for "_name_" (in other words, only the modules not belonging to an
+explicitly defined scope are considered). The `imports` attribute of the
+`soong_namespaces` allows to specify where to look for modules . For instance,
+with `device/google/bonito/Android.bp` containing
+
+```
+soong_namespace {
+    imports: [
+        "hardware/google/interfaces",
+        "hardware/google/pixel",
+        "hardware/qcom/bootctrl",
+    ],
+}
+```
+
+a reference to `"libpixelstats"` will resolve to the module defined in
+`hardware/google/pixel/pixelstats/Android.bp` because this module is in
+`hardware/google/pixel` namespace.
+
+**TODO**: Conventionally, languages with similar concepts provide separate
+constructs for namespace definition and name resolution (`namespace` and `using`
+in C++, for instance). Should Soong do that, too?
+
+#### Referencing modules in makefiles
+
+While we are gradually converting makefiles to Android.bp files, Android build
+is described by a mixture of Android.bp and Android.mk files, and a module
+defined in an Android.mk file can reference a module defined in Android.bp file.
+For instance, a binary still defined in an Android.mk file may have a library
+defined in already converted Android.bp as a dependency.
+
+A module defined in an Android.bp file and belonging to the global namespace can
+be referenced from a makefile without additional effort. If a module belongs to
+an explicit namespace, it can be referenced from a makefile only after after the
+name of the namespace has been added to the value of PRODUCT_SOONG_NAMESPACES
+variable.
+
+Note that makefiles have no notion of namespaces and exposing namespaces with
+the same modules via PRODUCT_SOONG_NAMESPACES may cause Make failure. For
+instance, exposing both `device/google/bonito` and `device/google/coral`
+namespaces will cause Make failure because it will see two targets for the
+`pixelstats-vendor` module.
 
 ### Visibility
 
@@ -191,13 +303,13 @@
 `//independent:evil`)
 * `["//project"]`: This is shorthand for `["//project:__pkg__"]`
 * `[":__subpackages__"]`: This is shorthand for `["//project:__subpackages__"]`
-where `//project` is the module's package. e.g. using `[":__subpackages__"]` in
+where `//project` is the module's package, e.g. using `[":__subpackages__"]` in
 `packages/apps/Settings/Android.bp` is equivalent to
 `//packages/apps/Settings:__subpackages__`.
 * `["//visibility:legacy_public"]`: The default visibility, behaves as
 `//visibility:public` for now. It is an error if it is used in a module.
 
-The visibility rules of `//visibility:public` and `//visibility:private` can not
+The visibility rules of `//visibility:public` and `//visibility:private` cannot
 be combined with any other visibility specifications, except
 `//visibility:public` is allowed to override visibility specifications imported
 through the `defaults` property.
@@ -207,17 +319,31 @@
 say `vendor/google`, instead it must make itself visible to all packages within
 `vendor/` using `//vendor:__subpackages__`.
 
-If a module does not specify the `visibility` property the module is
-`//visibility:legacy_public`. Once the build has been completely switched over to
-soong it is possible that a global refactoring will be done to change this to
-`//visibility:private` at which point all modules that do not currently specify
-a `visibility` property will be updated to have
-`visibility = [//visibility:legacy_public]` added. It will then be the owner's
-responsibility to replace that with a more appropriate visibility.
+If a module does not specify the `visibility` property then it uses the
+`default_visibility` property of the `package` module in the module's package.
+
+If the `default_visibility` property is not set for the module's package then
+it will use the `default_visibility` of its closest ancestor package for which
+a `default_visibility` property is specified.
+
+If no `default_visibility` property can be found then the module uses the
+global default of `//visibility:legacy_public`.
+
+The `visibility` property has no effect on a defaults module although it does
+apply to any non-defaults module that uses it. To set the visibility of a
+defaults module, use the `defaults_visibility` property on the defaults module;
+not to be confused with the `default_visibility` property on the package module.
+
+Once the build has been completely switched over to soong it is possible that a
+global refactoring will be done to change this to `//visibility:private` at
+which point all packages that do not currently specify a `default_visibility`
+property will be updated to have
+`default_visibility = [//visibility:legacy_public]` added. It will then be the
+owner's responsibility to replace that with a more appropriate visibility.
 
 ### Formatter
 
-Soong includes a canonical formatter for blueprint files, similar to
+Soong includes a canonical formatter for Android.bp files, similar to
 [gofmt](https://golang.org/cmd/gofmt/).  To recursively reformat all Android.bp files
 in the current directory:
 ```
@@ -270,12 +396,19 @@
 
 ### How do I write conditionals?
 
-Soong deliberately does not support conditionals in Android.bp files.
-Instead, complexity in build rules that would require conditionals are handled
-in Go, where high level language features can be used and implicit dependencies
-introduced by conditionals can be tracked.  Most conditionals are converted
-to a map property, where one of the values in the map will be selected and
-appended to the top level properties.
+Soong deliberately does not support conditionals in Android.bp files.  We
+suggest removing most conditionals from the build.  See
+[Best Practices](docs/best_practices.md#removing-conditionals) for some
+examples on how to remove conditionals.
+
+In cases where build time conditionals are unavoidable, complexity in build
+rules that would require conditionals are handled in Go through Soong plugins.
+This allows Go language features to be used for better readability and
+testability, and implicit dependencies introduced by conditionals can be
+tracked.  Most conditionals supported natively by Soong are converted to a map
+property.  When building the module one of the properties in the map will be
+selected, and its values appended to the property with the same name at the
+top level of the module.
 
 For example, to support architecture specific files:
 ```
@@ -293,9 +426,9 @@
 }
 ```
 
-See [art/build/art.go](https://android.googlesource.com/platform/art/+/master/build/art.go)
-or [external/llvm/soong/llvm.go](https://android.googlesource.com/platform/external/llvm/+/master/soong/llvm.go)
-for examples of more complex conditionals on product variables or environment variables.
+When building the module for arm the `generic.cpp` and `arm.cpp` sources will
+be built.  When building for x86 the `generic.cpp` and 'x86.cpp' sources will
+be built.
 
 ## Developing for Soong
 
@@ -309,6 +442,31 @@
 This will bind mount the Soong source directories into the directory in the layout expected by
 the IDE.
 
+### Running Soong in a debugger
+
+To run the soong_build process in a debugger, install `dlv` and then start the build with
+`SOONG_DELVE=<listen addr>` in the environment.
+For example:
+```bash
+SOONG_DELVE=:1234 m nothing
+```
+and then in another terminal:
+```
+dlv connect :1234
+```
+
+If you see an error:
+```
+Could not attach to pid 593: this could be caused by a kernel
+security setting, try writing "0" to /proc/sys/kernel/yama/ptrace_scope
+```
+you can temporarily disable
+[Yama's ptrace protection](https://www.kernel.org/doc/Documentation/security/Yama.txt)
+using:
+```bash
+sudo sysctl -w kernel.yama.ptrace_scope=0
+```
+
 ## Contact
 
 Email android-building@googlegroups.com (external) for any questions, or see
diff --git a/android/androidmk.go b/android/androidmk.go
index 7d0aa3b..dbf3aa8 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -61,7 +61,7 @@
 
 // Allows modules to customize their Android*.mk output.
 type AndroidMkEntriesProvider interface {
-	AndroidMkEntries() AndroidMkEntries
+	AndroidMkEntries() []AndroidMkEntries
 	BaseModuleName() string
 }
 
@@ -79,12 +79,16 @@
 	header bytes.Buffer
 	footer bytes.Buffer
 
-	AddCustomEntries func(name, prefix, moduleDir string, entries *AndroidMkEntries)
+	ExtraEntries []AndroidMkExtraEntriesFunc
+	ExtraFooters []AndroidMkExtraFootersFunc
 
 	EntryMap   map[string][]string
 	entryOrder []string
 }
 
+type AndroidMkExtraEntriesFunc func(entries *AndroidMkEntries)
+type AndroidMkExtraFootersFunc func(w io.Writer, name, prefix, moduleDir string, entries *AndroidMkEntries)
+
 func (a *AndroidMkEntries) SetString(name, value string) {
 	if _, ok := a.EntryMap[name]; !ok {
 		a.entryOrder = append(a.entryOrder, name)
@@ -92,6 +96,13 @@
 	a.EntryMap[name] = []string{value}
 }
 
+func (a *AndroidMkEntries) SetPath(name string, path Path) {
+	if _, ok := a.EntryMap[name]; !ok {
+		a.entryOrder = append(a.entryOrder, name)
+	}
+	a.EntryMap[name] = []string{path.String()}
+}
+
 func (a *AndroidMkEntries) SetBoolIfTrue(name string, flag bool) {
 	if flag {
 		if _, ok := a.EntryMap[name]; !ok {
@@ -101,6 +112,17 @@
 	}
 }
 
+func (a *AndroidMkEntries) SetBool(name string, flag bool) {
+	if _, ok := a.EntryMap[name]; !ok {
+		a.entryOrder = append(a.entryOrder, name)
+	}
+	if flag {
+		a.EntryMap[name] = []string{"true"}
+	} else {
+		a.EntryMap[name] = []string{"false"}
+	}
+}
+
 func (a *AndroidMkEntries) AddStrings(name string, value ...string) {
 	if len(value) == 0 {
 		return
@@ -177,29 +199,21 @@
 	switch amod.Os().Class {
 	case Host:
 		// Make cannot identify LOCAL_MODULE_HOST_ARCH:= common.
-		if archStr != "common" {
+		if amod.Arch().ArchType != Common {
 			a.SetString("LOCAL_MODULE_HOST_ARCH", archStr)
 		}
 		host = true
 	case HostCross:
 		// Make cannot identify LOCAL_MODULE_HOST_CROSS_ARCH:= common.
-		if archStr != "common" {
+		if amod.Arch().ArchType != Common {
 			a.SetString("LOCAL_MODULE_HOST_CROSS_ARCH", archStr)
 		}
 		host = true
 	case Device:
 		// Make cannot identify LOCAL_MODULE_TARGET_ARCH:= common.
-		if archStr != "common" {
+		if amod.Arch().ArchType != Common {
 			if amod.Target().NativeBridge {
-				// TODO: Unhardcode these rules.
-				guestArchStr := archStr
-				hostArchStr := ""
-				if guestArchStr == "arm" {
-					hostArchStr = "x86"
-				} else if guestArchStr == "arm64" {
-					hostArchStr = "x86_64"
-				}
-
+				hostArchStr := amod.Target().NativeBridgeHostArchName
 				if hostArchStr != "" {
 					a.SetString("LOCAL_MODULE_TARGET_ARCH", hostArchStr)
 				}
@@ -216,7 +230,7 @@
 		}
 		a.SetBoolIfTrue("LOCAL_ODM_MODULE", Bool(amod.commonProperties.Device_specific))
 		a.SetBoolIfTrue("LOCAL_PRODUCT_MODULE", Bool(amod.commonProperties.Product_specific))
-		a.SetBoolIfTrue("LOCAL_PRODUCT_SERVICES_MODULE", Bool(amod.commonProperties.Product_services_specific))
+		a.SetBoolIfTrue("LOCAL_SYSTEM_EXT_MODULE", Bool(amod.commonProperties.System_ext_specific))
 		if amod.commonProperties.Owner != nil {
 			a.SetString("LOCAL_MODULE_OWNER", *amod.commonProperties.Owner)
 		}
@@ -251,16 +265,27 @@
 			prefix = "2ND_" + prefix
 		}
 	}
-	blueprintDir := filepath.Dir(bpPath)
-	if a.AddCustomEntries != nil {
-		a.AddCustomEntries(name, prefix, blueprintDir, a)
+	for _, extra := range a.ExtraEntries {
+		extra(a)
 	}
 
 	// Write to footer.
 	fmt.Fprintln(&a.footer, "include "+a.Include)
+	blueprintDir := filepath.Dir(bpPath)
+	for _, footerFunc := range a.ExtraFooters {
+		footerFunc(&a.footer, name, prefix, blueprintDir, a)
+	}
 }
 
 func (a *AndroidMkEntries) write(w io.Writer) {
+	if a.Disabled {
+		return
+	}
+
+	if !a.OutputFile.Valid() {
+		return
+	}
+
 	w.Write(a.header.Bytes())
 	for _, name := range a.entryOrder {
 		fmt.Fprintln(w, name+" := "+strings.Join(a.EntryMap[name], " "))
@@ -268,6 +293,10 @@
 	w.Write(a.footer.Bytes())
 }
 
+func (a *AndroidMkEntries) FooterLinesForTests() []string {
+	return strings.Split(string(a.footer.Bytes()), "\n")
+}
+
 func AndroidMkSingleton() Singleton {
 	return &androidMkSingleton{}
 }
@@ -294,7 +323,7 @@
 		return
 	}
 
-	err := translateAndroidMk(ctx, transMk.String(), androidMkModulesList)
+	err := translateAndroidMk(ctx, absolutePath(transMk.String()), androidMkModulesList)
 	if err != nil {
 		ctx.Errorf(err.Error())
 	}
@@ -335,8 +364,8 @@
 	}
 
 	// Don't write to the file if it hasn't changed
-	if _, err := os.Stat(mkFile); !os.IsNotExist(err) {
-		if data, err := ioutil.ReadFile(mkFile); err == nil {
+	if _, err := os.Stat(absolutePath(mkFile)); !os.IsNotExist(err) {
+		if data, err := ioutil.ReadFile(absolutePath(mkFile)); err == nil {
 			matches := buf.Len() == len(data)
 
 			if matches {
@@ -354,7 +383,7 @@
 		}
 	}
 
-	return ioutil.WriteFile(mkFile, buf.Bytes(), 0666)
+	return ioutil.WriteFile(absolutePath(mkFile), buf.Bytes(), 0666)
 }
 
 func translateAndroidMkModule(ctx SingletonContext, w io.Writer, mod blueprint.Module) error {
@@ -388,6 +417,31 @@
 	return nil
 }
 
+func (data *AndroidMkData) fillInData(config Config, bpPath string, mod blueprint.Module) {
+	// Get the preamble content through AndroidMkEntries logic.
+	entries := AndroidMkEntries{
+		Class:           data.Class,
+		SubName:         data.SubName,
+		DistFile:        data.DistFile,
+		OutputFile:      data.OutputFile,
+		Disabled:        data.Disabled,
+		Include:         data.Include,
+		Required:        data.Required,
+		Host_required:   data.Host_required,
+		Target_required: data.Target_required,
+	}
+	entries.fillInEntries(config, bpPath, mod)
+
+	// preamble doesn't need the footer content.
+	entries.footer = bytes.Buffer{}
+	entries.write(&data.preamble)
+
+	// copy entries back to data since it is used in Custom
+	data.Required = entries.Required
+	data.Host_required = entries.Host_required
+	data.Target_required = entries.Target_required
+}
+
 func translateAndroidModule(ctx SingletonContext, w io.Writer, mod blueprint.Module,
 	provider AndroidMkDataProvider) error {
 
@@ -401,22 +455,7 @@
 		data.Include = "$(BUILD_PREBUILT)"
 	}
 
-	// Get the preamble content through AndroidMkEntries logic.
-	entries := AndroidMkEntries{
-		Class:           data.Class,
-		SubName:         data.SubName,
-		DistFile:        data.DistFile,
-		OutputFile:      data.OutputFile,
-		Disabled:        data.Disabled,
-		Include:         data.Include,
-		Required:        data.Required,
-		Host_required:   data.Host_required,
-		Target_required: data.Target_required,
-	}
-	entries.fillInEntries(ctx.Config(), ctx.BlueprintFile(mod), mod)
-	// preamble doesn't need the footer content.
-	entries.footer = bytes.Buffer{}
-	entries.write(&data.preamble)
+	data.fillInData(ctx.Config(), ctx.BlueprintFile(mod), mod)
 
 	prefix := ""
 	if amod.ArchSpecific() {
@@ -471,10 +510,10 @@
 		return nil
 	}
 
-	entries := provider.AndroidMkEntries()
-	entries.fillInEntries(ctx.Config(), ctx.BlueprintFile(mod), mod)
-
-	entries.write(w)
+	for _, entries := range provider.AndroidMkEntries() {
+		entries.fillInEntries(ctx.Config(), ctx.BlueprintFile(mod), mod)
+		entries.write(w)
+	}
 
 	return nil
 }
diff --git a/android/androidmk_test.go b/android/androidmk_test.go
new file mode 100644
index 0000000..71f8020
--- /dev/null
+++ b/android/androidmk_test.go
@@ -0,0 +1,78 @@
+// 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 (
+	"io"
+	"reflect"
+	"testing"
+)
+
+type customModule struct {
+	ModuleBase
+	data AndroidMkData
+}
+
+func (m *customModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+}
+
+func (m *customModule) AndroidMk() AndroidMkData {
+	return AndroidMkData{
+		Custom: func(w io.Writer, name, prefix, moduleDir string, data AndroidMkData) {
+			m.data = data
+		},
+	}
+}
+
+func customModuleFactory() Module {
+	module := &customModule{}
+	InitAndroidModule(module)
+	return module
+}
+
+func TestAndroidMkSingleton_PassesUpdatedAndroidMkDataToCustomCallback(t *testing.T) {
+	bp := `
+	custom {
+		name: "foo",
+		required: ["bar"],
+		host_required: ["baz"],
+		target_required: ["qux"],
+	}
+	`
+
+	config := TestConfig(buildDir, nil, bp, nil)
+	config.inMake = true // Enable androidmk Singleton
+
+	ctx := NewTestContext()
+	ctx.RegisterSingletonType("androidmk", AndroidMkSingleton)
+	ctx.RegisterModuleType("custom", customModuleFactory)
+	ctx.Register(config)
+
+	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+	FailIfErrored(t, errs)
+	_, errs = ctx.PrepareBuildActions(config)
+	FailIfErrored(t, errs)
+
+	m := ctx.ModuleForTests("foo", "").Module().(*customModule)
+
+	assertEqual := func(expected interface{}, actual interface{}) {
+		if !reflect.DeepEqual(expected, actual) {
+			t.Errorf("%q expected, but got %q", expected, actual)
+		}
+	}
+	assertEqual([]string{"bar"}, m.data.Required)
+	assertEqual([]string{"baz"}, m.data.Host_required)
+	assertEqual([]string{"qux"}, m.data.Target_required)
+}
diff --git a/android/apex.go b/android/apex.go
index 17df762..8482dc2 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -17,8 +17,6 @@
 import (
 	"sort"
 	"sync"
-
-	"github.com/google/blueprint"
 )
 
 // ApexModule is the interface that a module type is expected to implement if
@@ -69,14 +67,28 @@
 
 	// Mutate this module into one or more variants each of which is built
 	// for an APEX marked via BuildForApex().
-	CreateApexVariations(mctx BottomUpMutatorContext) []blueprint.Module
+	CreateApexVariations(mctx BottomUpMutatorContext) []Module
 
 	// Sets the name of the apex variant of this module. Called inside
 	// CreateApexVariations.
 	setApexName(apexName string)
+
+	// Tests if this module is available for the specified APEX or ":platform"
+	AvailableFor(what string) bool
+
+	// DepIsInSameApex tests if the other module 'dep' is installed to the same
+	// APEX as this module
+	DepIsInSameApex(ctx BaseModuleContext, dep Module) bool
 }
 
 type ApexProperties struct {
+	// Availability of this module in APEXes. Only the listed APEXes can include this module.
+	// "//apex_available:anyapex" is a pseudo APEX name that matches to any APEX.
+	// "//apex_available:platform" refers to non-APEX partitions like "system.img".
+	// Default is ["//apex_available:platform", "//apex_available:anyapex"].
+	// TODO(b/128708192) change the default to ["//apex_available:platform"]
+	Apex_available []string
+
 	// Name of the apex variant that this module is mutated into
 	ApexName string `blueprint:"mutated"`
 }
@@ -125,15 +137,59 @@
 	return false
 }
 
-func (m *ApexModuleBase) CreateApexVariations(mctx BottomUpMutatorContext) []blueprint.Module {
+const (
+	AvailableToPlatform = "//apex_available:platform"
+	availableToAnyApex  = "//apex_available:anyapex"
+)
+
+func CheckAvailableForApex(what string, apex_available []string) bool {
+	if len(apex_available) == 0 {
+		// apex_available defaults to ["//apex_available:platform", "//apex_available:anyapex"],
+		// which means 'available to everybody'.
+		return true
+	}
+	return InList(what, apex_available) ||
+		(what != AvailableToPlatform && InList(availableToAnyApex, apex_available))
+}
+
+func (m *ApexModuleBase) AvailableFor(what string) bool {
+	return CheckAvailableForApex(what, m.ApexProperties.Apex_available)
+}
+
+func (m *ApexModuleBase) DepIsInSameApex(ctx BaseModuleContext, dep Module) bool {
+	// By default, if there is a dependency from A to B, we try to include both in the same APEX,
+	// unless B is explicitly from outside of the APEX (i.e. a stubs lib). Thus, returning true.
+	// This is overridden by some module types like apex.ApexBundle, cc.Module, java.Module, etc.
+	return true
+}
+
+func (m *ApexModuleBase) checkApexAvailableProperty(mctx BaseModuleContext) {
+	for _, n := range m.ApexProperties.Apex_available {
+		if n == AvailableToPlatform || n == availableToAnyApex {
+			continue
+		}
+		if !mctx.OtherModuleExists(n) && !mctx.Config().AllowMissingDependencies() {
+			mctx.PropertyErrorf("apex_available", "%q is not a valid module name", n)
+		}
+	}
+}
+
+func (m *ApexModuleBase) CreateApexVariations(mctx BottomUpMutatorContext) []Module {
 	if len(m.apexVariations) > 0 {
+		m.checkApexAvailableProperty(mctx)
 		sort.Strings(m.apexVariations)
-		variations := []string{""} // Original variation for platform
+		variations := []string{}
+		availableForPlatform := mctx.Module().(ApexModule).AvailableFor(AvailableToPlatform) || mctx.Host()
+		if availableForPlatform {
+			variations = append(variations, "") // Original variation for platform
+		}
 		variations = append(variations, m.apexVariations...)
 
+		defaultVariation := ""
+		mctx.SetDefaultDependencyVariation(&defaultVariation)
 		modules := mctx.CreateVariations(variations...)
 		for i, m := range modules {
-			if i == 0 {
+			if availableForPlatform && i == 0 {
 				continue
 			}
 			m.(ApexModule).setApexName(variations[i])
@@ -174,6 +230,14 @@
 	apexNames[apexName] = apexNames[apexName] || directDep
 }
 
+// TODO(b/146393795): remove this when b/146393795 is fixed
+func ClearApexDependency() {
+	m := apexNamesMap()
+	for k := range m {
+		delete(m, k)
+	}
+}
+
 // Tests whether a module named moduleName is directly depended on by an APEX
 // named apexName.
 func DirectlyInApex(apexName string, moduleName string) bool {
diff --git a/android/api_levels.go b/android/api_levels.go
index 961685a..4f6efee 100644
--- a/android/api_levels.go
+++ b/android/api_levels.go
@@ -71,6 +71,7 @@
 			"O":     26,
 			"O-MR1": 27,
 			"P":     28,
+			"Q":     29,
 		}
 		for i, codename := range config.PlatformVersionCombinedCodenames() {
 			apiLevelsMap[codename] = baseApiLevel + i
diff --git a/android/arch.go b/android/arch.go
index 46e582c..b5b8a8f 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -22,9 +22,12 @@
 	"strconv"
 	"strings"
 
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
 
+const COMMON_VARIANT = "common"
+
 var (
 	archTypeList []ArchType
 
@@ -36,7 +39,7 @@
 	X86_64 = newArch("x86_64", "lib64")
 
 	Common = ArchType{
-		Name: "common",
+		Name: COMMON_VARIANT,
 	}
 )
 
@@ -527,7 +530,6 @@
 	CpuVariant   string
 	Abi          []string
 	ArchFeatures []string
-	Native       bool
 }
 
 func (a Arch) String() string {
@@ -557,6 +559,10 @@
 	return archType
 }
 
+func ArchTypeList() []ArchType {
+	return append([]ArchType(nil), archTypeList...)
+}
+
 func (a ArchType) String() string {
 	return a.Name
 }
@@ -691,17 +697,90 @@
 )
 
 type Target struct {
-	Os           OsType
-	Arch         Arch
-	NativeBridge NativeBridgeSupport
+	Os                       OsType
+	Arch                     Arch
+	NativeBridge             NativeBridgeSupport
+	NativeBridgeHostArchName string
+	NativeBridgeRelativePath string
 }
 
 func (target Target) String() string {
-	variant := ""
+	return target.OsVariation() + "_" + target.ArchVariation()
+}
+
+func (target Target) OsVariation() string {
+	return target.Os.String()
+}
+
+func (target Target) ArchVariation() string {
+	var variation string
 	if target.NativeBridge {
-		variant = "native_bridge_"
+		variation = "native_bridge_"
 	}
-	return target.Os.String() + "_" + variant + target.Arch.String()
+	variation += target.Arch.String()
+
+	return variation
+}
+
+func (target Target) Variations() []blueprint.Variation {
+	return []blueprint.Variation{
+		{Mutator: "os", Variation: target.OsVariation()},
+		{Mutator: "arch", Variation: target.ArchVariation()},
+	}
+}
+
+func osMutator(mctx BottomUpMutatorContext) {
+	var module Module
+	var ok bool
+	if module, ok = mctx.Module().(Module); !ok {
+		return
+	}
+
+	base := module.base()
+
+	if !base.ArchSpecific() {
+		return
+	}
+
+	osClasses := base.OsClassSupported()
+
+	var moduleOSList []OsType
+
+	for _, os := range osTypeList {
+		supportedClass := false
+		for _, osClass := range osClasses {
+			if os.Class == osClass {
+				supportedClass = true
+			}
+		}
+		if !supportedClass {
+			continue
+		}
+
+		if len(mctx.Config().Targets[os]) == 0 {
+			continue
+		}
+
+		moduleOSList = append(moduleOSList, os)
+	}
+
+	if len(moduleOSList) == 0 {
+		base.commonProperties.Enabled = boolPtr(false)
+		return
+	}
+
+	osNames := make([]string, len(moduleOSList))
+
+	for i, os := range moduleOSList {
+		osNames[i] = os.String()
+	}
+
+	modules := mctx.CreateVariations(osNames...)
+	for i, m := range modules {
+		m.(Module).base().commonProperties.CompileOS = moduleOSList[i]
+		m.(Module).base().setOSProperties(mctx)
+	}
+
 }
 
 // archMutator splits a module into a variant for each Target requested by the module.  Target selection
@@ -741,84 +820,72 @@
 		return
 	}
 
-	var moduleTargets []Target
-	moduleMultiTargets := make(map[int][]Target)
-	primaryModules := make(map[int]bool)
-	osClasses := base.OsClassSupported()
+	os := base.commonProperties.CompileOS
+	osTargets := mctx.Config().Targets[os]
+	image := base.commonProperties.ImageVariation
+	// Filter NativeBridge targets unless they are explicitly supported
+	// Skip creating native bridge variants for vendor modules
+	if os == Android &&
+		!(Bool(base.commonProperties.Native_bridge_supported) && image == CoreVariation) {
 
-	for _, os := range osTypeList {
-		supportedClass := false
-		for _, osClass := range osClasses {
-			if os.Class == osClass {
-				supportedClass = true
+		var targets []Target
+		for _, t := range osTargets {
+			if !t.NativeBridge {
+				targets = append(targets, t)
 			}
 		}
-		if !supportedClass {
-			continue
-		}
 
-		osTargets := mctx.Config().Targets[os]
-		if len(osTargets) == 0 {
-			continue
-		}
+		osTargets = targets
+	}
 
-		// Filter NativeBridge targets unless they are explicitly supported
-		if os == Android && !Bool(base.commonProperties.Native_bridge_supported) {
-			var targets []Target
-			for _, t := range osTargets {
-				if !t.NativeBridge {
-					targets = append(targets, t)
-				}
-			}
+	// only the primary arch in the recovery partition
+	if os == Android && module.InstallInRecovery() {
+		osTargets = []Target{osTargets[0]}
+	}
 
-			osTargets = targets
-		}
+	prefer32 := false
+	if base.prefer32 != nil {
+		prefer32 = base.prefer32(mctx, base, os.Class)
+	}
 
-		// only the primary arch in the recovery partition
-		if os == Android && module.InstallInRecovery() {
-			osTargets = []Target{osTargets[0]}
-		}
+	multilib, extraMultilib := decodeMultilib(base, os.Class)
+	targets, err := decodeMultilibTargets(multilib, osTargets, prefer32)
+	if err != nil {
+		mctx.ModuleErrorf("%s", err.Error())
+	}
 
-		prefer32 := false
-		if base.prefer32 != nil {
-			prefer32 = base.prefer32(mctx, base, os.Class)
-		}
-
-		multilib, extraMultilib := decodeMultilib(base, os.Class)
-		targets, err := decodeMultilibTargets(multilib, osTargets, prefer32)
+	var multiTargets []Target
+	if extraMultilib != "" {
+		multiTargets, err = decodeMultilibTargets(extraMultilib, osTargets, prefer32)
 		if err != nil {
 			mctx.ModuleErrorf("%s", err.Error())
 		}
-
-		var multiTargets []Target
-		if extraMultilib != "" {
-			multiTargets, err = decodeMultilibTargets(extraMultilib, osTargets, prefer32)
-			if err != nil {
-				mctx.ModuleErrorf("%s", err.Error())
-			}
-		}
-
-		if len(targets) > 0 {
-			primaryModules[len(moduleTargets)] = true
-			moduleMultiTargets[len(moduleTargets)] = multiTargets
-			moduleTargets = append(moduleTargets, targets...)
-		}
 	}
 
-	if len(moduleTargets) == 0 {
+	if image == RecoveryVariation {
+		primaryArch := mctx.Config().DevicePrimaryArchType()
+		targets = filterToArch(targets, primaryArch)
+		multiTargets = filterToArch(multiTargets, primaryArch)
+	}
+
+	if len(targets) == 0 {
 		base.commonProperties.Enabled = boolPtr(false)
 		return
 	}
 
-	targetNames := make([]string, len(moduleTargets))
+	targetNames := make([]string, len(targets))
 
-	for i, target := range moduleTargets {
-		targetNames[i] = target.String()
+	for i, target := range targets {
+		targetNames[i] = target.ArchVariation()
 	}
 
 	modules := mctx.CreateVariations(targetNames...)
 	for i, m := range modules {
-		m.(Module).base().SetTarget(moduleTargets[i], moduleMultiTargets[i], primaryModules[i])
+		m.(Module).base().commonProperties.CompileTarget = targets[i]
+		m.(Module).base().commonProperties.CompileMultiTargets = multiTargets
+		if i == 0 {
+			m.(Module).base().commonProperties.CompilePrimary = true
+		}
 		m.(Module).base().setArchProperties(mctx)
 	}
 }
@@ -849,149 +916,21 @@
 	}
 }
 
-func filterArchStructFields(fields []reflect.StructField) (filteredFields []reflect.StructField, filtered bool) {
-	for _, field := range fields {
-		if !proptools.HasTag(field, "android", "arch_variant") {
-			filtered = true
-			continue
+func filterToArch(targets []Target, arch ArchType) []Target {
+	for i := 0; i < len(targets); i++ {
+		if targets[i].Arch.ArchType != arch {
+			targets = append(targets[:i], targets[i+1:]...)
+			i--
 		}
-
-		// The arch_variant field isn't necessary past this point
-		// Instead of wasting space, just remove it. Go also has a
-		// 16-bit limit on structure name length. The name is constructed
-		// based on the Go source representation of the structure, so
-		// the tag names count towards that length.
-		//
-		// TODO: handle the uncommon case of other tags being involved
-		if field.Tag == `android:"arch_variant"` {
-			field.Tag = ""
-		}
-
-		// Recurse into structs
-		switch field.Type.Kind() {
-		case reflect.Struct:
-			var subFiltered bool
-			field.Type, subFiltered = filterArchStruct(field.Type)
-			filtered = filtered || subFiltered
-			if field.Type == nil {
-				continue
-			}
-		case reflect.Ptr:
-			if field.Type.Elem().Kind() == reflect.Struct {
-				nestedType, subFiltered := filterArchStruct(field.Type.Elem())
-				filtered = filtered || subFiltered
-				if nestedType == nil {
-					continue
-				}
-				field.Type = reflect.PtrTo(nestedType)
-			}
-		case reflect.Interface:
-			panic("Interfaces are not supported in arch_variant properties")
-		}
-
-		filteredFields = append(filteredFields, field)
 	}
-
-	return filteredFields, filtered
-}
-
-// filterArchStruct takes a reflect.Type that is either a sturct or a pointer to a struct, and returns a reflect.Type
-// that only contains the fields in the original type that have an `android:"arch_variant"` struct tag, and a bool
-// that is true if the new struct type has fewer fields than the original type.  If there are no fields in the
-// original type with the struct tag it returns nil and true.
-func filterArchStruct(prop reflect.Type) (filteredProp reflect.Type, filtered bool) {
-	var fields []reflect.StructField
-
-	ptr := prop.Kind() == reflect.Ptr
-	if ptr {
-		prop = prop.Elem()
-	}
-
-	for i := 0; i < prop.NumField(); i++ {
-		fields = append(fields, prop.Field(i))
-	}
-
-	filteredFields, filtered := filterArchStructFields(fields)
-
-	if len(filteredFields) == 0 {
-		return nil, true
-	}
-
-	if !filtered {
-		if ptr {
-			return reflect.PtrTo(prop), false
-		}
-		return prop, false
-	}
-
-	ret := reflect.StructOf(filteredFields)
-	if ptr {
-		ret = reflect.PtrTo(ret)
-	}
-
-	return ret, true
-}
-
-// filterArchStruct takes a reflect.Type that is either a sturct or a pointer to a struct, and returns a list of
-// reflect.Type that only contains the fields in the original type that have an `android:"arch_variant"` struct tag,
-// and a bool that is true if the new struct type has fewer fields than the original type.  If there are no fields in
-// the original type with the struct tag it returns nil and true.  Each returned struct type will have a maximum of
-// 10 top level fields in it to attempt to avoid hitting the reflect.StructOf name length limit, although the limit
-// can still be reached with a single struct field with many fields in it.
-func filterArchStructSharded(prop reflect.Type) (filteredProp []reflect.Type, filtered bool) {
-	var fields []reflect.StructField
-
-	ptr := prop.Kind() == reflect.Ptr
-	if ptr {
-		prop = prop.Elem()
-	}
-
-	for i := 0; i < prop.NumField(); i++ {
-		fields = append(fields, prop.Field(i))
-	}
-
-	fields, filtered = filterArchStructFields(fields)
-	if !filtered {
-		if ptr {
-			return []reflect.Type{reflect.PtrTo(prop)}, false
-		}
-		return []reflect.Type{prop}, false
-	}
-
-	if len(fields) == 0 {
-		return nil, true
-	}
-
-	shards := shardFields(fields, 10)
-
-	for _, shard := range shards {
-		s := reflect.StructOf(shard)
-		if ptr {
-			s = reflect.PtrTo(s)
-		}
-		filteredProp = append(filteredProp, s)
-	}
-
-	return filteredProp, true
-}
-
-func shardFields(fields []reflect.StructField, shardSize int) [][]reflect.StructField {
-	ret := make([][]reflect.StructField, 0, (len(fields)+shardSize-1)/shardSize)
-	for len(fields) > shardSize {
-		ret = append(ret, fields[0:shardSize])
-		fields = fields[shardSize:]
-	}
-	if len(fields) > 0 {
-		ret = append(ret, fields)
-	}
-	return ret
+	return targets
 }
 
 // createArchType takes a reflect.Type that is either a struct or a pointer to a struct, and returns a list of
 // reflect.Type that contains the arch-variant properties inside structs for each architecture, os, target, multilib,
 // etc.
 func createArchType(props reflect.Type) []reflect.Type {
-	propShards, _ := filterArchStructSharded(props)
+	propShards, _ := proptools.FilterPropertyStructSharded(props, filterArchStruct)
 	if len(propShards) == 0 {
 		return nil
 	}
@@ -1090,6 +1029,23 @@
 	return ret
 }
 
+func filterArchStruct(field reflect.StructField, prefix string) (bool, reflect.StructField) {
+	if proptools.HasTag(field, "android", "arch_variant") {
+		// The arch_variant field isn't necessary past this point
+		// Instead of wasting space, just remove it. Go also has a
+		// 16-bit limit on structure name length. The name is constructed
+		// based on the Go source representation of the structure, so
+		// the tag names count towards that length.
+		//
+		// TODO: handle the uncommon case of other tags being involved
+		if field.Tag == `android:"arch_variant"` {
+			field.Tag = ""
+		}
+		return true, field
+	}
+	return false, field
+}
+
 var archPropTypeMap OncePer
 
 func InitArchModule(m Module) {
@@ -1166,6 +1122,100 @@
 	return ret
 }
 
+// Rewrite the module's properties structs to contain os-specific values.
+func (m *ModuleBase) setOSProperties(ctx BottomUpMutatorContext) {
+	os := m.commonProperties.CompileOS
+
+	for i := range m.generalProperties {
+		genProps := m.generalProperties[i]
+		if m.archProperties[i] == nil {
+			continue
+		}
+		for _, archProperties := range m.archProperties[i] {
+			archPropValues := reflect.ValueOf(archProperties).Elem()
+
+			targetProp := archPropValues.FieldByName("Target")
+
+			// Handle host-specific properties in the form:
+			// target: {
+			//     host: {
+			//         key: value,
+			//     },
+			// },
+			if os.Class == Host || os.Class == HostCross {
+				field := "Host"
+				prefix := "target.host"
+				m.appendProperties(ctx, genProps, targetProp, field, prefix)
+			}
+
+			// Handle target OS generalities of the form:
+			// target: {
+			//     bionic: {
+			//         key: value,
+			//     },
+			// }
+			if os.Linux() {
+				field := "Linux"
+				prefix := "target.linux"
+				m.appendProperties(ctx, genProps, targetProp, field, prefix)
+			}
+
+			if os.Bionic() {
+				field := "Bionic"
+				prefix := "target.bionic"
+				m.appendProperties(ctx, genProps, targetProp, field, prefix)
+			}
+
+			// Handle target OS properties in the form:
+			// target: {
+			//     linux_glibc: {
+			//         key: value,
+			//     },
+			//     not_windows: {
+			//         key: value,
+			//     },
+			//     android {
+			//         key: value,
+			//     },
+			// },
+			field := os.Field
+			prefix := "target." + os.Name
+			m.appendProperties(ctx, genProps, targetProp, field, prefix)
+
+			if (os.Class == Host || os.Class == HostCross) && os != Windows {
+				field := "Not_windows"
+				prefix := "target.not_windows"
+				m.appendProperties(ctx, genProps, targetProp, field, prefix)
+			}
+
+			// Handle 64-bit device properties in the form:
+			// target {
+			//     android64 {
+			//         key: value,
+			//     },
+			//     android32 {
+			//         key: value,
+			//     },
+			// },
+			// WARNING: this is probably not what you want to use in your blueprints file, it selects
+			// options for all targets on a device that supports 64-bit binaries, not just the targets
+			// that are being compiled for 64-bit.  Its expected use case is binaries like linker and
+			// debuggerd that need to know when they are a 32-bit process running on a 64-bit device
+			if os.Class == Device {
+				if ctx.Config().Android64() {
+					field := "Android64"
+					prefix := "target.android64"
+					m.appendProperties(ctx, genProps, targetProp, field, prefix)
+				} else {
+					field := "Android32"
+					prefix := "target.android32"
+					m.appendProperties(ctx, genProps, targetProp, field, prefix)
+				}
+			}
+		}
+	}
+}
+
 // Rewrite the module's properties structs to contain arch-specific values.
 func (m *ModuleBase) setArchProperties(ctx BottomUpMutatorContext) {
 	arch := m.Arch()
@@ -1183,9 +1233,6 @@
 			multilibProp := archPropValues.FieldByName("Multilib")
 			targetProp := archPropValues.FieldByName("Target")
 
-			var field string
-			var prefix string
-
 			// Handle arch-specific properties in the form:
 			// arch: {
 			//     arm64: {
@@ -1250,68 +1297,32 @@
 				m.appendProperties(ctx, genProps, multilibProp, field, prefix)
 			}
 
-			// Handle host-specific properties in the form:
+			// Handle combined OS-feature and arch specific properties in the form:
 			// target: {
-			//     host: {
-			//         key: value,
-			//     },
-			// },
-			if os.Class == Host || os.Class == HostCross {
-				field = "Host"
-				prefix = "target.host"
-				m.appendProperties(ctx, genProps, targetProp, field, prefix)
-			}
-
-			// Handle target OS generalities of the form:
-			// target: {
-			//     bionic: {
-			//         key: value,
-			//     },
 			//     bionic_x86: {
 			//         key: value,
 			//     },
 			// }
-			if os.Linux() {
-				field = "Linux"
-				prefix = "target.linux"
+			if os.Linux() && arch.ArchType != Common {
+				field := "Linux_" + arch.ArchType.Name
+				prefix := "target.linux_" + arch.ArchType.Name
 				m.appendProperties(ctx, genProps, targetProp, field, prefix)
-
-				if arch.ArchType != Common {
-					field = "Linux_" + arch.ArchType.Name
-					prefix = "target.linux_" + arch.ArchType.Name
-					m.appendProperties(ctx, genProps, targetProp, field, prefix)
-				}
 			}
 
-			if os.Bionic() {
-				field = "Bionic"
-				prefix = "target.bionic"
+			if os.Bionic() && arch.ArchType != Common {
+				field := "Bionic_" + t.Name
+				prefix := "target.bionic_" + t.Name
 				m.appendProperties(ctx, genProps, targetProp, field, prefix)
-
-				if arch.ArchType != Common {
-					field = "Bionic_" + t.Name
-					prefix = "target.bionic_" + t.Name
-					m.appendProperties(ctx, genProps, targetProp, field, prefix)
-				}
 			}
 
-			// Handle target OS properties in the form:
+			// Handle combined OS and arch specific properties in the form:
 			// target: {
-			//     linux_glibc: {
-			//         key: value,
-			//     },
-			//     not_windows: {
-			//         key: value,
-			//     },
 			//     linux_glibc_x86: {
 			//         key: value,
 			//     },
 			//     linux_glibc_arm: {
 			//         key: value,
 			//     },
-			//     android {
-			//         key: value,
-			//     },
 			//     android_arm {
 			//         key: value,
 			//     },
@@ -1319,46 +1330,23 @@
 			//         key: value,
 			//     },
 			// },
-			field = os.Field
-			prefix = "target." + os.Name
-			m.appendProperties(ctx, genProps, targetProp, field, prefix)
-
 			if arch.ArchType != Common {
-				field = os.Field + "_" + t.Name
-				prefix = "target." + os.Name + "_" + t.Name
+				field := os.Field + "_" + t.Name
+				prefix := "target." + os.Name + "_" + t.Name
 				m.appendProperties(ctx, genProps, targetProp, field, prefix)
 			}
 
-			if (os.Class == Host || os.Class == HostCross) && os != Windows {
-				field := "Not_windows"
-				prefix := "target.not_windows"
-				m.appendProperties(ctx, genProps, targetProp, field, prefix)
-			}
-
-			// Handle 64-bit device properties in the form:
+			// Handle arm on x86 properties in the form:
 			// target {
-			//     android64 {
+			//     arm_on_x86 {
 			//         key: value,
 			//     },
-			//     android32 {
+			//     arm_on_x86_64 {
 			//         key: value,
 			//     },
 			// },
-			// WARNING: this is probably not what you want to use in your blueprints file, it selects
-			// options for all targets on a device that supports 64-bit binaries, not just the targets
-			// that are being compiled for 64-bit.  Its expected use case is binaries like linker and
-			// debuggerd that need to know when they are a 32-bit process running on a 64-bit device
+			// TODO(ccross): is this still necessary with native bridge?
 			if os.Class == Device {
-				if ctx.Config().Android64() {
-					field := "Android64"
-					prefix := "target.android64"
-					m.appendProperties(ctx, genProps, targetProp, field, prefix)
-				} else {
-					field := "Android32"
-					prefix := "target.android32"
-					m.appendProperties(ctx, genProps, targetProp, field, prefix)
-				}
-
 				if (arch.ArchType == X86 && (hasArmAbi(arch) ||
 					hasArmAndroidArch(ctx.Config().Targets[Android]))) ||
 					(arch.ArchType == Arm &&
@@ -1403,7 +1391,8 @@
 	var targetErr error
 
 	addTarget := func(os OsType, archName string, archVariant, cpuVariant *string, abi []string,
-		nativeBridgeEnabled NativeBridgeSupport) {
+		nativeBridgeEnabled NativeBridgeSupport, nativeBridgeHostArchName *string,
+		nativeBridgeRelativePath *string) {
 		if targetErr != nil {
 			return
 		}
@@ -1413,12 +1402,21 @@
 			targetErr = err
 			return
 		}
+		nativeBridgeRelativePathStr := String(nativeBridgeRelativePath)
+		nativeBridgeHostArchNameStr := String(nativeBridgeHostArchName)
+
+		// Use guest arch as relative install path by default
+		if nativeBridgeEnabled && nativeBridgeRelativePathStr == "" {
+			nativeBridgeRelativePathStr = arch.ArchType.String()
+		}
 
 		targets[os] = append(targets[os],
 			Target{
-				Os:           os,
-				Arch:         arch,
-				NativeBridge: nativeBridgeEnabled,
+				Os:                       os,
+				Arch:                     arch,
+				NativeBridge:             nativeBridgeEnabled,
+				NativeBridgeHostArchName: nativeBridgeHostArchNameStr,
+				NativeBridgeRelativePath: nativeBridgeRelativePathStr,
 			})
 	}
 
@@ -1426,14 +1424,14 @@
 		return nil, fmt.Errorf("No host primary architecture set")
 	}
 
-	addTarget(BuildOs, *variables.HostArch, nil, nil, nil, NativeBridgeDisabled)
+	addTarget(BuildOs, *variables.HostArch, nil, nil, nil, NativeBridgeDisabled, nil, nil)
 
 	if variables.HostSecondaryArch != nil && *variables.HostSecondaryArch != "" {
-		addTarget(BuildOs, *variables.HostSecondaryArch, nil, nil, nil, NativeBridgeDisabled)
+		addTarget(BuildOs, *variables.HostSecondaryArch, nil, nil, nil, NativeBridgeDisabled, nil, nil)
 	}
 
 	if Bool(config.Host_bionic) {
-		addTarget(LinuxBionic, "x86_64", nil, nil, nil, NativeBridgeDisabled)
+		addTarget(LinuxBionic, "x86_64", nil, nil, nil, NativeBridgeDisabled, nil, nil)
 	}
 
 	if String(variables.CrossHost) != "" {
@@ -1446,10 +1444,10 @@
 			return nil, fmt.Errorf("No cross-host primary architecture set")
 		}
 
-		addTarget(crossHostOs, *variables.CrossHostArch, nil, nil, nil, NativeBridgeDisabled)
+		addTarget(crossHostOs, *variables.CrossHostArch, nil, nil, nil, NativeBridgeDisabled, nil, nil)
 
 		if variables.CrossHostSecondaryArch != nil && *variables.CrossHostSecondaryArch != "" {
-			addTarget(crossHostOs, *variables.CrossHostSecondaryArch, nil, nil, nil, NativeBridgeDisabled)
+			addTarget(crossHostOs, *variables.CrossHostSecondaryArch, nil, nil, nil, NativeBridgeDisabled, nil, nil)
 		}
 	}
 
@@ -1460,23 +1458,19 @@
 		}
 
 		addTarget(target, *variables.DeviceArch, variables.DeviceArchVariant,
-			variables.DeviceCpuVariant, variables.DeviceAbi, NativeBridgeDisabled)
+			variables.DeviceCpuVariant, variables.DeviceAbi, NativeBridgeDisabled, nil, nil)
 
 		if variables.DeviceSecondaryArch != nil && *variables.DeviceSecondaryArch != "" {
 			addTarget(Android, *variables.DeviceSecondaryArch,
 				variables.DeviceSecondaryArchVariant, variables.DeviceSecondaryCpuVariant,
-				variables.DeviceSecondaryAbi, NativeBridgeDisabled)
-
-			deviceArches := targets[Android]
-			if deviceArches[0].Arch.ArchType.Multilib == deviceArches[1].Arch.ArchType.Multilib {
-				deviceArches[1].Arch.Native = false
-			}
+				variables.DeviceSecondaryAbi, NativeBridgeDisabled, nil, nil)
 		}
 
 		if variables.NativeBridgeArch != nil && *variables.NativeBridgeArch != "" {
 			addTarget(Android, *variables.NativeBridgeArch,
 				variables.NativeBridgeArchVariant, variables.NativeBridgeCpuVariant,
-				variables.NativeBridgeAbi, NativeBridgeEnabled)
+				variables.NativeBridgeAbi, NativeBridgeEnabled, variables.DeviceArch,
+				variables.NativeBridgeRelativePath)
 		}
 
 		if variables.DeviceSecondaryArch != nil && *variables.DeviceSecondaryArch != "" &&
@@ -1484,7 +1478,10 @@
 			addTarget(Android, *variables.NativeBridgeSecondaryArch,
 				variables.NativeBridgeSecondaryArchVariant,
 				variables.NativeBridgeSecondaryCpuVariant,
-				variables.NativeBridgeSecondaryAbi, NativeBridgeEnabled)
+				variables.NativeBridgeSecondaryAbi,
+				NativeBridgeEnabled,
+				variables.DeviceSecondaryArch,
+				variables.NativeBridgeSecondaryRelativePath)
 		}
 	}
 
@@ -1605,6 +1602,15 @@
 	}
 }
 
+func getAmlAbisConfig() []archConfig {
+	return []archConfig{
+		{"arm", "armv7-a", "", []string{"armeabi-v7a"}},
+		{"arm64", "armv8-a", "", []string{"arm64-v8a"}},
+		{"x86", "", "", []string{"x86"}},
+		{"x86_64", "", "", []string{"x86_64"}},
+	}
+}
+
 func decodeArchSettings(os OsType, archConfigs []archConfig) ([]Target, error) {
 	var ret []Target
 
@@ -1614,7 +1620,7 @@
 		if err != nil {
 			return nil, err
 		}
-		arch.Native = false
+
 		ret = append(ret, Target{
 			Os:   Android,
 			Arch: arch,
@@ -1643,7 +1649,6 @@
 		ArchVariant: stringPtr(archVariant),
 		CpuVariant:  stringPtr(cpuVariant),
 		Abi:         abi,
-		Native:      true,
 	}
 
 	if a.ArchVariant == a.ArchType.Name || a.ArchVariant == "generic" {
diff --git a/android/arch_test.go b/android/arch_test.go
index 0589e6c..98b0534 100644
--- a/android/arch_test.go
+++ b/android/arch_test.go
@@ -16,7 +16,10 @@
 
 import (
 	"reflect"
+	"runtime"
 	"testing"
+
+	"github.com/google/blueprint/proptools"
 )
 
 type Named struct {
@@ -219,7 +222,7 @@
 
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
-			out, filtered := filterArchStruct(reflect.TypeOf(test.in))
+			out, filtered := proptools.FilterPropertyStruct(reflect.TypeOf(test.in), filterArchStruct)
 			if filtered != test.filtered {
 				t.Errorf("expected filtered %v, got %v", test.filtered, filtered)
 			}
@@ -230,3 +233,135 @@
 		})
 	}
 }
+
+type archTestModule struct {
+	ModuleBase
+	props struct {
+		Deps []string
+	}
+}
+
+func (m *archTestModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+}
+
+func (m *archTestModule) DepsMutator(ctx BottomUpMutatorContext) {
+	ctx.AddDependency(ctx.Module(), nil, m.props.Deps...)
+}
+
+func archTestModuleFactory() Module {
+	m := &archTestModule{}
+	m.AddProperties(&m.props)
+	InitAndroidArchModule(m, HostAndDeviceSupported, MultilibBoth)
+	return m
+}
+
+func TestArchMutator(t *testing.T) {
+	var buildOSVariants []string
+	var buildOS32Variants []string
+	switch runtime.GOOS {
+	case "linux":
+		buildOSVariants = []string{"linux_glibc_x86_64", "linux_glibc_x86"}
+		buildOS32Variants = []string{"linux_glibc_x86"}
+	case "darwin":
+		buildOSVariants = []string{"darwin_x86_64"}
+		buildOS32Variants = nil
+	}
+
+	bp := `
+		module {
+			name: "foo",
+		}
+
+		module {
+			name: "bar",
+			host_supported: true,
+		}
+
+		module {
+			name: "baz",
+			device_supported: false,
+		}
+
+		module {
+			name: "qux",
+			host_supported: true,
+			compile_multilib: "32",
+		}
+	`
+
+	testCases := []struct {
+		name        string
+		config      func(Config)
+		fooVariants []string
+		barVariants []string
+		bazVariants []string
+		quxVariants []string
+	}{
+		{
+			name:        "normal",
+			config:      nil,
+			fooVariants: []string{"android_arm64_armv8-a", "android_arm_armv7-a-neon"},
+			barVariants: append(buildOSVariants, "android_arm64_armv8-a", "android_arm_armv7-a-neon"),
+			bazVariants: nil,
+			quxVariants: append(buildOS32Variants, "android_arm_armv7-a-neon"),
+		},
+		{
+			name: "host-only",
+			config: func(config Config) {
+				config.BuildOSTarget = Target{}
+				config.BuildOSCommonTarget = Target{}
+				config.Targets[Android] = nil
+			},
+			fooVariants: nil,
+			barVariants: buildOSVariants,
+			bazVariants: nil,
+			quxVariants: buildOS32Variants,
+		},
+	}
+
+	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 := TestArchConfig(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 baz variants:\n%q\ngot:\n%q\n", w, g)
+			}
+
+			if g, w := enabledVariants(ctx, "qux"), tt.quxVariants; !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 b0d8b7f..3c49c1a 100644
--- a/android/config.go
+++ b/android/config.go
@@ -25,7 +25,9 @@
 	"strings"
 	"sync"
 
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/bootstrap"
+	"github.com/google/blueprint/pathtools"
 	"github.com/google/blueprint/proptools"
 )
 
@@ -89,9 +91,14 @@
 	ConfigFileName           string
 	ProductVariablesFileName string
 
-	Targets              map[OsType][]Target
-	BuildOsVariant       string
-	BuildOsCommonVariant string
+	Targets             map[OsType][]Target
+	BuildOSTarget       Target // the Target for tools run on the build machine
+	BuildOSCommonTarget Target // the Target for common (java) tools run on the build machine
+	AndroidCommonTarget Target // the Target for common modules for the Android device
+
+	// multilibConflicts for an ArchType is true if there is earlier configured device architecture with the same
+	// multilib value.
+	multilibConflicts map[ArchType]bool
 
 	deviceConfig *deviceConfig
 
@@ -108,10 +115,11 @@
 	captureBuild      bool // true for tests, saves build parameters for each module
 	ignoreEnvironment bool // true for tests, returns empty from all Getenv calls
 
-	targetOpenJDK9 bool // Target 1.9
-
 	stopBefore bootstrap.StopBefore
 
+	fs         pathtools.FileSystem
+	mockBpList string
+
 	OncePer
 }
 
@@ -127,12 +135,12 @@
 }
 
 func loadConfig(config *config) error {
-	err := loadFromConfigFile(&config.FileConfigurableOptions, config.ConfigFileName)
+	err := loadFromConfigFile(&config.FileConfigurableOptions, absolutePath(config.ConfigFileName))
 	if err != nil {
 		return err
 	}
 
-	return loadFromConfigFile(&config.productVariables, config.ProductVariablesFileName)
+	return loadFromConfigFile(&config.productVariables, absolutePath(config.ProductVariablesFileName))
 }
 
 // loads configuration options from a JSON file in the cwd.
@@ -196,8 +204,19 @@
 	return nil
 }
 
+// NullConfig returns a mostly empty Config for use by standalone tools like dexpreopt_gen that
+// use the android package.
+func NullConfig(buildDir string) Config {
+	return Config{
+		config: &config{
+			buildDir: buildDir,
+			fs:       pathtools.OsFs,
+		},
+	}
+}
+
 // TestConfig returns a Config object suitable for using for tests
-func TestConfig(buildDir string, env map[string]string) Config {
+func TestConfig(buildDir string, env map[string]string, bp string, fs map[string][]byte) Config {
 	envCopy := make(map[string]string)
 	for k, v := range env {
 		envCopy[k] = v
@@ -209,13 +228,14 @@
 	config := &config{
 		productVariables: productVariables{
 			DeviceName:                  stringPtr("test_device"),
-			Platform_sdk_version:        intPtr(26),
+			Platform_sdk_version:        intPtr(30),
 			DeviceSystemSdkVersions:     []string{"14", "15"},
-			Platform_systemsdk_versions: []string{"25", "26"},
+			Platform_systemsdk_versions: []string{"29", "30"},
 			AAPTConfig:                  []string{"normal", "large", "xlarge", "hdpi", "xhdpi", "xxhdpi"},
 			AAPTPreferredConfig:         stringPtr("xhdpi"),
 			AAPTCharacteristics:         stringPtr("nosdcard"),
 			AAPTPrebuiltDPI:             []string{"xhdpi", "xxhdpi"},
+			UncompressPrivAppDex:        boolPtr(true),
 		},
 
 		buildDir:     buildDir,
@@ -227,6 +247,8 @@
 	}
 	config.TestProductVariables = &config.productVariables
 
+	config.mockFileSystem(bp, fs)
+
 	if err := config.fromEnv(); err != nil {
 		panic(err)
 	}
@@ -234,30 +256,30 @@
 	return Config{config}
 }
 
-func TestArchConfigNativeBridge(buildDir string, env map[string]string) Config {
-	testConfig := TestArchConfig(buildDir, env)
+func TestArchConfigNativeBridge(buildDir string, env map[string]string, bp string, fs map[string][]byte) Config {
+	testConfig := TestArchConfig(buildDir, env, bp, fs)
 	config := testConfig.config
 
 	config.Targets[Android] = []Target{
-		{Android, Arch{ArchType: X86_64, ArchVariant: "silvermont", Native: true, Abi: []string{"arm64-v8a"}}, NativeBridgeDisabled},
-		{Android, Arch{ArchType: X86, ArchVariant: "silvermont", Native: true, Abi: []string{"armeabi-v7a"}}, NativeBridgeDisabled},
-		{Android, Arch{ArchType: Arm64, ArchVariant: "armv8-a", Native: true, Abi: []string{"arm64-v8a"}}, NativeBridgeEnabled},
-		{Android, Arch{ArchType: Arm, ArchVariant: "armv7-a-neon", Native: true, Abi: []string{"armeabi-v7a"}}, NativeBridgeEnabled},
+		{Android, Arch{ArchType: X86_64, ArchVariant: "silvermont", Abi: []string{"arm64-v8a"}}, NativeBridgeDisabled, "", ""},
+		{Android, Arch{ArchType: X86, ArchVariant: "silvermont", Abi: []string{"armeabi-v7a"}}, NativeBridgeDisabled, "", ""},
+		{Android, Arch{ArchType: Arm64, ArchVariant: "armv8-a", Abi: []string{"arm64-v8a"}}, NativeBridgeEnabled, "x86_64", "arm64"},
+		{Android, Arch{ArchType: Arm, ArchVariant: "armv7-a-neon", Abi: []string{"armeabi-v7a"}}, NativeBridgeEnabled, "x86", "arm"},
 	}
 
 	return testConfig
 }
 
-func TestArchConfigFuchsia(buildDir string, env map[string]string) Config {
-	testConfig := TestConfig(buildDir, env)
+func TestArchConfigFuchsia(buildDir string, env map[string]string, bp string, fs map[string][]byte) Config {
+	testConfig := TestConfig(buildDir, env, bp, fs)
 	config := testConfig.config
 
 	config.Targets = map[OsType][]Target{
 		Fuchsia: []Target{
-			{Fuchsia, Arch{ArchType: Arm64, ArchVariant: "", Native: true}, NativeBridgeDisabled},
+			{Fuchsia, Arch{ArchType: Arm64, ArchVariant: ""}, NativeBridgeDisabled, "", ""},
 		},
 		BuildOs: []Target{
-			{BuildOs, Arch{ArchType: X86_64}, NativeBridgeDisabled},
+			{BuildOs, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", ""},
 		},
 	}
 
@@ -265,18 +287,18 @@
 }
 
 // TestConfig returns a Config object suitable for using for tests that need to run the arch mutator
-func TestArchConfig(buildDir string, env map[string]string) Config {
-	testConfig := TestConfig(buildDir, env)
+func TestArchConfig(buildDir string, env map[string]string, bp string, fs map[string][]byte) Config {
+	testConfig := TestConfig(buildDir, env, bp, fs)
 	config := testConfig.config
 
 	config.Targets = map[OsType][]Target{
 		Android: []Target{
-			{Android, Arch{ArchType: Arm64, ArchVariant: "armv8-a", Native: true, Abi: []string{"arm64-v8a"}}, NativeBridgeDisabled},
-			{Android, Arch{ArchType: Arm, ArchVariant: "armv7-a-neon", Native: true, Abi: []string{"armeabi-v7a"}}, NativeBridgeDisabled},
+			{Android, Arch{ArchType: Arm64, ArchVariant: "armv8-a", Abi: []string{"arm64-v8a"}}, NativeBridgeDisabled, "", ""},
+			{Android, Arch{ArchType: Arm, ArchVariant: "armv7-a-neon", Abi: []string{"armeabi-v7a"}}, NativeBridgeDisabled, "", ""},
 		},
 		BuildOs: []Target{
-			{BuildOs, Arch{ArchType: X86_64}, NativeBridgeDisabled},
-			{BuildOs, Arch{ArchType: X86}, NativeBridgeDisabled},
+			{BuildOs, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", ""},
+			{BuildOs, Arch{ArchType: X86}, NativeBridgeDisabled, "", ""},
 		},
 	}
 
@@ -284,8 +306,9 @@
 		config.Targets[BuildOs] = config.Targets[BuildOs][:1]
 	}
 
-	config.BuildOsVariant = config.Targets[BuildOs][0].String()
-	config.BuildOsCommonVariant = getCommonTargets(config.Targets[BuildOs])[0].String()
+	config.BuildOSTarget = config.Targets[BuildOs][0]
+	config.BuildOSCommonTarget = getCommonTargets(config.Targets[BuildOs])[0]
+	config.AndroidCommonTarget = getCommonTargets(config.Targets[Android])[0]
 	config.TestProductVariables.DeviceArch = proptools.StringPtr("arm64")
 	config.TestProductVariables.DeviceArchVariant = proptools.StringPtr("armv8-a")
 	config.TestProductVariables.DeviceSecondaryArch = proptools.StringPtr("arm")
@@ -304,8 +327,11 @@
 
 		env: originalEnv,
 
-		srcDir:   srcDir,
-		buildDir: buildDir,
+		srcDir:            srcDir,
+		buildDir:          buildDir,
+		multilibConflicts: make(map[ArchType]bool),
+
+		fs: pathtools.NewOsFs(absSrcDir),
 	}
 
 	config.deviceConfig = &deviceConfig{
@@ -335,7 +361,7 @@
 	}
 
 	inMakeFile := filepath.Join(buildDir, ".soong.in_make")
-	if _, err := os.Stat(inMakeFile); err == nil {
+	if _, err := os.Stat(absolutePath(inMakeFile)); err == nil {
 		config.inMake = true
 	}
 
@@ -349,6 +375,8 @@
 		archConfig = getMegaDeviceConfig()
 	} else if config.NdkAbis() {
 		archConfig = getNdkAbisConfig()
+	} else if config.AmlAbis() {
+		archConfig = getAmlAbisConfig()
 	}
 
 	if archConfig != nil {
@@ -359,9 +387,20 @@
 		targets[Android] = androidTargets
 	}
 
+	multilib := make(map[string]bool)
+	for _, target := range targets[Android] {
+		if seen := multilib[target.Arch.ArchType.Multilib]; seen {
+			config.multilibConflicts[target.Arch.ArchType] = true
+		}
+		multilib[target.Arch.ArchType.Multilib] = true
+	}
+
 	config.Targets = targets
-	config.BuildOsVariant = targets[BuildOs][0].String()
-	config.BuildOsCommonVariant = getCommonTargets(targets[BuildOs])[0].String()
+	config.BuildOSTarget = config.Targets[BuildOs][0]
+	config.BuildOSCommonTarget = getCommonTargets(config.Targets[BuildOs])[0]
+	if len(config.Targets[Android]) > 0 {
+		config.AndroidCommonTarget = getCommonTargets(config.Targets[Android])[0]
+	}
 
 	if err := config.fromEnv(); err != nil {
 		return Config{}, err
@@ -370,15 +409,44 @@
 	return Config{config}, nil
 }
 
+var TestConfigOsFs = map[string][]byte{}
+
+// mockFileSystem replaces all reads with accesses to the provided map of
+// filenames to contents stored as a byte slice.
+func (c *config) mockFileSystem(bp string, fs map[string][]byte) {
+	mockFS := map[string][]byte{}
+
+	if _, exists := mockFS["Android.bp"]; !exists {
+		mockFS["Android.bp"] = []byte(bp)
+	}
+
+	for k, v := range fs {
+		mockFS[k] = v
+	}
+
+	// no module list file specified; find every file named Blueprints or Android.bp
+	pathsToParse := []string{}
+	for candidate := range mockFS {
+		base := filepath.Base(candidate)
+		if base == "Blueprints" || base == "Android.bp" {
+			pathsToParse = append(pathsToParse, candidate)
+		}
+	}
+	if len(pathsToParse) < 1 {
+		panic(fmt.Sprintf("No Blueprint or Android.bp files found in mock filesystem: %v\n", mockFS))
+	}
+	mockFS[blueprint.MockModuleListFile] = []byte(strings.Join(pathsToParse, "\n"))
+
+	c.fs = pathtools.MockFs(mockFS)
+	c.mockBpList = blueprint.MockModuleListFile
+}
+
 func (c *config) fromEnv() error {
 	switch c.Getenv("EXPERIMENTAL_JAVA_LANGUAGE_LEVEL_9") {
-	case "":
-		// Nothing, this is the default
-	case "true":
-		// Use -source 9 -target 9
-		c.targetOpenJDK9 = true
+	case "", "true":
+		// Do nothing
 	default:
-		return fmt.Errorf(`Invalid value for EXPERIMENTAL_JAVA_LANGUAGE_LEVEL_9, should be "" or "true"`)
+		return fmt.Errorf("The environment variable EXPERIMENTAL_JAVA_LANGUAGE_LEVEL_9 is no longer supported. Java language level 9 is now the global default.")
 	}
 
 	return nil
@@ -404,6 +472,18 @@
 	return PathForOutput(ctx, "host", c.PrebuiltOS(), "bin", tool)
 }
 
+func (c *config) HostJNIToolPath(ctx PathContext, path string) Path {
+	ext := ".so"
+	if runtime.GOOS == "darwin" {
+		ext = ".dylib"
+	}
+	return PathForOutput(ctx, "host", c.PrebuiltOS(), "lib64", path+ext)
+}
+
+func (c *config) HostJavaToolPath(ctx PathContext, path string) Path {
+	return PathForOutput(ctx, "host", c.PrebuiltOS(), "framework", path)
+}
+
 // HostSystemTool looks for non-hermetic tools from the system we're running on.
 // Generally shouldn't be used, but useful to find the XCode SDK, etc.
 func (c *config) HostSystemTool(name string) string {
@@ -688,10 +768,6 @@
 	return c.Targets[Android][0].Arch.ArchType
 }
 
-func (c *config) SkipDeviceInstall() bool {
-	return c.EmbeddedInMake()
-}
-
 func (c *config) SkipMegaDeviceInstall(path string) bool {
 	return Bool(c.Mega_device) &&
 		strings.HasPrefix(path, filepath.Join(c.buildDir, "target", "product"))
@@ -747,13 +823,20 @@
 	return Bool(c.productVariables.UseGoma)
 }
 
+func (c *config) UseRBE() bool {
+	return Bool(c.productVariables.UseRBE)
+}
+
 func (c *config) RunErrorProne() bool {
 	return c.IsEnvTrue("RUN_ERROR_PRONE")
 }
 
-// Returns true if -source 1.9 -target 1.9 is being passed to javac
-func (c *config) TargetOpenJDK9() bool {
-	return c.targetOpenJDK9
+func (c *config) XrefCorpusName() string {
+	return c.Getenv("XREF_CORPUS")
+}
+
+func (c *config) EmitXrefRules() bool {
+	return c.XrefCorpusName() != ""
 }
 
 func (c *config) ClangTidy() bool {
@@ -791,7 +874,7 @@
 func (c *config) EnforceRROForModule(name string) bool {
 	enforceList := c.productVariables.EnforceRROTargets
 	if enforceList != nil {
-		if len(enforceList) == 1 && (enforceList)[0] == "*" {
+		if InList("*", enforceList) {
 			return true
 		}
 		return InList(name, enforceList)
@@ -831,14 +914,27 @@
 	return c.productVariables.BootJars
 }
 
-func (c *config) DexpreoptGlobalConfig() string {
-	return String(c.productVariables.DexpreoptGlobalConfig)
+func (c *config) DexpreoptGlobalConfig(ctx PathContext) ([]byte, error) {
+	if c.productVariables.DexpreoptGlobalConfig == nil {
+		return nil, nil
+	}
+	path := absolutePath(*c.productVariables.DexpreoptGlobalConfig)
+	ctx.AddNinjaFileDeps(path)
+	return ioutil.ReadFile(path)
 }
 
 func (c *config) FrameworksBaseDirExists(ctx PathContext) bool {
 	return ExistentPathForSource(ctx, "frameworks", "base").Valid()
 }
 
+func (c *config) VndkSnapshotBuildArtifacts() bool {
+	return Bool(c.productVariables.VndkSnapshotBuildArtifacts)
+}
+
+func (c *config) HasMultilibConflict(arch ArchType) bool {
+	return c.multilibConflicts[arch]
+}
+
 func (c *deviceConfig) Arches() []Arch {
 	var arches []Arch
 	for _, target := range c.config.Targets[Android] {
@@ -870,6 +966,10 @@
 	return String(c.config.productVariables.Platform_vndk_version)
 }
 
+func (c *deviceConfig) ProductVndkVersion() string {
+	return String(c.config.productVariables.ProductVndkVersion)
+}
+
 func (c *deviceConfig) ExtraVndkVersions() []string {
 	return c.config.productVariables.ExtraVndkVersions
 }
@@ -900,11 +1000,11 @@
 	return "product"
 }
 
-func (c *deviceConfig) ProductServicesPath() string {
-	if c.config.productVariables.ProductServicesPath != nil {
-		return *c.config.productVariables.ProductServicesPath
+func (c *deviceConfig) SystemExtPath() string {
+	if c.config.productVariables.SystemExtPath != nil {
+		return *c.config.productVariables.SystemExtPath
 	}
-	return "product_services"
+	return "system_ext"
 }
 
 func (c *deviceConfig) BtConfigIncludeDir() string {
@@ -996,19 +1096,6 @@
 	return "", false
 }
 
-// SecondArchIsTranslated returns true if the primary device arch is X86 or X86_64 and the device also has an arch
-// that is Arm or Arm64.
-func (c *config) SecondArchIsTranslated() bool {
-	deviceTargets := c.Targets[Android]
-	if len(deviceTargets) < 2 {
-		return false
-	}
-
-	arch := deviceTargets[0].Arch
-
-	return (arch.ArchType == X86 || arch.ArchType == X86_64) && hasArmAndroidArch(deviceTargets)
-}
-
 func (c *config) IntegerOverflowDisabledForPath(path string) bool {
 	if c.productVariables.IntegerOverflowExcludePaths == nil {
 		return false
@@ -1059,12 +1146,16 @@
 	return Bool(c.productVariables.Ndk_abis)
 }
 
+func (c *config) AmlAbis() bool {
+	return Bool(c.productVariables.Aml_abis)
+}
+
 func (c *config) ExcludeDraftNdkApis() bool {
 	return Bool(c.productVariables.Exclude_draft_ndk_apis)
 }
 
 func (c *config) FlattenApex() bool {
-	return Bool(c.productVariables.FlattenApex)
+	return Bool(c.productVariables.Flatten_apex)
 }
 
 func (c *config) EnforceSystemCertificate() bool {
@@ -1075,6 +1166,14 @@
 	return c.productVariables.EnforceSystemCertificateWhitelist
 }
 
+func (c *config) EnforceProductPartitionInterface() bool {
+	return Bool(c.productVariables.EnforceProductPartitionInterface)
+}
+
+func (c *config) InstallExtraFlattenedApexes() bool {
+	return Bool(c.productVariables.InstallExtraFlattenedApexes)
+}
+
 func (c *config) ProductHiddenAPIStubs() []string {
 	return c.productVariables.ProductHiddenAPIStubs
 }
diff --git a/android/csuite_config.go b/android/csuite_config.go
new file mode 100644
index 0000000..15c518a
--- /dev/null
+++ b/android/csuite_config.go
@@ -0,0 +1,70 @@
+// 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"
+	"io"
+)
+
+func init() {
+	RegisterModuleType("csuite_config", CSuiteConfigFactory)
+}
+
+type csuiteConfigProperties struct {
+	// Override the default (AndroidTest.xml) test manifest file name.
+	Test_config *string
+}
+
+type CSuiteConfig struct {
+	ModuleBase
+	properties     csuiteConfigProperties
+	OutputFilePath OutputPath
+}
+
+func (me *CSuiteConfig) GenerateAndroidBuildActions(ctx ModuleContext) {
+	me.OutputFilePath = PathForModuleOut(ctx, me.BaseModuleName()).OutputPath
+}
+
+func (me *CSuiteConfig) AndroidMk() AndroidMkData {
+	androidMkData := AndroidMkData{
+		Class:      "FAKE",
+		Include:    "$(BUILD_SYSTEM)/suite_host_config.mk",
+		OutputFile: OptionalPathForPath(me.OutputFilePath),
+	}
+	androidMkData.Extra = []AndroidMkExtraFunc{
+		func(w io.Writer, outputFile Path) {
+			if me.properties.Test_config != nil {
+				fmt.Fprintf(w, "LOCAL_TEST_CONFIG := %s\n",
+					*me.properties.Test_config)
+			}
+			fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE := csuite")
+		},
+	}
+	return androidMkData
+}
+
+func InitCSuiteConfigModule(me *CSuiteConfig) {
+	me.AddProperties(&me.properties)
+}
+
+// csuite_config generates an App Compatibility Test Suite (C-Suite) configuration file from the
+// <test_config> xml file and stores it in a subdirectory of $(HOST_OUT).
+func CSuiteConfigFactory() Module {
+	module := &CSuiteConfig{}
+	InitCSuiteConfigModule(module)
+	InitAndroidArchModule(module, HostSupported, MultilibFirst)
+	return module
+}
diff --git a/android/csuite_config_test.go b/android/csuite_config_test.go
new file mode 100644
index 0000000..bf1a19a
--- /dev/null
+++ b/android/csuite_config_test.go
@@ -0,0 +1,49 @@
+// 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 (
+	"testing"
+)
+
+func testCSuiteConfig(test *testing.T, bpFileContents string) *TestContext {
+	config := TestArchConfig(buildDir, nil, bpFileContents, nil)
+
+	ctx := NewTestArchContext()
+	ctx.RegisterModuleType("csuite_config", CSuiteConfigFactory)
+	ctx.Register(config)
+	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+	FailIfErrored(test, errs)
+	_, errs = ctx.PrepareBuildActions(config)
+	FailIfErrored(test, errs)
+	return ctx
+}
+
+func TestCSuiteConfig(t *testing.T) {
+	ctx := testCSuiteConfig(t, `
+csuite_config { name: "plain"}
+csuite_config { name: "with_manifest", test_config: "manifest.xml" }
+`)
+
+	variants := ctx.ModuleVariantsForTests("plain")
+	if len(variants) > 1 {
+		t.Errorf("expected 1, got %d", len(variants))
+	}
+	expectedOutputFilename := ctx.ModuleForTests(
+		"plain", variants[0]).Module().(*CSuiteConfig).OutputFilePath.Base()
+	if expectedOutputFilename != "plain" {
+		t.Errorf("expected plain, got %q", expectedOutputFilename)
+	}
+}
diff --git a/android/defaults.go b/android/defaults.go
index 844b4d4..7597446 100644
--- a/android/defaults.go
+++ b/android/defaults.go
@@ -42,9 +42,16 @@
 	d.defaultableProperties = props
 }
 
+// Interface that must be supported by any module to which defaults can be applied.
 type Defaultable interface {
+	// Get a pointer to the struct containing the Defaults property.
 	defaults() *defaultsProperties
+
+	// Set the property structures into which defaults will be added.
 	setProperties([]interface{})
+
+	// Apply defaults from the supplied Defaults to the property structures supplied to
+	// setProperties(...).
 	applyDefaults(TopDownMutatorContext, []Defaults)
 }
 
@@ -56,45 +63,119 @@
 var _ Defaultable = (*DefaultableModuleBase)(nil)
 
 func InitDefaultableModule(module DefaultableModule) {
-	module.(Defaultable).setProperties(module.(Module).GetProperties())
+	module.setProperties(module.(Module).GetProperties())
 
 	module.AddProperties(module.defaults())
 }
 
-type DefaultsModuleBase struct {
-	DefaultableModuleBase
-	defaultProperties []interface{}
+// The Defaults_visibility property.
+type DefaultsVisibilityProperties struct {
+
+	// Controls the visibility of the defaults module itself.
+	Defaults_visibility []string
 }
 
+type DefaultsModuleBase struct {
+	DefaultableModuleBase
+
+	// Container for defaults of the common properties
+	commonProperties commonProperties
+
+	defaultsVisibilityProperties DefaultsVisibilityProperties
+}
+
+// The common pattern for defaults modules is to register separate instances of
+// the xxxProperties structs in the AddProperties calls, rather than reusing the
+// ones inherited from Module.
+//
+// The effect is that e.g. myDefaultsModuleInstance.base().xxxProperties won't
+// contain the values that have been set for the defaults module. Rather, to
+// retrieve the values it is necessary to iterate over properties(). E.g. to get
+// the commonProperties instance that have the real values:
+//
+//   d := myModule.(Defaults)
+//   for _, props := range d.properties() {
+//     if cp, ok := props.(*commonProperties); ok {
+//       ... access property values in cp ...
+//     }
+//   }
+//
+// The rationale is that the properties on a defaults module apply to the
+// defaultable modules using it, not to the defaults module itself. E.g. setting
+// the "enabled" property false makes inheriting modules disabled by default,
+// rather than disabling the defaults module itself.
 type Defaults interface {
 	Defaultable
+
+	// Although this function is unused it is actually needed to ensure that only modules that embed
+	// DefaultsModuleBase will type-assert to the Defaults interface.
 	isDefaults() bool
+
+	// Get the structures containing the properties for which defaults can be provided.
 	properties() []interface{}
+
+	// Return the defaults common properties.
+	common() *commonProperties
+
+	// Return the defaults visibility properties.
+	defaultsVisibility() *DefaultsVisibilityProperties
 }
 
 func (d *DefaultsModuleBase) isDefaults() bool {
 	return true
 }
 
+type DefaultsModule interface {
+	Module
+	Defaults
+}
+
 func (d *DefaultsModuleBase) properties() []interface{} {
 	return d.defaultableProperties
 }
 
+func (d *DefaultsModuleBase) common() *commonProperties {
+	return &d.commonProperties
+}
+
+func (d *DefaultsModuleBase) defaultsVisibility() *DefaultsVisibilityProperties {
+	return &d.defaultsVisibilityProperties
+}
+
 func (d *DefaultsModuleBase) GenerateAndroidBuildActions(ctx ModuleContext) {
 }
 
-func InitDefaultsModule(module DefaultableModule) {
+func InitDefaultsModule(module DefaultsModule) {
+	commonProperties := module.common()
+
 	module.AddProperties(
 		&hostAndDeviceProperties{},
-		&commonProperties{},
-		&variableProperties{})
+		commonProperties,
+		&variableProperties{},
+		&ApexProperties{})
 
 	InitArchModule(module)
 	InitDefaultableModule(module)
 
-	module.AddProperties(&module.base().nameProperties)
+	// Add properties that will not have defaults applied to them.
+	base := module.base()
+	defaultsVisibility := module.defaultsVisibility()
+	module.AddProperties(&base.nameProperties, defaultsVisibility)
 
-	module.base().module = module
+	// The defaults_visibility property controls the visibility of a defaults module.
+	base.primaryVisibilityProperty =
+		newVisibilityProperty("defaults_visibility", &defaultsVisibility.Defaults_visibility)
+
+	// Unlike non-defaults modules the visibility property is not stored in m.base().commonProperties.
+	// Instead it is stored in a separate instance of commonProperties created above so use that.
+	// The visibility property needs to be checked (but not parsed) by the visibility module during
+	// its checking phase and parsing phase.
+	base.visibilityPropertyInfo = []visibilityProperty{
+		base.primaryVisibilityProperty,
+		newVisibilityProperty("visibility", &commonProperties.Visibility),
+	}
+
+	base.module = module
 }
 
 var _ Defaults = (*DefaultsModuleBase)(nil)
diff --git a/android/defaults_test.go b/android/defaults_test.go
index fa26595..ba607ef 100644
--- a/android/defaults_test.go
+++ b/android/defaults_test.go
@@ -58,19 +58,6 @@
 }
 
 func TestDefaultsAllowMissingDependencies(t *testing.T) {
-	config := TestConfig(buildDir, nil)
-	config.TestProductVariables.Allow_missing_dependencies = proptools.BoolPtr(true)
-
-	ctx := NewTestContext()
-	ctx.SetAllowMissingDependencies(true)
-
-	ctx.RegisterModuleType("test", ModuleFactoryAdaptor(defaultsTestModuleFactory))
-	ctx.RegisterModuleType("defaults", ModuleFactoryAdaptor(defaultsTestDefaultsFactory))
-
-	ctx.PreArchMutators(RegisterDefaultsPreArchMutators)
-
-	ctx.Register()
-
 	bp := `
 		defaults {
 			name: "defaults",
@@ -91,9 +78,18 @@
 		}
 	`
 
-	ctx.MockFileSystem(map[string][]byte{
-		"Android.bp": []byte(bp),
-	})
+	config := TestConfig(buildDir, nil, bp, nil)
+	config.TestProductVariables.Allow_missing_dependencies = proptools.BoolPtr(true)
+
+	ctx := NewTestContext()
+	ctx.SetAllowMissingDependencies(true)
+
+	ctx.RegisterModuleType("test", defaultsTestModuleFactory)
+	ctx.RegisterModuleType("defaults", defaultsTestDefaultsFactory)
+
+	ctx.PreArchMutators(RegisterDefaultsPreArchMutators)
+
+	ctx.Register(config)
 
 	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
 	FailIfErrored(t, errs)
diff --git a/android/env.go b/android/env.go
index 469dfff..46bd3d6 100644
--- a/android/env.go
+++ b/android/env.go
@@ -16,6 +16,7 @@
 
 import (
 	"os"
+	"os/exec"
 	"strings"
 
 	"android/soong/env"
@@ -29,8 +30,16 @@
 // a manifest regeneration.
 
 var originalEnv map[string]string
+var SoongDelveListen string
+var SoongDelvePath string
 
 func init() {
+	// Delve support needs to read this environment variable very early, before NewConfig has created a way to
+	// access originalEnv with dependencies.  Store the value where soong_build can find it, it will manually
+	// ensure the dependencies are created.
+	SoongDelveListen = os.Getenv("SOONG_DELVE")
+	SoongDelvePath, _ = exec.LookPath("dlv")
+
 	originalEnv = make(map[string]string)
 	for _, env := range os.Environ() {
 		idx := strings.IndexRune(env, '=')
@@ -38,9 +47,22 @@
 			originalEnv[env[:idx]] = env[idx+1:]
 		}
 	}
+	// Clear the environment to prevent use of os.Getenv(), which would not provide dependencies on environment
+	// variable values.  The environment is available through ctx.Config().Getenv, ctx.Config().IsEnvTrue, etc.
 	os.Clearenv()
 }
 
+// getenv checks either os.Getenv or originalEnv so that it works before or after the init()
+// function above.  It doesn't add any dependencies on the environment variable, so it should
+// only be used for values that won't change.  For values that might change use ctx.Config().Getenv.
+func getenv(key string) string {
+	if originalEnv == nil {
+		return os.Getenv(key)
+	} else {
+		return originalEnv[key]
+	}
+}
+
 func EnvSingleton() Singleton {
 	return &envSingleton{}
 }
@@ -55,7 +77,12 @@
 		return
 	}
 
-	err := env.WriteEnvFile(envFile.String(), envDeps)
+	data, err := env.EnvFileContents(envDeps)
+	if err != nil {
+		ctx.Errorf(err.Error())
+	}
+
+	err = WriteFileToOutputDir(envFile, data, 0666)
 	if err != nil {
 		ctx.Errorf(err.Error())
 	}
diff --git a/android/expand.go b/android/expand.go
index 527c4ac..67fb4ee 100644
--- a/android/expand.go
+++ b/android/expand.go
@@ -18,12 +18,30 @@
 	"fmt"
 	"strings"
 	"unicode"
+
+	"github.com/google/blueprint/proptools"
 )
 
+// ExpandNinjaEscaped substitutes $() variables in a string
+// $(var) is passed to mapping(var), which should return the expanded value, a bool for whether the result should
+// be left unescaped when using in a ninja value (generally false, true if the expanded value is a ninja variable like
+// '${in}'), and an error.
+// $$ is converted to $, which is escaped back to $$.
+func ExpandNinjaEscaped(s string, mapping func(string) (string, bool, error)) (string, error) {
+	return expand(s, true, mapping)
+}
+
 // Expand substitutes $() variables in a string
-// $(var) is passed to Expander(var)
-// $$ is converted to $
+// $(var) is passed to mapping(var), which should return the expanded value and an error.
+// $$ is converted to $.
 func Expand(s string, mapping func(string) (string, error)) (string, error) {
+	return expand(s, false, func(s string) (string, bool, error) {
+		s, err := mapping(s)
+		return s, false, err
+	})
+}
+
+func expand(s string, ninjaEscape bool, mapping func(string) (string, bool, error)) (string, error) {
 	// based on os.Expand
 	buf := make([]byte, 0, 2*len(s))
 	i := 0
@@ -33,10 +51,13 @@
 				return "", fmt.Errorf("expected character after '$'")
 			}
 			buf = append(buf, s[i:j]...)
-			value, w, err := getMapping(s[j+1:], mapping)
+			value, ninjaVariable, w, err := getMapping(s[j+1:], mapping)
 			if err != nil {
 				return "", err
 			}
+			if !ninjaVariable && ninjaEscape {
+				value = proptools.NinjaEscape(value)
+			}
 			buf = append(buf, value...)
 			j += w
 			i = j + 1
@@ -45,26 +66,26 @@
 	return string(buf) + s[i:], nil
 }
 
-func getMapping(s string, mapping func(string) (string, error)) (string, int, error) {
+func getMapping(s string, mapping func(string) (string, bool, error)) (string, bool, int, error) {
 	switch s[0] {
 	case '(':
 		// Scan to closing brace
 		for i := 1; i < len(s); i++ {
 			if s[i] == ')' {
-				ret, err := mapping(strings.TrimSpace(s[1:i]))
-				return ret, i + 1, err
+				ret, ninjaVariable, err := mapping(strings.TrimSpace(s[1:i]))
+				return ret, ninjaVariable, i + 1, err
 			}
 		}
-		return "", len(s), fmt.Errorf("missing )")
+		return "", false, len(s), fmt.Errorf("missing )")
 	case '$':
-		return "$$", 1, nil
+		return "$", false, 1, nil
 	default:
 		i := strings.IndexFunc(s, unicode.IsSpace)
 		if i == 0 {
-			return "", 0, fmt.Errorf("unexpected character '%c' after '$'", s[0])
+			return "", false, 0, fmt.Errorf("unexpected character '%c' after '$'", s[0])
 		} else if i == -1 {
 			i = len(s)
 		}
-		return "", 0, fmt.Errorf("expected '(' after '$', did you mean $(%s)?", s[:i])
+		return "", false, 0, fmt.Errorf("expected '(' after '$', did you mean $(%s)?", s[:i])
 	}
 }
diff --git a/android/expand_test.go b/android/expand_test.go
index 128de8a..12179ed 100644
--- a/android/expand_test.go
+++ b/android/expand_test.go
@@ -20,88 +20,111 @@
 )
 
 var vars = map[string]string{
-	"var1": "abc",
-	"var2": "",
-	"var3": "def",
-	"💩":    "😃",
+	"var1":   "abc",
+	"var2":   "",
+	"var3":   "def",
+	"💩":      "😃",
+	"escape": "${in}",
 }
 
-func expander(s string) (string, error) {
+func expander(s string) (string, bool, error) {
 	if val, ok := vars[s]; ok {
-		return val, nil
+		return val, s == "escape", nil
 	} else {
-		return "", fmt.Errorf("unknown variable %q", s)
+		return "", false, fmt.Errorf("unknown variable %q", s)
 	}
 }
 
 var expandTestCases = []struct {
-	in  string
-	out string
-	err bool
+	in          string
+	out         string
+	out_escaped string
+	err         bool
 }{
 	{
-		in:  "$(var1)",
-		out: "abc",
+		in:          "$(var1)",
+		out:         "abc",
+		out_escaped: "abc",
 	},
 	{
-		in:  "$( var1 )",
-		out: "abc",
+		in:          "$( var1 )",
+		out:         "abc",
+		out_escaped: "abc",
 	},
 	{
-		in:  "def$(var1)",
-		out: "defabc",
+		in:          "def$(var1)",
+		out:         "defabc",
+		out_escaped: "defabc",
 	},
 	{
-		in:  "$(var1)def",
-		out: "abcdef",
+		in:          "$(var1)def",
+		out:         "abcdef",
+		out_escaped: "abcdef",
 	},
 	{
-		in:  "def$(var1)def",
-		out: "defabcdef",
+		in:          "def$(var1)def",
+		out:         "defabcdef",
+		out_escaped: "defabcdef",
 	},
 	{
-		in:  "$(var2)",
-		out: "",
+		in:          "$(var2)",
+		out:         "",
+		out_escaped: "",
 	},
 	{
-		in:  "def$(var2)",
-		out: "def",
+		in:          "def$(var2)",
+		out:         "def",
+		out_escaped: "def",
 	},
 	{
-		in:  "$(var2)def",
-		out: "def",
+		in:          "$(var2)def",
+		out:         "def",
+		out_escaped: "def",
 	},
 	{
-		in:  "def$(var2)def",
-		out: "defdef",
+		in:          "def$(var2)def",
+		out:         "defdef",
+		out_escaped: "defdef",
 	},
 	{
-		in:  "$(var1)$(var3)",
-		out: "abcdef",
+		in:          "$(var1)$(var3)",
+		out:         "abcdef",
+		out_escaped: "abcdef",
 	},
 	{
-		in:  "$(var1)g$(var3)",
-		out: "abcgdef",
+		in:          "$(var1)g$(var3)",
+		out:         "abcgdef",
+		out_escaped: "abcgdef",
 	},
 	{
-		in:  "$$",
-		out: "$$",
+		in:          "$$",
+		out:         "$",
+		out_escaped: "$$",
 	},
 	{
-		in:  "$$(var1)",
-		out: "$$(var1)",
+		in:          "$$(var1)",
+		out:         "$(var1)",
+		out_escaped: "$$(var1)",
 	},
 	{
-		in:  "$$$(var1)",
-		out: "$$abc",
+		in:          "$$$(var1)",
+		out:         "$abc",
+		out_escaped: "$$abc",
 	},
 	{
-		in:  "$(var1)$$",
-		out: "abc$$",
+		in:          "$(var1)$$",
+		out:         "abc$",
+		out_escaped: "abc$$",
 	},
 	{
-		in:  "$(💩)",
-		out: "😃",
+		in:          "$(💩)",
+		out:         "😃",
+		out_escaped: "😃",
+	},
+	{
+		in:          "$$a$(escape)$$b",
+		out:         "$a${in}$b",
+		out_escaped: "$$a${in}$$b",
 	},
 
 	// Errors
@@ -141,7 +164,10 @@
 
 func TestExpand(t *testing.T) {
 	for _, test := range expandTestCases {
-		got, err := Expand(test.in, expander)
+		got, err := Expand(test.in, func(s string) (string, error) {
+			s, _, err := expander(s)
+			return s, err
+		})
 		if err != nil && !test.err {
 			t.Errorf("%q: unexpected error %s", test.in, err.Error())
 		} else if err == nil && test.err {
@@ -151,3 +177,16 @@
 		}
 	}
 }
+
+func TestExpandNinjaEscaped(t *testing.T) {
+	for _, test := range expandTestCases {
+		got, err := ExpandNinjaEscaped(test.in, expander)
+		if err != nil && !test.err {
+			t.Errorf("%q: unexpected error %s", test.in, err.Error())
+		} else if err == nil && test.err {
+			t.Errorf("%q: expected error, got %q", test.in, got)
+		} else if !test.err && got != test.out_escaped {
+			t.Errorf("%q: expected %q, got %q", test.in, test.out, got)
+		}
+	}
+}
diff --git a/android/hooks.go b/android/hooks.go
index 2d2f797..04ba69e 100644
--- a/android/hooks.go
+++ b/android/hooks.go
@@ -15,7 +15,10 @@
 package android
 
 import (
+	"reflect"
+
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
 )
 
 // This file implements hooks that external module types can use to inject logic into existing
@@ -26,56 +29,81 @@
 // before the module has been split into architecture variants, and before defaults modules have
 // been applied.
 type LoadHookContext interface {
-	// TODO: a new context that includes Config() but not Target(), etc.?
-	BaseModuleContext
-	AppendProperties(...interface{})
-	PrependProperties(...interface{})
-	CreateModule(blueprint.ModuleFactory, ...interface{})
-}
+	EarlyModuleContext
 
-// Arch hooks are run after the module has been split into architecture variants, and can be used
-// to add architecture-specific properties.
-type ArchHookContext interface {
-	BaseModuleContext
 	AppendProperties(...interface{})
 	PrependProperties(...interface{})
+	CreateModule(ModuleFactory, ...interface{}) Module
 }
 
 func AddLoadHook(m blueprint.Module, hook func(LoadHookContext)) {
-	h := &m.(Module).base().hooks
-	h.load = append(h.load, hook)
+	blueprint.AddLoadHook(m, func(ctx blueprint.LoadHookContext) {
+		actx := &loadHookContext{
+			earlyModuleContext: m.(Module).base().earlyModuleContextFactory(ctx),
+			bp:                 ctx,
+		}
+		hook(actx)
+	})
 }
 
-func AddArchHook(m blueprint.Module, hook func(ArchHookContext)) {
-	h := &m.(Module).base().hooks
-	h.arch = append(h.arch, hook)
+type loadHookContext struct {
+	earlyModuleContext
+	bp     blueprint.LoadHookContext
+	module Module
 }
 
-func (x *hooks) runLoadHooks(ctx LoadHookContext, m *ModuleBase) {
-	if len(x.load) > 0 {
-		for _, x := range x.load {
-			x(ctx)
-			if ctx.Failed() {
-				return
+func (l *loadHookContext) AppendProperties(props ...interface{}) {
+	for _, p := range props {
+		err := proptools.AppendMatchingProperties(l.Module().base().customizableProperties,
+			p, nil)
+		if err != nil {
+			if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
+				l.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
+			} else {
+				panic(err)
 			}
 		}
 	}
 }
 
-func (x *hooks) runArchHooks(ctx ArchHookContext, m *ModuleBase) {
-	if len(x.arch) > 0 {
-		for _, x := range x.arch {
-			x(ctx)
-			if ctx.Failed() {
-				return
+func (l *loadHookContext) PrependProperties(props ...interface{}) {
+	for _, p := range props {
+		err := proptools.PrependMatchingProperties(l.Module().base().customizableProperties,
+			p, nil)
+		if err != nil {
+			if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
+				l.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
+			} else {
+				panic(err)
 			}
 		}
 	}
 }
 
+func (l *loadHookContext) CreateModule(factory ModuleFactory, props ...interface{}) Module {
+	inherited := []interface{}{&l.Module().base().commonProperties}
+	module := l.bp.CreateModule(ModuleFactoryAdaptor(factory), append(inherited, props...)...).(Module)
+
+	if l.Module().base().variableProperties != nil && module.base().variableProperties != nil {
+		src := l.Module().base().variableProperties
+		dst := []interface{}{
+			module.base().variableProperties,
+			// Put an empty copy of the src properties into dst so that properties in src that are not in dst
+			// don't cause a "failed to find property to extend" error.
+			proptools.CloneEmptyProperties(reflect.ValueOf(src).Elem()).Interface(),
+		}
+		err := proptools.AppendMatchingProperties(dst, src, nil)
+		if err != nil {
+			panic(err)
+		}
+	}
+
+	return module
+}
+
 type InstallHookContext interface {
 	ModuleContext
-	Path() OutputPath
+	Path() InstallPath
 	Symlink() bool
 }
 
@@ -89,11 +117,11 @@
 
 type installHookContext struct {
 	ModuleContext
-	path    OutputPath
+	path    InstallPath
 	symlink bool
 }
 
-func (x *installHookContext) Path() OutputPath {
+func (x *installHookContext) Path() InstallPath {
 	return x.path
 }
 
@@ -101,7 +129,7 @@
 	return x.symlink
 }
 
-func (x *hooks) runInstallHooks(ctx ModuleContext, path OutputPath, symlink bool) {
+func (x *hooks) runInstallHooks(ctx ModuleContext, path InstallPath, symlink bool) {
 	if len(x.install) > 0 {
 		mctx := &installHookContext{
 			ModuleContext: ctx,
@@ -118,29 +146,5 @@
 }
 
 type hooks struct {
-	load    []func(LoadHookContext)
-	arch    []func(ArchHookContext)
 	install []func(InstallHookContext)
 }
-
-func registerLoadHookMutator(ctx RegisterMutatorsContext) {
-	ctx.TopDown("load_hooks", LoadHookMutator).Parallel()
-}
-
-func LoadHookMutator(ctx TopDownMutatorContext) {
-	if m, ok := ctx.Module().(Module); ok {
-		// Cast through *topDownMutatorContext because AppendProperties is implemented
-		// on *topDownMutatorContext but not exposed through TopDownMutatorContext
-		var loadHookCtx LoadHookContext = ctx.(*topDownMutatorContext)
-		m.base().hooks.runLoadHooks(loadHookCtx, m.base())
-	}
-}
-
-func archHookMutator(ctx TopDownMutatorContext) {
-	if m, ok := ctx.Module().(Module); ok {
-		// Cast through *topDownMutatorContext because AppendProperties is implemented
-		// on *topDownMutatorContext but not exposed through TopDownMutatorContext
-		var archHookCtx ArchHookContext = ctx.(*topDownMutatorContext)
-		m.base().hooks.runArchHooks(archHookCtx, m.base())
-	}
-}
diff --git a/android/image.go b/android/image.go
new file mode 100644
index 0000000..5291ce3
--- /dev/null
+++ b/android/image.go
@@ -0,0 +1,83 @@
+// 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
+
+// ImageInterface is implemented by modules that need to be split by the imageMutator.
+type ImageInterface interface {
+	// ImageMutatorBegin is called before any other method in the ImageInterface.
+	ImageMutatorBegin(ctx BaseModuleContext)
+
+	// CoreVariantNeeded should return true if the module needs a core variant (installed on the system image).
+	CoreVariantNeeded(ctx BaseModuleContext) bool
+
+	// RecoveryVariantNeeded should return true if the module needs a recovery variant (installed on the
+	// recovery partition).
+	RecoveryVariantNeeded(ctx BaseModuleContext) bool
+
+	// ExtraImageVariations should return a list of the additional variations needed for the module.  After the
+	// variants are created the SetImageVariation method will be called on each newly created variant with the
+	// its variation.
+	ExtraImageVariations(ctx BaseModuleContext) []string
+
+	// SetImageVariation will be passed a newly created recovery variant of the module.  ModuleBase implements
+	// SetImageVariation, most module types will not need to override it, and those that do must call the
+	// overridden method.  Implementors of SetImageVariation must be careful to modify the module argument
+	// and not the receiver.
+	SetImageVariation(ctx BaseModuleContext, variation string, module Module)
+}
+
+const (
+	// CoreVariation is the variant used for framework-private libraries, or
+	// SDK libraries. (which framework-private libraries can use), which
+	// will be installed to the system image.
+	CoreVariation string = ""
+
+	// RecoveryVariation means a module to be installed to recovery image.
+	RecoveryVariation string = "recovery"
+)
+
+// imageMutator creates variants for modules that implement the ImageInterface that
+// allow them to build differently for each partition (recovery, core, vendor, etc.).
+func imageMutator(ctx BottomUpMutatorContext) {
+	if ctx.Os() != Android {
+		return
+	}
+
+	if m, ok := ctx.Module().(ImageInterface); ok {
+		m.ImageMutatorBegin(ctx)
+
+		var variations []string
+
+		if m.CoreVariantNeeded(ctx) {
+			variations = append(variations, CoreVariation)
+		}
+		if m.RecoveryVariantNeeded(ctx) {
+			variations = append(variations, RecoveryVariation)
+		}
+
+		extraVariations := m.ExtraImageVariations(ctx)
+		variations = append(variations, extraVariations...)
+
+		if len(variations) == 0 {
+			return
+		}
+
+		mod := ctx.CreateVariations(variations...)
+		for i, v := range variations {
+			mod[i].base().setImageVariation(v)
+			m.SetImageVariation(ctx, v, mod[i])
+		}
+	}
+}
diff --git a/android/makevars.go b/android/makevars.go
index c011ea6..aba4cce 100644
--- a/android/makevars.go
+++ b/android/makevars.go
@@ -23,7 +23,6 @@
 	"strings"
 
 	"github.com/google/blueprint"
-	"github.com/google/blueprint/pathtools"
 	"github.com/google/blueprint/proptools"
 )
 
@@ -41,7 +40,6 @@
 	Config() Config
 	DeviceConfig() DeviceConfig
 	AddNinjaFileDeps(deps ...string)
-	Fs() pathtools.FileSystem
 
 	ModuleName(module blueprint.Module) string
 	ModuleDir(module blueprint.Module) string
@@ -80,6 +78,12 @@
 	// Eval().
 	StrictRaw(name, value string)
 	CheckRaw(name, value string)
+
+	// GlobWithDeps returns a list of files that match the specified pattern but do not match any
+	// of the patterns in excludes.  It also adds efficient dependencies to rerun the primary
+	// builder whenever a file matching the pattern as added or removed, without rerunning if a
+	// file that does not match the pattern is added to a searched directory.
+	GlobWithDeps(pattern string, excludes []string) ([]string, error)
 }
 
 var _ PathContext = MakeVarsContext(nil)
@@ -145,7 +149,8 @@
 		return
 	}
 
-	outFile := PathForOutput(ctx, "make_vars"+proptools.String(ctx.Config().productVariables.Make_suffix)+".mk").String()
+	outFile := absolutePath(PathForOutput(ctx,
+		"make_vars"+proptools.String(ctx.Config().productVariables.Make_suffix)+".mk").String())
 
 	if ctx.Failed() {
 		return
@@ -169,15 +174,15 @@
 
 	outBytes := s.writeVars(vars)
 
-	if _, err := os.Stat(outFile); err == nil {
-		if data, err := ioutil.ReadFile(outFile); err == nil {
+	if _, err := os.Stat(absolutePath(outFile)); err == nil {
+		if data, err := ioutil.ReadFile(absolutePath(outFile)); err == nil {
 			if bytes.Equal(data, outBytes) {
 				return
 			}
 		}
 	}
 
-	if err := ioutil.WriteFile(outFile, outBytes, 0666); err != nil {
+	if err := ioutil.WriteFile(absolutePath(outFile), outBytes, 0666); err != nil {
 		ctx.Errorf(err.Error())
 	}
 }
diff --git a/android/module.go b/android/module.go
index 87e2ca7..67d1f12 100644
--- a/android/module.go
+++ b/android/module.go
@@ -16,13 +16,13 @@
 
 import (
 	"fmt"
+	"os"
 	"path"
 	"path/filepath"
 	"strings"
 	"text/scanner"
 
 	"github.com/google/blueprint"
-	"github.com/google/blueprint/pathtools"
 	"github.com/google/blueprint/proptools"
 )
 
@@ -55,22 +55,59 @@
 
 type ModuleBuildParams BuildParams
 
+// EarlyModuleContext provides methods that can be called early, as soon as the properties have
+// been parsed into the module and before any mutators have run.
+type EarlyModuleContext interface {
+	Module() Module
+	ModuleName() string
+	ModuleDir() string
+	ModuleType() string
+
+	ContainsProperty(name string) bool
+	Errorf(pos scanner.Position, fmt string, args ...interface{})
+	ModuleErrorf(fmt string, args ...interface{})
+	PropertyErrorf(property, fmt string, args ...interface{})
+	Failed() bool
+
+	AddNinjaFileDeps(deps ...string)
+
+	DeviceSpecific() bool
+	SocSpecific() bool
+	ProductSpecific() bool
+	SystemExtSpecific() bool
+	Platform() bool
+
+	Config() Config
+	DeviceConfig() DeviceConfig
+
+	// Deprecated: use Config()
+	AConfig() Config
+
+	// GlobWithDeps returns a list of files that match the specified pattern but do not match any
+	// of the patterns in excludes.  It also adds efficient dependencies to rerun the primary
+	// builder whenever a file matching the pattern as added or removed, without rerunning if a
+	// file that does not match the pattern is added to a searched directory.
+	GlobWithDeps(pattern string, excludes []string) ([]string, error)
+
+	Glob(globPattern string, excludes []string) Paths
+	GlobFiles(globPattern string, excludes []string) Paths
+	IsSymlink(path Path) bool
+	Readlink(path Path) string
+}
+
 // BaseModuleContext is the same as blueprint.BaseModuleContext except that Config() returns
 // a Config instead of an interface{}, and some methods have been wrapped to use an android.Module
 // instead of a blueprint.Module, plus some extra methods that return Android-specific information
 // about the current module.
 type BaseModuleContext interface {
-	Module() Module
-	ModuleName() string
-	ModuleDir() string
-	ModuleType() string
-	Config() Config
+	EarlyModuleContext
 
 	OtherModuleName(m blueprint.Module) string
 	OtherModuleDir(m blueprint.Module) string
 	OtherModuleErrorf(m blueprint.Module, fmt string, args ...interface{})
 	OtherModuleDependencyTag(m blueprint.Module) blueprint.DependencyTag
 	OtherModuleExists(name string) bool
+	OtherModuleType(m blueprint.Module) string
 
 	GetDirectDepsWithTag(tag blueprint.DependencyTag) []Module
 	GetDirectDepWithTag(name string, tag blueprint.DependencyTag) blueprint.Module
@@ -90,24 +127,6 @@
 	// and returns a top-down dependency path from a start module to current child module.
 	GetWalkPath() []Module
 
-	ContainsProperty(name string) bool
-	Errorf(pos scanner.Position, fmt string, args ...interface{})
-	ModuleErrorf(fmt string, args ...interface{})
-	PropertyErrorf(property, fmt string, args ...interface{})
-	Failed() bool
-
-	// GlobWithDeps returns a list of files that match the specified pattern but do not match any
-	// of the patterns in excludes.  It also adds efficient dependencies to rerun the primary
-	// builder whenever a file matching the pattern as added or removed, without rerunning if a
-	// file that does not match the pattern is added to a searched directory.
-	GlobWithDeps(pattern string, excludes []string) ([]string, error)
-
-	Glob(globPattern string, excludes []string) Paths
-	GlobFiles(globPattern string, excludes []string) Paths
-
-	Fs() pathtools.FileSystem
-	AddNinjaFileDeps(deps ...string)
-
 	AddMissingDependencies(missingDeps []string)
 
 	Target() Target
@@ -122,18 +141,11 @@
 	Windows() bool
 	Debug() bool
 	PrimaryArch() bool
-	Platform() bool
-	DeviceSpecific() bool
-	SocSpecific() bool
-	ProductSpecific() bool
-	ProductServicesSpecific() bool
-	AConfig() Config
-	DeviceConfig() DeviceConfig
 }
 
-// Deprecated: use BaseModuleContext instead
+// Deprecated: use EarlyModuleContext instead
 type BaseContext interface {
-	BaseModuleContext
+	EarlyModuleContext
 }
 
 type ModuleContext interface {
@@ -146,15 +158,18 @@
 	ExpandSource(srcFile, prop string) Path
 	ExpandOptionalSource(srcFile *string, prop string) OptionalPath
 
-	InstallExecutable(installPath OutputPath, name string, srcPath Path, deps ...Path) OutputPath
-	InstallFile(installPath OutputPath, name string, srcPath Path, deps ...Path) OutputPath
-	InstallSymlink(installPath OutputPath, name string, srcPath OutputPath) OutputPath
-	InstallAbsoluteSymlink(installPath OutputPath, name string, absPath string) OutputPath
+	InstallExecutable(installPath InstallPath, name string, srcPath Path, deps ...Path) InstallPath
+	InstallFile(installPath InstallPath, name string, srcPath Path, deps ...Path) InstallPath
+	InstallSymlink(installPath InstallPath, name string, srcPath InstallPath) InstallPath
+	InstallAbsoluteSymlink(installPath InstallPath, name string, absPath string) InstallPath
 	CheckbuildFile(srcPath Path)
 
 	InstallInData() bool
+	InstallInTestcases() bool
 	InstallInSanitizerDir() bool
 	InstallInRecovery() bool
+	InstallInRoot() bool
+	InstallBypassMake() bool
 
 	RequiredModuleNames() []string
 	HostRequiredModuleNames() []string
@@ -190,8 +205,11 @@
 	Enabled() bool
 	Target() Target
 	InstallInData() bool
+	InstallInTestcases() bool
 	InstallInSanitizerDir() bool
 	InstallInRecovery() bool
+	InstallInRoot() bool
+	InstallBypassMake() bool
 	SkipInstall()
 	ExportedToMake() bool
 	NoticeFile() OptionalPath
@@ -202,6 +220,65 @@
 	BuildParamsForTests() []BuildParams
 	RuleParamsForTests() map[blueprint.Rule]blueprint.RuleParams
 	VariablesForTests() map[string]string
+
+	// String returns a string that includes the module name and variants for printing during debugging.
+	String() string
+
+	// Get the qualified module id for this module.
+	qualifiedModuleId(ctx BaseModuleContext) qualifiedModuleName
+
+	// Get information about the properties that can contain visibility rules.
+	visibilityProperties() []visibilityProperty
+
+	// Get the visibility rules that control the visibility of this module.
+	visibility() []string
+
+	RequiredModuleNames() []string
+	HostRequiredModuleNames() []string
+	TargetRequiredModuleNames() []string
+}
+
+// Qualified id for a module
+type qualifiedModuleName struct {
+	// The package (i.e. directory) in which the module is defined, without trailing /
+	pkg string
+
+	// The name of the module, empty string if package.
+	name string
+}
+
+func (q qualifiedModuleName) String() string {
+	if q.name == "" {
+		return "//" + q.pkg
+	}
+	return "//" + q.pkg + ":" + q.name
+}
+
+func (q qualifiedModuleName) isRootPackage() bool {
+	return q.pkg == "" && q.name == ""
+}
+
+// Get the id for the package containing this module.
+func (q qualifiedModuleName) getContainingPackageId() qualifiedModuleName {
+	pkg := q.pkg
+	if q.name == "" {
+		if pkg == "" {
+			panic(fmt.Errorf("Cannot get containing package id of root package"))
+		}
+
+		index := strings.LastIndex(pkg, "/")
+		if index == -1 {
+			pkg = ""
+		} else {
+			pkg = pkg[:index]
+		}
+	}
+	return newPackageId(pkg)
+}
+
+func newPackageId(pkg string) qualifiedModuleName {
+	// A qualified id for a package module has no name.
+	return qualifiedModuleName{pkg: pkg, name: ""}
 }
 
 type nameProperties struct {
@@ -236,6 +313,22 @@
 	//      //packages/apps/Settings:__subpackages__.
 	//  ["//visibility:legacy_public"]: The default visibility, behaves as //visibility:public
 	//      for now. It is an error if it is used in a module.
+	//
+	// If a module does not specify the `visibility` property then it uses the
+	// `default_visibility` property of the `package` module in the module's package.
+	//
+	// If the `default_visibility` property is not set for the module's package then
+	// it will use the `default_visibility` of its closest ancestor package for which
+	// a `default_visibility` property is specified.
+	//
+	// If no `default_visibility` property can be found then the module uses the
+	// global default of `//visibility:legacy_public`.
+	//
+	// The `visibility` property has no effect on a defaults module although it does
+	// apply to any non-defaults module that uses it. To set the visibility of a
+	// defaults module, use the `defaults_visibility` property on the defaults module;
+	// not to be confused with the `default_visibility` property on the package module.
+	//
 	// See https://android.googlesource.com/platform/build/soong/+/master/README.md#visibility for
 	// more details.
 	Visibility []string
@@ -284,10 +377,9 @@
 	// /system/product if product partition does not exist).
 	Product_specific *bool
 
-	// whether this module provides services owned by the OS provider to the core platform. When set
-	// to true, it is installed into  /product_services (or /system/product_services if
-	// product_services partition does not exist).
-	Product_services_specific *bool
+	// whether this module extends system. When set to true, it is installed into /system_ext
+	// (or /system/system_ext if system_ext partition does not exist).
+	System_ext_specific *bool
 
 	// Whether this module is installed to recovery partition
 	Recovery *bool
@@ -332,6 +424,7 @@
 	} `android:"arch_variant"`
 
 	// Set by TargetMutator
+	CompileOS           OsType   `blueprint:"mutated"`
 	CompileTarget       Target   `blueprint:"mutated"`
 	CompileMultiTargets []Target `blueprint:"mutated"`
 	CompilePrimary      bool     `blueprint:"mutated"`
@@ -345,6 +438,14 @@
 	NamespaceExportedToMake bool `blueprint:"mutated"`
 
 	MissingDeps []string `blueprint:"mutated"`
+
+	// Name and variant strings stored by mutators to enable Module.String()
+	DebugName       string   `blueprint:"mutated"`
+	DebugMutators   []string `blueprint:"mutated"`
+	DebugVariations []string `blueprint:"mutated"`
+
+	// set by ImageMutator
+	ImageVariation string `blueprint:"mutated"`
 }
 
 type hostAndDeviceProperties struct {
@@ -398,7 +499,7 @@
 	deviceSpecificModule
 	socSpecificModule
 	productSpecificModule
-	productServicesSpecificModule
+	systemExtSpecificModule
 )
 
 func (k moduleKind) String() string {
@@ -411,8 +512,8 @@
 		return "soc-specific"
 	case productSpecificModule:
 		return "product-specific"
-	case productServicesSpecificModule:
-		return "productservices-specific"
+	case systemExtSpecificModule:
+		return "systemext-specific"
 	default:
 		panic(fmt.Errorf("unknown module kind %d", k))
 	}
@@ -424,10 +525,27 @@
 
 	m.AddProperties(
 		&base.nameProperties,
-		&base.commonProperties,
-		&base.variableProperties)
+		&base.commonProperties)
+
+	// Allow tests to override the default product variables
+	if base.variableProperties == nil {
+		base.variableProperties = zeroProductVariables
+	}
+
+	// Filter the product variables properties to the ones that exist on this module
+	base.variableProperties = createVariableProperties(m.GetProperties(), base.variableProperties)
+	if base.variableProperties != nil {
+		m.AddProperties(base.variableProperties)
+	}
+
 	base.generalProperties = m.GetProperties()
 	base.customizableProperties = m.GetProperties()
+
+	// The default_visibility property needs to be checked and parsed by the visibility module during
+	// its checking and parsing phases.
+	base.primaryVisibilityProperty =
+		newVisibilityProperty("visibility", &base.commonProperties.Visibility)
+	base.visibilityPropertyInfo = []visibilityProperty{base.primaryVisibilityProperty}
 }
 
 func InitAndroidArchModule(m Module, hod HostOrDeviceSupported, defaultMultilib Multilib) {
@@ -501,12 +619,19 @@
 
 	nameProperties          nameProperties
 	commonProperties        commonProperties
-	variableProperties      variableProperties
+	variableProperties      interface{}
 	hostAndDeviceProperties hostAndDeviceProperties
 	generalProperties       []interface{}
 	archProperties          [][]interface{}
 	customizableProperties  []interface{}
 
+	// Information about all the properties on the module that contains visibility rules that need
+	// checking.
+	visibilityPropertyInfo []visibilityProperty
+
+	// The primary visibility property, may be nil, that controls access to the module.
+	primaryVisibilityProperty visibilityProperty
+
 	noAddressSanitizer bool
 	installFiles       Paths
 	checkbuildFiles    Paths
@@ -562,6 +687,23 @@
 	return String(m.nameProperties.Name)
 }
 
+// String returns a string that includes the module name and variants for printing during debugging.
+func (m *ModuleBase) String() string {
+	sb := strings.Builder{}
+	sb.WriteString(m.commonProperties.DebugName)
+	sb.WriteString("{")
+	for i := range m.commonProperties.DebugMutators {
+		if i != 0 {
+			sb.WriteString(",")
+		}
+		sb.WriteString(m.commonProperties.DebugMutators[i])
+		sb.WriteString(":")
+		sb.WriteString(m.commonProperties.DebugVariations[i])
+	}
+	sb.WriteString("}")
+	return sb.String()
+}
+
 // BaseModuleName returns the name of the module as specified in the blueprints file.
 func (m *ModuleBase) BaseModuleName() string {
 	return String(m.nameProperties.Name)
@@ -571,10 +713,21 @@
 	return m
 }
 
-func (m *ModuleBase) SetTarget(target Target, multiTargets []Target, primary bool) {
-	m.commonProperties.CompileTarget = target
-	m.commonProperties.CompileMultiTargets = multiTargets
-	m.commonProperties.CompilePrimary = primary
+func (m *ModuleBase) qualifiedModuleId(ctx BaseModuleContext) qualifiedModuleName {
+	return qualifiedModuleName{pkg: ctx.ModuleDir(), name: ctx.ModuleName()}
+}
+
+func (m *ModuleBase) visibilityProperties() []visibilityProperty {
+	return m.visibilityPropertyInfo
+}
+
+func (m *ModuleBase) visibility() []string {
+	// The soong_namespace module does not initialize the primaryVisibilityProperty.
+	if m.primaryVisibilityProperty != nil {
+		return m.primaryVisibilityProperty.getStrings()
+	} else {
+		return nil
+	}
 }
 
 func (m *ModuleBase) Target() Target {
@@ -637,8 +790,15 @@
 				*m.hostAndDeviceProperties.Device_supported)
 }
 
+func (m *ModuleBase) HostSupported() bool {
+	return m.commonProperties.HostOrDeviceSupported == HostSupported ||
+		m.commonProperties.HostOrDeviceSupported == HostAndDeviceSupported &&
+			(m.hostAndDeviceProperties.Host_supported != nil &&
+				*m.hostAndDeviceProperties.Host_supported)
+}
+
 func (m *ModuleBase) Platform() bool {
-	return !m.DeviceSpecific() && !m.SocSpecific() && !m.ProductSpecific() && !m.ProductServicesSpecific()
+	return !m.DeviceSpecific() && !m.SocSpecific() && !m.ProductSpecific() && !m.SystemExtSpecific()
 }
 
 func (m *ModuleBase) DeviceSpecific() bool {
@@ -653,8 +813,8 @@
 	return Bool(m.commonProperties.Product_specific)
 }
 
-func (m *ModuleBase) ProductServicesSpecific() bool {
-	return Bool(m.commonProperties.Product_services_specific)
+func (m *ModuleBase) SystemExtSpecific() bool {
+	return Bool(m.commonProperties.System_ext_specific)
 }
 
 func (m *ModuleBase) Enabled() bool {
@@ -699,6 +859,10 @@
 	return false
 }
 
+func (m *ModuleBase) InstallInTestcases() bool {
+	return false
+}
+
 func (m *ModuleBase) InstallInSanitizerDir() bool {
 	return false
 }
@@ -707,6 +871,14 @@
 	return Bool(m.commonProperties.Recovery)
 }
 
+func (m *ModuleBase) InstallInRoot() bool {
+	return false
+}
+
+func (m *ModuleBase) InstallBypassMake() bool {
+	return false
+}
+
 func (m *ModuleBase) Owner() string {
 	return String(m.commonProperties.Owner)
 }
@@ -715,6 +887,33 @@
 	return m.noticeFile
 }
 
+func (m *ModuleBase) setImageVariation(variant string) {
+	m.commonProperties.ImageVariation = variant
+}
+
+func (m *ModuleBase) ImageVariation() blueprint.Variation {
+	return blueprint.Variation{
+		Mutator:   "image",
+		Variation: m.base().commonProperties.ImageVariation,
+	}
+}
+
+func (m *ModuleBase) InRecovery() bool {
+	return m.base().commonProperties.ImageVariation == RecoveryVariation
+}
+
+func (m *ModuleBase) RequiredModuleNames() []string {
+	return m.base().commonProperties.Required
+}
+
+func (m *ModuleBase) HostRequiredModuleNames() []string {
+	return m.base().commonProperties.Host_required
+}
+
+func (m *ModuleBase) TargetRequiredModuleNames() []string {
+	return m.base().commonProperties.Target_required
+}
+
 func (m *ModuleBase) generateModuleTarget(ctx ModuleContext) {
 	allInstalledFiles := Paths{}
 	allCheckbuildFiles := Paths{}
@@ -771,11 +970,11 @@
 	}
 }
 
-func determineModuleKind(m *ModuleBase, ctx blueprint.BaseModuleContext) moduleKind {
+func determineModuleKind(m *ModuleBase, ctx blueprint.EarlyModuleContext) moduleKind {
 	var socSpecific = Bool(m.commonProperties.Vendor) || Bool(m.commonProperties.Proprietary) || Bool(m.commonProperties.Soc_specific)
 	var deviceSpecific = Bool(m.commonProperties.Device_specific)
 	var productSpecific = Bool(m.commonProperties.Product_specific)
-	var productServicesSpecific = Bool(m.commonProperties.Product_services_specific)
+	var systemExtSpecific = Bool(m.commonProperties.System_ext_specific)
 
 	msg := "conflicting value set here"
 	if socSpecific && deviceSpecific {
@@ -791,16 +990,16 @@
 		}
 	}
 
-	if productSpecific && productServicesSpecific {
-		ctx.PropertyErrorf("product_specific", "a module cannot be specific to product and product_services at the same time.")
-		ctx.PropertyErrorf("product_services_specific", msg)
+	if productSpecific && systemExtSpecific {
+		ctx.PropertyErrorf("product_specific", "a module cannot be specific to product and system_ext at the same time.")
+		ctx.PropertyErrorf("system_ext_specific", msg)
 	}
 
-	if (socSpecific || deviceSpecific) && (productSpecific || productServicesSpecific) {
+	if (socSpecific || deviceSpecific) && (productSpecific || systemExtSpecific) {
 		if productSpecific {
 			ctx.PropertyErrorf("product_specific", "a module cannot be specific to SoC or device and product at the same time.")
 		} else {
-			ctx.PropertyErrorf("product_services_specific", "a module cannot be specific to SoC or device and product_services at the same time.")
+			ctx.PropertyErrorf("system_ext_specific", "a module cannot be specific to SoC or device and system_ext at the same time.")
 		}
 		if deviceSpecific {
 			ctx.PropertyErrorf("device_specific", msg)
@@ -819,8 +1018,8 @@
 
 	if productSpecific {
 		return productSpecificModule
-	} else if productServicesSpecific {
-		return productServicesSpecificModule
+	} else if systemExtSpecific {
+		return systemExtSpecificModule
 	} else if deviceSpecific {
 		return deviceSpecificModule
 	} else if socSpecific {
@@ -830,14 +1029,22 @@
 	}
 }
 
+func (m *ModuleBase) earlyModuleContextFactory(ctx blueprint.EarlyModuleContext) earlyModuleContext {
+	return earlyModuleContext{
+		EarlyModuleContext: ctx,
+		kind:               determineModuleKind(m, ctx),
+		config:             ctx.Config().(Config),
+	}
+}
+
 func (m *ModuleBase) baseModuleContextFactory(ctx blueprint.BaseModuleContext) baseModuleContext {
 	return baseModuleContext{
-		BaseModuleContext: ctx,
-		target:            m.commonProperties.CompileTarget,
-		targetPrimary:     m.commonProperties.CompilePrimary,
-		multiTargets:      m.commonProperties.CompileMultiTargets,
-		kind:              determineModuleKind(m, ctx),
-		config:            ctx.Config().(Config),
+		bp:                 ctx,
+		earlyModuleContext: m.earlyModuleContextFactory(ctx),
+		os:                 m.commonProperties.CompileOS,
+		target:             m.commonProperties.CompileTarget,
+		targetPrimary:      m.commonProperties.CompilePrimary,
+		multiTargets:       m.commonProperties.CompileMultiTargets,
 	}
 }
 
@@ -901,13 +1108,12 @@
 	}
 
 	if m.Enabled() {
-		m.module.GenerateAndroidBuildActions(ctx)
-		if ctx.Failed() {
-			return
-		}
-
-		m.installFiles = append(m.installFiles, ctx.installFiles...)
-		m.checkbuildFiles = append(m.checkbuildFiles, ctx.checkbuildFiles...)
+		// ensure all direct android.Module deps are enabled
+		ctx.VisitDirectDepsBlueprint(func(bm blueprint.Module) {
+			if _, ok := bm.(Module); ok {
+				ctx.validateAndroidModule(bm, ctx.baseModuleContext.strictVisitDeps)
+			}
+		})
 
 		notice := proptools.StringDefault(m.commonProperties.Notice, "NOTICE")
 		if module := SrcIsModule(notice); module != "" {
@@ -916,6 +1122,14 @@
 			noticePath := filepath.Join(ctx.ModuleDir(), notice)
 			m.noticeFile = ExistentPathForSource(ctx, noticePath)
 		}
+
+		m.module.GenerateAndroidBuildActions(ctx)
+		if ctx.Failed() {
+			return
+		}
+
+		m.installFiles = append(m.installFiles, ctx.installFiles...)
+		m.checkbuildFiles = append(m.checkbuildFiles, ctx.checkbuildFiles...)
 	} else if ctx.Config().AllowMissingDependencies() {
 		// If the module is not enabled it will not create any build rules, nothing will call
 		// ctx.GetMissingDependencies(), and blueprint will consider the missing dependencies to be unhandled
@@ -936,20 +1150,111 @@
 	m.variables = ctx.variables
 }
 
+type earlyModuleContext struct {
+	blueprint.EarlyModuleContext
+
+	kind   moduleKind
+	config Config
+}
+
+func (e *earlyModuleContext) Glob(globPattern string, excludes []string) Paths {
+	ret, err := e.GlobWithDeps(globPattern, excludes)
+	if err != nil {
+		e.ModuleErrorf("glob: %s", err.Error())
+	}
+	return pathsForModuleSrcFromFullPath(e, ret, true)
+}
+
+func (e *earlyModuleContext) GlobFiles(globPattern string, excludes []string) Paths {
+	ret, err := e.GlobWithDeps(globPattern, excludes)
+	if err != nil {
+		e.ModuleErrorf("glob: %s", err.Error())
+	}
+	return pathsForModuleSrcFromFullPath(e, ret, false)
+}
+
+func (b *earlyModuleContext) IsSymlink(path Path) bool {
+	fileInfo, err := b.config.fs.Lstat(path.String())
+	if err != nil {
+		b.ModuleErrorf("os.Lstat(%q) failed: %s", path.String(), err)
+	}
+	return fileInfo.Mode()&os.ModeSymlink == os.ModeSymlink
+}
+
+func (b *earlyModuleContext) Readlink(path Path) string {
+	dest, err := b.config.fs.Readlink(path.String())
+	if err != nil {
+		b.ModuleErrorf("os.Readlink(%q) failed: %s", path.String(), err)
+	}
+	return dest
+}
+
+func (e *earlyModuleContext) Module() Module {
+	module, _ := e.EarlyModuleContext.Module().(Module)
+	return module
+}
+
+func (e *earlyModuleContext) Config() Config {
+	return e.EarlyModuleContext.Config().(Config)
+}
+
+func (e *earlyModuleContext) AConfig() Config {
+	return e.config
+}
+
+func (e *earlyModuleContext) DeviceConfig() DeviceConfig {
+	return DeviceConfig{e.config.deviceConfig}
+}
+
+func (e *earlyModuleContext) Platform() bool {
+	return e.kind == platformModule
+}
+
+func (e *earlyModuleContext) DeviceSpecific() bool {
+	return e.kind == deviceSpecificModule
+}
+
+func (e *earlyModuleContext) SocSpecific() bool {
+	return e.kind == socSpecificModule
+}
+
+func (e *earlyModuleContext) ProductSpecific() bool {
+	return e.kind == productSpecificModule
+}
+
+func (e *earlyModuleContext) SystemExtSpecific() bool {
+	return e.kind == systemExtSpecificModule
+}
+
 type baseModuleContext struct {
-	blueprint.BaseModuleContext
+	bp blueprint.BaseModuleContext
+	earlyModuleContext
+	os            OsType
 	target        Target
 	multiTargets  []Target
 	targetPrimary bool
 	debug         bool
-	kind          moduleKind
-	config        Config
 
 	walkPath []Module
 
 	strictVisitDeps bool // If true, enforce that all dependencies are enabled
 }
 
+func (b *baseModuleContext) OtherModuleName(m blueprint.Module) string { return b.bp.OtherModuleName(m) }
+func (b *baseModuleContext) OtherModuleDir(m blueprint.Module) string  { return b.bp.OtherModuleDir(m) }
+func (b *baseModuleContext) OtherModuleErrorf(m blueprint.Module, fmt string, args ...interface{}) {
+	b.bp.OtherModuleErrorf(m, fmt, args)
+}
+func (b *baseModuleContext) OtherModuleDependencyTag(m blueprint.Module) blueprint.DependencyTag {
+	return b.bp.OtherModuleDependencyTag(m)
+}
+func (b *baseModuleContext) OtherModuleExists(name string) bool        { return b.bp.OtherModuleExists(name) }
+func (b *baseModuleContext) OtherModuleType(m blueprint.Module) string { return b.bp.OtherModuleType(m) }
+
+func (b *baseModuleContext) GetDirectDepWithTag(name string, tag blueprint.DependencyTag) blueprint.Module {
+	return b.bp.GetDirectDepWithTag(name, tag)
+}
+
 type moduleContext struct {
 	bp blueprint.ModuleContext
 	baseModuleContext
@@ -1033,6 +1338,12 @@
 func (m *moduleContext) Rule(pctx PackageContext, name string, params blueprint.RuleParams,
 	argNames ...string) blueprint.Rule {
 
+	if (m.config.UseGoma() || m.config.UseRBE()) && params.Pool == nil {
+		// When USE_GOMA=true or USE_RBE=true are set and the rule is not supported by goma/RBE, restrict
+		// jobs to the local parallelism value
+		params.Pool = localPool
+	}
+
 	rule := m.bp.Rule(pctx.PackageContext, name, params, argNames...)
 
 	if m.config.captureBuild {
@@ -1058,16 +1369,6 @@
 
 	m.bp.Build(pctx.PackageContext, convertBuildParams(params))
 }
-
-func (b *baseModuleContext) Module() Module {
-	module, _ := b.BaseModuleContext.Module().(Module)
-	return module
-}
-
-func (b *baseModuleContext) Config() Config {
-	return b.BaseModuleContext.Config().(Config)
-}
-
 func (m *moduleContext) GetMissingDependencies() []string {
 	var missingDeps []string
 	missingDeps = append(missingDeps, m.Module().base().commonProperties.MissingDeps...)
@@ -1115,7 +1416,7 @@
 	var deps []dep
 	b.VisitDirectDepsBlueprint(func(module blueprint.Module) {
 		if aModule, _ := module.(Module); aModule != nil && aModule.base().BaseModuleName() == name {
-			returnedTag := b.BaseModuleContext.OtherModuleDependencyTag(aModule)
+			returnedTag := b.bp.OtherModuleDependencyTag(aModule)
 			if tag == nil || returnedTag == tag {
 				deps = append(deps, dep{aModule, returnedTag})
 			}
@@ -1135,7 +1436,7 @@
 	var deps []Module
 	b.VisitDirectDepsBlueprint(func(module blueprint.Module) {
 		if aModule, _ := module.(Module); aModule != nil {
-			if b.BaseModuleContext.OtherModuleDependencyTag(aModule) == tag {
+			if b.bp.OtherModuleDependencyTag(aModule) == tag {
 				deps = append(deps, aModule)
 			}
 		}
@@ -1153,11 +1454,11 @@
 }
 
 func (b *baseModuleContext) VisitDirectDepsBlueprint(visit func(blueprint.Module)) {
-	b.BaseModuleContext.VisitDirectDeps(visit)
+	b.bp.VisitDirectDeps(visit)
 }
 
 func (b *baseModuleContext) VisitDirectDeps(visit func(Module)) {
-	b.BaseModuleContext.VisitDirectDeps(func(module blueprint.Module) {
+	b.bp.VisitDirectDeps(func(module blueprint.Module) {
 		if aModule := b.validateAndroidModule(module, b.strictVisitDeps); aModule != nil {
 			visit(aModule)
 		}
@@ -1165,9 +1466,9 @@
 }
 
 func (b *baseModuleContext) VisitDirectDepsWithTag(tag blueprint.DependencyTag, visit func(Module)) {
-	b.BaseModuleContext.VisitDirectDeps(func(module blueprint.Module) {
+	b.bp.VisitDirectDeps(func(module blueprint.Module) {
 		if aModule := b.validateAndroidModule(module, b.strictVisitDeps); aModule != nil {
-			if b.BaseModuleContext.OtherModuleDependencyTag(aModule) == tag {
+			if b.bp.OtherModuleDependencyTag(aModule) == tag {
 				visit(aModule)
 			}
 		}
@@ -1175,7 +1476,7 @@
 }
 
 func (b *baseModuleContext) VisitDirectDepsIf(pred func(Module) bool, visit func(Module)) {
-	b.BaseModuleContext.VisitDirectDepsIf(
+	b.bp.VisitDirectDepsIf(
 		// pred
 		func(module blueprint.Module) bool {
 			if aModule := b.validateAndroidModule(module, b.strictVisitDeps); aModule != nil {
@@ -1191,7 +1492,7 @@
 }
 
 func (b *baseModuleContext) VisitDepsDepthFirst(visit func(Module)) {
-	b.BaseModuleContext.VisitDepsDepthFirst(func(module blueprint.Module) {
+	b.bp.VisitDepsDepthFirst(func(module blueprint.Module) {
 		if aModule := b.validateAndroidModule(module, b.strictVisitDeps); aModule != nil {
 			visit(aModule)
 		}
@@ -1199,7 +1500,7 @@
 }
 
 func (b *baseModuleContext) VisitDepsDepthFirstIf(pred func(Module) bool, visit func(Module)) {
-	b.BaseModuleContext.VisitDepsDepthFirstIf(
+	b.bp.VisitDepsDepthFirstIf(
 		// pred
 		func(module blueprint.Module) bool {
 			if aModule := b.validateAndroidModule(module, b.strictVisitDeps); aModule != nil {
@@ -1215,12 +1516,12 @@
 }
 
 func (b *baseModuleContext) WalkDepsBlueprint(visit func(blueprint.Module, blueprint.Module) bool) {
-	b.BaseModuleContext.WalkDeps(visit)
+	b.bp.WalkDeps(visit)
 }
 
 func (b *baseModuleContext) WalkDeps(visit func(Module, Module) bool) {
 	b.walkPath = []Module{b.Module()}
-	b.BaseModuleContext.WalkDeps(func(child, parent blueprint.Module) bool {
+	b.bp.WalkDeps(func(child, parent blueprint.Module) bool {
 		childAndroidModule, _ := child.(Module)
 		parentAndroidModule, _ := parent.(Module)
 		if childAndroidModule != nil && parentAndroidModule != nil {
@@ -1275,27 +1576,27 @@
 }
 
 func (b *baseModuleContext) Os() OsType {
-	return b.target.Os
+	return b.os
 }
 
 func (b *baseModuleContext) Host() bool {
-	return b.target.Os.Class == Host || b.target.Os.Class == HostCross
+	return b.os.Class == Host || b.os.Class == HostCross
 }
 
 func (b *baseModuleContext) Device() bool {
-	return b.target.Os.Class == Device
+	return b.os.Class == Device
 }
 
 func (b *baseModuleContext) Darwin() bool {
-	return b.target.Os == Darwin
+	return b.os == Darwin
 }
 
 func (b *baseModuleContext) Fuchsia() bool {
-	return b.target.Os == Fuchsia
+	return b.os == Fuchsia
 }
 
 func (b *baseModuleContext) Windows() bool {
-	return b.target.Os == Windows
+	return b.os == Windows
 }
 
 func (b *baseModuleContext) Debug() bool {
@@ -1309,52 +1610,41 @@
 	return b.target.Arch.ArchType == b.config.Targets[b.target.Os][0].Arch.ArchType
 }
 
-func (b *baseModuleContext) AConfig() Config {
-	return b.config
-}
-
-func (b *baseModuleContext) DeviceConfig() DeviceConfig {
-	return DeviceConfig{b.config.deviceConfig}
-}
-
-func (b *baseModuleContext) Platform() bool {
-	return b.kind == platformModule
-}
-
-func (b *baseModuleContext) DeviceSpecific() bool {
-	return b.kind == deviceSpecificModule
-}
-
-func (b *baseModuleContext) SocSpecific() bool {
-	return b.kind == socSpecificModule
-}
-
-func (b *baseModuleContext) ProductSpecific() bool {
-	return b.kind == productSpecificModule
-}
-
-func (b *baseModuleContext) ProductServicesSpecific() bool {
-	return b.kind == productServicesSpecificModule
-}
-
 // Makes this module a platform module, i.e. not specific to soc, device,
-// product, or product_services.
+// product, or system_ext.
 func (m *ModuleBase) MakeAsPlatform() {
 	m.commonProperties.Vendor = boolPtr(false)
 	m.commonProperties.Proprietary = boolPtr(false)
 	m.commonProperties.Soc_specific = boolPtr(false)
 	m.commonProperties.Product_specific = boolPtr(false)
-	m.commonProperties.Product_services_specific = boolPtr(false)
+	m.commonProperties.System_ext_specific = boolPtr(false)
 }
 
 func (m *ModuleBase) EnableNativeBridgeSupportByDefault() {
 	m.commonProperties.Native_bridge_supported = boolPtr(true)
 }
 
+func (m *ModuleBase) MakeAsSystemExt() {
+	m.commonProperties.Vendor = boolPtr(false)
+	m.commonProperties.Proprietary = boolPtr(false)
+	m.commonProperties.Soc_specific = boolPtr(false)
+	m.commonProperties.Product_specific = boolPtr(false)
+	m.commonProperties.System_ext_specific = boolPtr(true)
+}
+
+// IsNativeBridgeSupported returns true if "native_bridge_supported" is explicitly set as "true"
+func (m *ModuleBase) IsNativeBridgeSupported() bool {
+	return proptools.Bool(m.commonProperties.Native_bridge_supported)
+}
+
 func (m *moduleContext) InstallInData() bool {
 	return m.module.InstallInData()
 }
 
+func (m *moduleContext) InstallInTestcases() bool {
+	return m.module.InstallInTestcases()
+}
+
 func (m *moduleContext) InstallInSanitizerDir() bool {
 	return m.module.InstallInSanitizerDir()
 }
@@ -1363,7 +1653,15 @@
 	return m.module.InstallInRecovery()
 }
 
-func (m *moduleContext) skipInstall(fullInstallPath OutputPath) bool {
+func (m *moduleContext) InstallInRoot() bool {
+	return m.module.InstallInRoot()
+}
+
+func (m *moduleContext) InstallBypassMake() bool {
+	return m.module.InstallBypassMake()
+}
+
+func (m *moduleContext) skipInstall(fullInstallPath InstallPath) bool {
 	if m.module.base().commonProperties.SkipInstall {
 		return true
 	}
@@ -1376,7 +1674,7 @@
 	}
 
 	if m.Device() {
-		if m.Config().SkipDeviceInstall() {
+		if m.Config().EmbeddedInMake() && !m.InstallBypassMake() {
 			return true
 		}
 
@@ -1388,18 +1686,18 @@
 	return false
 }
 
-func (m *moduleContext) InstallFile(installPath OutputPath, name string, srcPath Path,
-	deps ...Path) OutputPath {
+func (m *moduleContext) InstallFile(installPath InstallPath, name string, srcPath Path,
+	deps ...Path) InstallPath {
 	return m.installFile(installPath, name, srcPath, Cp, deps)
 }
 
-func (m *moduleContext) InstallExecutable(installPath OutputPath, name string, srcPath Path,
-	deps ...Path) OutputPath {
+func (m *moduleContext) InstallExecutable(installPath InstallPath, name string, srcPath Path,
+	deps ...Path) InstallPath {
 	return m.installFile(installPath, name, srcPath, CpExecutable, deps)
 }
 
-func (m *moduleContext) installFile(installPath OutputPath, name string, srcPath Path,
-	rule blueprint.Rule, deps []Path) OutputPath {
+func (m *moduleContext) installFile(installPath InstallPath, name string, srcPath Path,
+	rule blueprint.Rule, deps []Path) InstallPath {
 
 	fullInstallPath := installPath.Join(m, name)
 	m.module.base().hooks.runInstallHooks(m, fullInstallPath, false)
@@ -1434,7 +1732,7 @@
 	return fullInstallPath
 }
 
-func (m *moduleContext) InstallSymlink(installPath OutputPath, name string, srcPath OutputPath) OutputPath {
+func (m *moduleContext) InstallSymlink(installPath InstallPath, name string, srcPath InstallPath) InstallPath {
 	fullInstallPath := installPath.Join(m, name)
 	m.module.base().hooks.runInstallHooks(m, fullInstallPath, true)
 
@@ -1463,7 +1761,7 @@
 
 // installPath/name -> absPath where absPath might be a path that is available only at runtime
 // (e.g. /apex/...)
-func (m *moduleContext) InstallAbsoluteSymlink(installPath OutputPath, name string, absPath string) OutputPath {
+func (m *moduleContext) InstallAbsoluteSymlink(installPath InstallPath, name string, absPath string) InstallPath {
 	fullInstallPath := installPath.Join(m, name)
 	m.module.base().hooks.runInstallHooks(m, fullInstallPath, true)
 
@@ -1585,12 +1883,55 @@
 }
 
 // A module that implements OutputFileProducer can be referenced from any property that is tagged with `android:"path"`
-// using the ":module" syntax or ":module{.tag}" syntax and provides a list of otuput files to be used as if they were
+// using the ":module" syntax or ":module{.tag}" syntax and provides a list of output files to be used as if they were
 // listed in the property.
 type OutputFileProducer interface {
 	OutputFiles(tag string) (Paths, error)
 }
 
+// OutputFilesForModule returns the paths from an OutputFileProducer with the given tag.  On error, including if the
+// module produced zero paths, it reports errors to the ctx and returns nil.
+func OutputFilesForModule(ctx PathContext, module blueprint.Module, tag string) Paths {
+	paths, err := outputFilesForModule(ctx, module, tag)
+	if err != nil {
+		reportPathError(ctx, err)
+		return nil
+	}
+	return paths
+}
+
+// OutputFileForModule returns the path from an OutputFileProducer with the given tag.  On error, including if the
+// module produced zero or multiple paths, it reports errors to the ctx and returns nil.
+func OutputFileForModule(ctx PathContext, module blueprint.Module, tag string) Path {
+	paths, err := outputFilesForModule(ctx, module, tag)
+	if err != nil {
+		reportPathError(ctx, err)
+		return nil
+	}
+	if len(paths) > 1 {
+		reportPathErrorf(ctx, "got multiple output files from module %q, expected exactly one",
+			pathContextName(ctx, module))
+		return nil
+	}
+	return paths[0]
+}
+
+func outputFilesForModule(ctx PathContext, module blueprint.Module, tag string) (Paths, error) {
+	if outputFileProducer, ok := module.(OutputFileProducer); ok {
+		paths, err := outputFileProducer.OutputFiles(tag)
+		if err != nil {
+			return nil, fmt.Errorf("failed to get output file from module %q: %s",
+				pathContextName(ctx, module), err.Error())
+		}
+		if len(paths) == 0 {
+			return nil, fmt.Errorf("failed to get output files from module %q", pathContextName(ctx, module))
+		}
+		return paths, nil
+	} else {
+		return nil, fmt.Errorf("module %q is not an OutputFileProducer", pathContextName(ctx, module))
+	}
+}
+
 type HostToolProvider interface {
 	HostToolPath() OptionalPath
 }
@@ -1622,31 +1963,15 @@
 }
 
 func (m *moduleContext) RequiredModuleNames() []string {
-	return m.module.base().commonProperties.Required
+	return m.module.RequiredModuleNames()
 }
 
 func (m *moduleContext) HostRequiredModuleNames() []string {
-	return m.module.base().commonProperties.Host_required
+	return m.module.HostRequiredModuleNames()
 }
 
 func (m *moduleContext) TargetRequiredModuleNames() []string {
-	return m.module.base().commonProperties.Target_required
-}
-
-func (b *baseModuleContext) Glob(globPattern string, excludes []string) Paths {
-	ret, err := b.GlobWithDeps(globPattern, excludes)
-	if err != nil {
-		b.ModuleErrorf("glob: %s", err.Error())
-	}
-	return pathsForModuleSrcFromFullPath(b, ret, true)
-}
-
-func (b *baseModuleContext) GlobFiles(globPattern string, excludes []string) Paths {
-	ret, err := b.GlobWithDeps(globPattern, excludes)
-	if err != nil {
-		b.ModuleErrorf("glob: %s", err.Error())
-	}
-	return pathsForModuleSrcFromFullPath(b, ret, false)
+	return m.module.TargetRequiredModuleNames()
 }
 
 func init() {
diff --git a/android/module_test.go b/android/module_test.go
index c790a68..6e648d7 100644
--- a/android/module_test.go
+++ b/android/module_test.go
@@ -14,7 +14,9 @@
 
 package android
 
-import "testing"
+import (
+	"testing"
+)
 
 func TestSrcIsModule(t *testing.T) {
 	type args struct {
@@ -139,3 +141,49 @@
 		})
 	}
 }
+
+type depsModule struct {
+	ModuleBase
+	props struct {
+		Deps []string
+	}
+}
+
+func (m *depsModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+}
+
+func (m *depsModule) DepsMutator(ctx BottomUpMutatorContext) {
+	ctx.AddDependency(ctx.Module(), nil, m.props.Deps...)
+}
+
+func depsModuleFactory() Module {
+	m := &depsModule{}
+	m.AddProperties(&m.props)
+	InitAndroidModule(m)
+	return m
+}
+
+func TestErrorDependsOnDisabledModule(t *testing.T) {
+	ctx := NewTestContext()
+	ctx.RegisterModuleType("deps", depsModuleFactory)
+
+	bp := `
+		deps {
+			name: "foo",
+			deps: ["bar"],
+		}
+		deps {
+			name: "bar",
+			enabled: false,
+		}
+	`
+
+	config := TestConfig(buildDir, nil, bp, nil)
+
+	ctx.Register(config)
+
+	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+	FailIfErrored(t, errs)
+	_, errs = ctx.PrepareBuildActions(config)
+	FailIfNoMatchingErrors(t, `module "foo": depends on disabled module "bar"`, errs)
+}
diff --git a/android/mutator.go b/android/mutator.go
index 081c2b2..f2f9663 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -15,6 +15,8 @@
 package android
 
 import (
+	"reflect"
+
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
@@ -73,17 +75,19 @@
 type RegisterMutatorFunc func(RegisterMutatorsContext)
 
 var preArch = []RegisterMutatorFunc{
-	registerLoadHookMutator,
 	RegisterNamespaceMutator,
+	// Rename package module types.
+	RegisterPackageRenamer,
 	RegisterPrebuiltsPreArchMutators,
-	registerVisibilityRuleChecker,
+	RegisterVisibilityRuleChecker,
 	RegisterDefaultsPreArchMutators,
-	registerVisibilityRuleGatherer,
+	RegisterVisibilityRuleGatherer,
 }
 
 func registerArchMutator(ctx RegisterMutatorsContext) {
+	ctx.BottomUp("os", osMutator).Parallel()
+	ctx.BottomUp("image", imageMutator).Parallel()
 	ctx.BottomUp("arch", archMutator).Parallel()
-	ctx.TopDown("arch_hooks", archHookMutator).Parallel()
 }
 
 var preDeps = []RegisterMutatorFunc{
@@ -93,7 +97,7 @@
 var postDeps = []RegisterMutatorFunc{
 	registerPathDepsMutator,
 	RegisterPrebuiltsPostDepsMutators,
-	registerVisibilityRuleEnforcer,
+	RegisterVisibilityRuleEnforcer,
 	registerNeverallowMutator,
 	RegisterOverridePostDepsMutators,
 }
@@ -115,9 +119,11 @@
 type TopDownMutatorContext interface {
 	BaseModuleContext
 
+	MutatorName() string
+
 	Rename(name string)
 
-	CreateModule(blueprint.ModuleFactory, ...interface{})
+	CreateModule(ModuleFactory, ...interface{}) Module
 }
 
 type topDownMutatorContext struct {
@@ -130,17 +136,21 @@
 type BottomUpMutatorContext interface {
 	BaseModuleContext
 
+	MutatorName() string
+
 	Rename(name string)
 
 	AddDependency(module blueprint.Module, tag blueprint.DependencyTag, name ...string)
 	AddReverseDependency(module blueprint.Module, tag blueprint.DependencyTag, name string)
-	CreateVariations(...string) []blueprint.Module
-	CreateLocalVariations(...string) []blueprint.Module
+	CreateVariations(...string) []Module
+	CreateLocalVariations(...string) []Module
 	SetDependencyVariation(string)
+	SetDefaultDependencyVariation(*string)
 	AddVariationDependencies([]blueprint.Variation, blueprint.DependencyTag, ...string)
 	AddFarVariationDependencies([]blueprint.Variation, blueprint.DependencyTag, ...string)
 	AddInterVariantDependency(tag blueprint.DependencyTag, from, to blueprint.Module)
 	ReplaceDependencies(string)
+	AliasVariation(variationName string)
 }
 
 type bottomUpMutatorContext struct {
@@ -227,16 +237,43 @@
 // non-overridden method has to be forwarded.  There are fewer non-overridden methods, so use the latter.  The following
 // methods forward to the identical blueprint versions for topDownMutatorContext and bottomUpMutatorContext.
 
-func (t *topDownMutatorContext) Rename(name string) {
-	t.bp.Rename(name)
+func (t *topDownMutatorContext) MutatorName() string {
+	return t.bp.MutatorName()
 }
 
-func (t *topDownMutatorContext) CreateModule(factory blueprint.ModuleFactory, props ...interface{}) {
-	t.bp.CreateModule(factory, props...)
+func (t *topDownMutatorContext) Rename(name string) {
+	t.bp.Rename(name)
+	t.Module().base().commonProperties.DebugName = name
+}
+
+func (t *topDownMutatorContext) CreateModule(factory ModuleFactory, props ...interface{}) Module {
+	inherited := []interface{}{&t.Module().base().commonProperties}
+	module := t.bp.CreateModule(ModuleFactoryAdaptor(factory), append(inherited, props...)...).(Module)
+
+	if t.Module().base().variableProperties != nil && module.base().variableProperties != nil {
+		src := t.Module().base().variableProperties
+		dst := []interface{}{
+			module.base().variableProperties,
+			// Put an empty copy of the src properties into dst so that properties in src that are not in dst
+			// don't cause a "failed to find property to extend" error.
+			proptools.CloneEmptyProperties(reflect.ValueOf(src).Elem()).Interface(),
+		}
+		err := proptools.AppendMatchingProperties(dst, src, nil)
+		if err != nil {
+			panic(err)
+		}
+	}
+
+	return module
+}
+
+func (b *bottomUpMutatorContext) MutatorName() string {
+	return b.bp.MutatorName()
 }
 
 func (b *bottomUpMutatorContext) Rename(name string) {
 	b.bp.Rename(name)
+	b.Module().base().commonProperties.DebugName = name
 }
 
 func (b *bottomUpMutatorContext) AddDependency(module blueprint.Module, tag blueprint.DependencyTag, name ...string) {
@@ -247,18 +284,42 @@
 	b.bp.AddReverseDependency(module, tag, name)
 }
 
-func (b *bottomUpMutatorContext) CreateVariations(variations ...string) []blueprint.Module {
-	return b.bp.CreateVariations(variations...)
+func (b *bottomUpMutatorContext) CreateVariations(variations ...string) []Module {
+	modules := b.bp.CreateVariations(variations...)
+
+	aModules := make([]Module, len(modules))
+	for i := range variations {
+		aModules[i] = modules[i].(Module)
+		base := aModules[i].base()
+		base.commonProperties.DebugMutators = append(base.commonProperties.DebugMutators, b.MutatorName())
+		base.commonProperties.DebugVariations = append(base.commonProperties.DebugVariations, variations[i])
+	}
+
+	return aModules
 }
 
-func (b *bottomUpMutatorContext) CreateLocalVariations(variations ...string) []blueprint.Module {
-	return b.bp.CreateLocalVariations(variations...)
+func (b *bottomUpMutatorContext) CreateLocalVariations(variations ...string) []Module {
+	modules := b.bp.CreateLocalVariations(variations...)
+
+	aModules := make([]Module, len(modules))
+	for i := range variations {
+		aModules[i] = modules[i].(Module)
+		base := aModules[i].base()
+		base.commonProperties.DebugMutators = append(base.commonProperties.DebugMutators, b.MutatorName())
+		base.commonProperties.DebugVariations = append(base.commonProperties.DebugVariations, variations[i])
+	}
+
+	return aModules
 }
 
 func (b *bottomUpMutatorContext) SetDependencyVariation(variation string) {
 	b.bp.SetDependencyVariation(variation)
 }
 
+func (b *bottomUpMutatorContext) SetDefaultDependencyVariation(variation *string) {
+	b.bp.SetDefaultDependencyVariation(variation)
+}
+
 func (b *bottomUpMutatorContext) AddVariationDependencies(variations []blueprint.Variation, tag blueprint.DependencyTag,
 	names ...string) {
 
@@ -278,3 +339,7 @@
 func (b *bottomUpMutatorContext) ReplaceDependencies(name string) {
 	b.bp.ReplaceDependencies(name)
 }
+
+func (b *bottomUpMutatorContext) AliasVariation(variationName string) {
+	b.bp.AliasVariation(variationName)
+}
diff --git a/android/mutator_test.go b/android/mutator_test.go
index 4cef400..d179f9d 100644
--- a/android/mutator_test.go
+++ b/android/mutator_test.go
@@ -15,8 +15,6 @@
 package android
 
 import (
-	"io/ioutil"
-	"os"
 	"reflect"
 	"testing"
 
@@ -26,6 +24,8 @@
 type mutatorTestModule struct {
 	ModuleBase
 	props struct {
+		Deps_missing_deps    []string
+		Mutator_missing_deps []string
 	}
 
 	missingDeps []string
@@ -48,44 +48,34 @@
 }
 
 func (m *mutatorTestModule) DepsMutator(ctx BottomUpMutatorContext) {
-	ctx.AddDependency(ctx.Module(), nil, "regular_missing_dep")
+	ctx.AddDependency(ctx.Module(), nil, m.props.Deps_missing_deps...)
 }
 
 func addMissingDependenciesMutator(ctx TopDownMutatorContext) {
-	ctx.AddMissingDependencies([]string{"added_missing_dep"})
+	ctx.AddMissingDependencies(ctx.Module().(*mutatorTestModule).props.Mutator_missing_deps)
 }
 
 func TestMutatorAddMissingDependencies(t *testing.T) {
-	buildDir, err := ioutil.TempDir("", "soong_mutator_test")
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer os.RemoveAll(buildDir)
+	bp := `
+		test {
+			name: "foo",
+			deps_missing_deps: ["regular_missing_dep"],
+			mutator_missing_deps: ["added_missing_dep"],
+		}
+	`
 
-	config := TestConfig(buildDir, nil)
+	config := TestConfig(buildDir, nil, bp, nil)
 	config.TestProductVariables.Allow_missing_dependencies = proptools.BoolPtr(true)
 
 	ctx := NewTestContext()
 	ctx.SetAllowMissingDependencies(true)
 
-	ctx.RegisterModuleType("test", ModuleFactoryAdaptor(mutatorTestModuleFactory))
+	ctx.RegisterModuleType("test", mutatorTestModuleFactory)
 	ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
 		ctx.TopDown("add_missing_dependencies", addMissingDependenciesMutator)
 	})
 
-	bp := `
-		test {
-			name: "foo",
-		}
-	`
-
-	mockFS := map[string][]byte{
-		"Android.bp": []byte(bp),
-	}
-
-	ctx.MockFileSystem(mockFS)
-
-	ctx.Register()
+	ctx.Register(config)
 	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
 	FailIfErrored(t, errs)
 	_, errs = ctx.PrepareBuildActions(config)
@@ -97,3 +87,101 @@
 		t.Errorf("want foo missing deps %q, got %q", w, g)
 	}
 }
+
+func TestModuleString(t *testing.T) {
+	ctx := NewTestContext()
+
+	var moduleStrings []string
+
+	ctx.PreArchMutators(func(ctx RegisterMutatorsContext) {
+		ctx.BottomUp("pre_arch", func(ctx BottomUpMutatorContext) {
+			moduleStrings = append(moduleStrings, ctx.Module().String())
+			ctx.CreateVariations("a", "b")
+		})
+		ctx.TopDown("rename_top_down", func(ctx TopDownMutatorContext) {
+			moduleStrings = append(moduleStrings, ctx.Module().String())
+			ctx.Rename(ctx.Module().base().Name() + "_renamed1")
+		})
+	})
+
+	ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
+		ctx.BottomUp("pre_deps", func(ctx BottomUpMutatorContext) {
+			moduleStrings = append(moduleStrings, ctx.Module().String())
+			ctx.CreateVariations("c", "d")
+		})
+	})
+
+	ctx.PostDepsMutators(func(ctx RegisterMutatorsContext) {
+		ctx.BottomUp("post_deps", func(ctx BottomUpMutatorContext) {
+			moduleStrings = append(moduleStrings, ctx.Module().String())
+			ctx.CreateLocalVariations("e", "f")
+		})
+		ctx.BottomUp("rename_bottom_up", func(ctx BottomUpMutatorContext) {
+			moduleStrings = append(moduleStrings, ctx.Module().String())
+			ctx.Rename(ctx.Module().base().Name() + "_renamed2")
+		})
+		ctx.BottomUp("final", func(ctx BottomUpMutatorContext) {
+			moduleStrings = append(moduleStrings, ctx.Module().String())
+		})
+	})
+
+	ctx.RegisterModuleType("test", mutatorTestModuleFactory)
+
+	bp := `
+		test {
+			name: "foo",
+		}
+	`
+
+	config := TestConfig(buildDir, nil, bp, nil)
+
+	ctx.Register(config)
+
+	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+	FailIfErrored(t, errs)
+	_, errs = ctx.PrepareBuildActions(config)
+	FailIfErrored(t, errs)
+
+	want := []string{
+		// Initial name.
+		"foo{}",
+
+		// After pre_arch (reversed because rename_top_down is TopDown so it visits in reverse order).
+		"foo{pre_arch:b}",
+		"foo{pre_arch:a}",
+
+		// After rename_top_down.
+		"foo_renamed1{pre_arch:a}",
+		"foo_renamed1{pre_arch:b}",
+
+		// After pre_deps.
+		"foo_renamed1{pre_arch:a,pre_deps:c}",
+		"foo_renamed1{pre_arch:a,pre_deps:d}",
+		"foo_renamed1{pre_arch:b,pre_deps:c}",
+		"foo_renamed1{pre_arch:b,pre_deps:d}",
+
+		// After post_deps.
+		"foo_renamed1{pre_arch:a,pre_deps:c,post_deps:e}",
+		"foo_renamed1{pre_arch:a,pre_deps:c,post_deps:f}",
+		"foo_renamed1{pre_arch:a,pre_deps:d,post_deps:e}",
+		"foo_renamed1{pre_arch:a,pre_deps:d,post_deps:f}",
+		"foo_renamed1{pre_arch:b,pre_deps:c,post_deps:e}",
+		"foo_renamed1{pre_arch:b,pre_deps:c,post_deps:f}",
+		"foo_renamed1{pre_arch:b,pre_deps:d,post_deps:e}",
+		"foo_renamed1{pre_arch:b,pre_deps:d,post_deps:f}",
+
+		// After rename_bottom_up.
+		"foo_renamed2{pre_arch:a,pre_deps:c,post_deps:e}",
+		"foo_renamed2{pre_arch:a,pre_deps:c,post_deps:f}",
+		"foo_renamed2{pre_arch:a,pre_deps:d,post_deps:e}",
+		"foo_renamed2{pre_arch:a,pre_deps:d,post_deps:f}",
+		"foo_renamed2{pre_arch:b,pre_deps:c,post_deps:e}",
+		"foo_renamed2{pre_arch:b,pre_deps:c,post_deps:f}",
+		"foo_renamed2{pre_arch:b,pre_deps:d,post_deps:e}",
+		"foo_renamed2{pre_arch:b,pre_deps:d,post_deps:f}",
+	}
+
+	if !reflect.DeepEqual(moduleStrings, want) {
+		t.Errorf("want module String() values:\n%q\ngot:\n%q", want, moduleStrings)
+	}
+}
diff --git a/android/namespace.go b/android/namespace.go
index 27ec163..64ad7e9 100644
--- a/android/namespace.go
+++ b/android/namespace.go
@@ -185,6 +185,7 @@
 	if ok {
 		// inform the module whether its namespace is one that we want to export to Make
 		amod.base().commonProperties.NamespaceExportedToMake = ns.exportToKati
+		amod.base().commonProperties.DebugName = module.Name()
 	}
 
 	return ns, nil
diff --git a/android/namespace_test.go b/android/namespace_test.go
index 20241fe..66c0d89 100644
--- a/android/namespace_test.go
+++ b/android/namespace_test.go
@@ -633,18 +633,17 @@
 }
 
 func setupTestFromFiles(bps map[string][]byte) (ctx *TestContext, errs []error) {
-	config := TestConfig(buildDir, nil)
+	config := TestConfig(buildDir, nil, "", bps)
 
 	ctx = NewTestContext()
-	ctx.MockFileSystem(bps)
-	ctx.RegisterModuleType("test_module", ModuleFactoryAdaptor(newTestModule))
-	ctx.RegisterModuleType("soong_namespace", ModuleFactoryAdaptor(NamespaceFactory))
-	ctx.RegisterModuleType("blueprint_test_module", newBlueprintTestModule)
+	ctx.RegisterModuleType("test_module", newTestModule)
+	ctx.RegisterModuleType("soong_namespace", NamespaceFactory)
+	ctx.Context.RegisterModuleType("blueprint_test_module", newBlueprintTestModule)
 	ctx.PreArchMutators(RegisterNamespaceMutator)
 	ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
 		ctx.BottomUp("rename", renameMutator)
 	})
-	ctx.Register()
+	ctx.Register(config)
 
 	_, errs = ctx.ParseBlueprintsFiles("Android.bp")
 	if len(errs) > 0 {
diff --git a/android/neverallow.go b/android/neverallow.go
index f35d1fe..cef73fb 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -31,61 +31,102 @@
 // work regardless of these restrictions.
 //
 // A module is disallowed if all of the following are true:
-// - it is in one of the "in" paths
-// - it is not in one of the "notIn" paths
-// - it has all "with" properties matched
+// - it is in one of the "In" paths
+// - it is not in one of the "NotIn" paths
+// - it has all "With" properties matched
 // - - values are matched in their entirety
 // - - nil is interpreted as an empty string
 // - - nested properties are separated with a '.'
 // - - if the property is a list, any of the values in the list being matches
 //     counts as a match
-// - it has none of the "without" properties matched (same rules as above)
+// - it has none of the "Without" properties matched (same rules as above)
 
 func registerNeverallowMutator(ctx RegisterMutatorsContext) {
 	ctx.BottomUp("neverallow", neverallowMutator).Parallel()
 }
 
-var neverallows = createNeverAllows()
+var neverallows = []Rule{}
 
-func createNeverAllows() []*rule {
-	rules := []*rule{}
-	rules = append(rules, createTrebleRules()...)
-	rules = append(rules, createLibcoreRules()...)
-	rules = append(rules, createJavaDeviceForHostRules()...)
+func init() {
+	AddNeverAllowRules(createIncludeDirsRules()...)
+	AddNeverAllowRules(createTrebleRules()...)
+	AddNeverAllowRules(createLibcoreRules()...)
+	AddNeverAllowRules(createMediaRules()...)
+	AddNeverAllowRules(createJavaDeviceForHostRules()...)
+}
+
+// Add a NeverAllow rule to the set of rules to apply.
+func AddNeverAllowRules(rules ...Rule) {
+	neverallows = append(neverallows, rules...)
+}
+
+func createIncludeDirsRules() []Rule {
+	// The list of paths that cannot be referenced using include_dirs
+	paths := []string{
+		"art",
+		"art/libnativebridge",
+		"art/libnativeloader",
+		"libcore",
+		"libnativehelper",
+		"external/apache-harmony",
+		"external/apache-xml",
+		"external/boringssl",
+		"external/bouncycastle",
+		"external/conscrypt",
+		"external/icu",
+		"external/okhttp",
+		"external/vixl",
+		"external/wycheproof",
+	}
+
+	// Create a composite matcher that will match if the value starts with any of the restricted
+	// paths. A / is appended to the prefix to ensure that restricting path X does not affect paths
+	// XY.
+	rules := make([]Rule, 0, len(paths))
+	for _, path := range paths {
+		rule :=
+			NeverAllow().
+				WithMatcher("include_dirs", StartsWith(path+"/")).
+				Because("include_dirs is deprecated, all usages of '" + path + "' have been migrated" +
+					" to use alternate mechanisms and so can no longer be used.")
+
+		rules = append(rules, rule)
+	}
+
 	return rules
 }
 
-func createTrebleRules() []*rule {
-	return []*rule{
-		neverallow().
-			in("vendor", "device").
-			with("vndk.enabled", "true").
-			without("vendor", "true").
-			because("the VNDK can never contain a library that is device dependent."),
-		neverallow().
-			with("vndk.enabled", "true").
-			without("vendor", "true").
-			without("owner", "").
-			because("a VNDK module can never have an owner."),
+func createTrebleRules() []Rule {
+	return []Rule{
+		NeverAllow().
+			In("vendor", "device").
+			With("vndk.enabled", "true").
+			Without("vendor", "true").
+			Because("the VNDK can never contain a library that is device dependent."),
+		NeverAllow().
+			With("vndk.enabled", "true").
+			Without("vendor", "true").
+			Without("owner", "").
+			Because("a VNDK module can never have an owner."),
 
 		// TODO(b/67974785): always enforce the manifest
-		neverallow().
-			without("name", "libhidltransport").
-			with("product_variables.enforce_vintf_manifest.cflags", "*").
-			because("manifest enforcement should be independent of ."),
+		NeverAllow().
+			Without("name", "libhidltransport-impl-internal").
+			With("product_variables.enforce_vintf_manifest.cflags", "*").
+			Because("manifest enforcement should be independent of ."),
 
 		// TODO(b/67975799): vendor code should always use /vendor/bin/sh
-		neverallow().
-			without("name", "libc_bionic_ndk").
-			with("product_variables.treble_linker_namespaces.cflags", "*").
-			because("nothing should care if linker namespaces are enabled or not"),
+		NeverAllow().
+			Without("name", "libc_bionic_ndk").
+			With("product_variables.treble_linker_namespaces.cflags", "*").
+			Because("nothing should care if linker namespaces are enabled or not"),
 
 		// Example:
-		// *neverallow().with("Srcs", "main.cpp"))
+		// *NeverAllow().with("Srcs", "main.cpp"))
 	}
 }
 
-func createLibcoreRules() []*rule {
+func createLibcoreRules() []Rule {
 	var coreLibraryProjects = []string{
 		"libcore",
 		"external/apache-harmony",
@@ -95,34 +136,39 @@
 		"external/icu",
 		"external/okhttp",
 		"external/wycheproof",
-
-		// Not really a core library but still needs access to same capabilities.
-		"development",
 	}
 
 	// Core library constraints. The sdk_version: "none" can only be used in core library projects.
 	// Access to core library targets is restricted using visibility rules.
-	rules := []*rule{
-		neverallow().
-			notIn(coreLibraryProjects...).
-			with("sdk_version", "none"),
+	rules := []Rule{
+		NeverAllow().
+			NotIn(coreLibraryProjects...).
+			With("sdk_version", "none"),
 	}
 
 	return rules
 }
 
-func createJavaDeviceForHostRules() []*rule {
+func createMediaRules() []Rule {
+	return []Rule{
+		NeverAllow().
+			With("libs", "updatable-media").
+			Because("updatable-media includes private APIs. Use updatable_media_stubs instead."),
+	}
+}
+
+func createJavaDeviceForHostRules() []Rule {
 	javaDeviceForHostProjectsWhitelist := []string{
 		"external/guava",
 		"external/robolectric-shadows",
 		"framework/layoutlib",
 	}
 
-	return []*rule{
-		neverallow().
-			notIn(javaDeviceForHostProjectsWhitelist...).
-			moduleType("java_device_for_host", "java_host_for_device").
-			because("java_device_for_host can only be used in whitelisted projects"),
+	return []Rule{
+		NeverAllow().
+			NotIn(javaDeviceForHostProjectsWhitelist...).
+			ModuleType("java_device_for_host", "java_host_for_device").
+			Because("java_device_for_host can only be used in whitelisted projects"),
 	}
 }
 
@@ -135,7 +181,10 @@
 	dir := ctx.ModuleDir() + "/"
 	properties := m.GetProperties()
 
-	for _, n := range neverallows {
+	osClass := ctx.Module().Target().Os.Class
+
+	for _, r := range neverallowRules(ctx.Config()) {
+		n := r.(*rule)
 		if !n.appliesToPath(dir) {
 			continue
 		}
@@ -148,13 +197,88 @@
 			continue
 		}
 
+		if !n.appliesToOsClass(osClass) {
+			continue
+		}
+
+		if !n.appliesToDirectDeps(ctx) {
+			continue
+		}
+
 		ctx.ModuleErrorf("violates " + n.String())
 	}
 }
 
+type ValueMatcher interface {
+	test(string) bool
+	String() string
+}
+
+type equalMatcher struct {
+	expected string
+}
+
+func (m *equalMatcher) test(value string) bool {
+	return m.expected == value
+}
+
+func (m *equalMatcher) String() string {
+	return "=" + m.expected
+}
+
+type anyMatcher struct {
+}
+
+func (m *anyMatcher) test(value string) bool {
+	return true
+}
+
+func (m *anyMatcher) String() string {
+	return "=*"
+}
+
+var anyMatcherInstance = &anyMatcher{}
+
+type startsWithMatcher struct {
+	prefix string
+}
+
+func (m *startsWithMatcher) test(value string) bool {
+	return strings.HasPrefix(value, m.prefix)
+}
+
+func (m *startsWithMatcher) String() string {
+	return ".starts-with(" + m.prefix + ")"
+}
+
 type ruleProperty struct {
-	fields []string // e.x.: Vndk.Enabled
-	value  string   // e.x.: true
+	fields  []string // e.x.: Vndk.Enabled
+	matcher ValueMatcher
+}
+
+// A NeverAllow rule.
+type Rule interface {
+	In(path ...string) Rule
+
+	NotIn(path ...string) Rule
+
+	InDirectDeps(deps ...string) Rule
+
+	WithOsClass(osClasses ...OsClass) Rule
+
+	ModuleType(types ...string) Rule
+
+	NotModuleType(types ...string) Rule
+
+	With(properties, value string) Rule
+
+	WithMatcher(properties string, matcher ValueMatcher) Rule
+
+	Without(properties, value string) Rule
+
+	WithoutMatcher(properties string, matcher ValueMatcher) Rule
+
+	Because(reason string) Rule
 }
 
 type rule struct {
@@ -164,6 +288,10 @@
 	paths       []string
 	unlessPaths []string
 
+	directDeps map[string]bool
+
+	osClasses []OsClass
+
 	moduleTypes       []string
 	unlessModuleTypes []string
 
@@ -171,47 +299,75 @@
 	unlessProps []ruleProperty
 }
 
-func neverallow() *rule {
-	return &rule{}
+// Create a new NeverAllow rule.
+func NeverAllow() Rule {
+	return &rule{directDeps: make(map[string]bool)}
 }
 
-func (r *rule) in(path ...string) *rule {
+func (r *rule) In(path ...string) Rule {
 	r.paths = append(r.paths, cleanPaths(path)...)
 	return r
 }
 
-func (r *rule) notIn(path ...string) *rule {
+func (r *rule) NotIn(path ...string) Rule {
 	r.unlessPaths = append(r.unlessPaths, cleanPaths(path)...)
 	return r
 }
 
-func (r *rule) moduleType(types ...string) *rule {
+func (r *rule) InDirectDeps(deps ...string) Rule {
+	for _, d := range deps {
+		r.directDeps[d] = true
+	}
+	return r
+}
+
+func (r *rule) WithOsClass(osClasses ...OsClass) Rule {
+	r.osClasses = append(r.osClasses, osClasses...)
+	return r
+}
+
+func (r *rule) ModuleType(types ...string) Rule {
 	r.moduleTypes = append(r.moduleTypes, types...)
 	return r
 }
 
-func (r *rule) notModuleType(types ...string) *rule {
+func (r *rule) NotModuleType(types ...string) Rule {
 	r.unlessModuleTypes = append(r.unlessModuleTypes, types...)
 	return r
 }
 
-func (r *rule) with(properties, value string) *rule {
+func (r *rule) With(properties, value string) Rule {
+	return r.WithMatcher(properties, selectMatcher(value))
+}
+
+func (r *rule) WithMatcher(properties string, matcher ValueMatcher) Rule {
 	r.props = append(r.props, ruleProperty{
-		fields: fieldNamesForProperties(properties),
-		value:  value,
+		fields:  fieldNamesForProperties(properties),
+		matcher: matcher,
 	})
 	return r
 }
 
-func (r *rule) without(properties, value string) *rule {
+func (r *rule) Without(properties, value string) Rule {
+	return r.WithoutMatcher(properties, selectMatcher(value))
+}
+
+func (r *rule) WithoutMatcher(properties string, matcher ValueMatcher) Rule {
 	r.unlessProps = append(r.unlessProps, ruleProperty{
-		fields: fieldNamesForProperties(properties),
-		value:  value,
+		fields:  fieldNamesForProperties(properties),
+		matcher: matcher,
 	})
 	return r
 }
 
-func (r *rule) because(reason string) *rule {
+func selectMatcher(expected string) ValueMatcher {
+	if expected == "*" {
+		return anyMatcherInstance
+	}
+	return &equalMatcher{expected: expected}
+}
+
+func (r *rule) Because(reason string) Rule {
 	r.reason = reason
 	return r
 }
@@ -231,10 +387,16 @@
 		s += " -type:" + v
 	}
 	for _, v := range r.props {
-		s += " " + strings.Join(v.fields, ".") + "=" + v.value
+		s += " " + strings.Join(v.fields, ".") + v.matcher.String()
 	}
 	for _, v := range r.unlessProps {
-		s += " -" + strings.Join(v.fields, ".") + "=" + v.value
+		s += " -" + strings.Join(v.fields, ".") + v.matcher.String()
+	}
+	for k := range r.directDeps {
+		s += " deps:" + k
+	}
+	for _, v := range r.osClasses {
+		s += " os:" + v.String()
 	}
 	if len(r.reason) != 0 {
 		s += " which is restricted because " + r.reason
@@ -248,6 +410,36 @@
 	return includePath && !excludePath
 }
 
+func (r *rule) appliesToDirectDeps(ctx BottomUpMutatorContext) bool {
+	if len(r.directDeps) == 0 {
+		return true
+	}
+
+	matches := false
+	ctx.VisitDirectDeps(func(m Module) {
+		if !matches {
+			name := ctx.OtherModuleName(m)
+			matches = r.directDeps[name]
+		}
+	})
+
+	return matches
+}
+
+func (r *rule) appliesToOsClass(osClass OsClass) bool {
+	if len(r.osClasses) == 0 {
+		return true
+	}
+
+	for _, c := range r.osClasses {
+		if c == osClass {
+			return true
+		}
+	}
+
+	return false
+}
+
 func (r *rule) appliesToModuleType(moduleType string) bool {
 	return (len(r.moduleTypes) == 0 || InList(moduleType, r.moduleTypes)) && !InList(moduleType, r.unlessModuleTypes)
 }
@@ -258,6 +450,10 @@
 	return includeProps && !excludeProps
 }
 
+func StartsWith(prefix string) ValueMatcher {
+	return &startsWithMatcher{prefix}
+}
+
 // assorted utils
 
 func cleanPaths(paths []string) []string {
@@ -316,8 +512,8 @@
 			continue
 		}
 
-		check := func(v string) bool {
-			return prop.value == "*" || prop.value == v
+		check := func(value string) bool {
+			return prop.matcher.test(value)
 		}
 
 		if matchValue(propertiesValue, check) {
@@ -361,3 +557,19 @@
 
 	panic("Can't handle type: " + value.Kind().String())
 }
+
+var neverallowRulesKey = NewOnceKey("neverallowRules")
+
+func neverallowRules(config Config) []Rule {
+	return config.Once(neverallowRulesKey, func() interface{} {
+		// No test rules were set by setTestNeverallowRules, use the global rules
+		return neverallows
+	}).([]Rule)
+}
+
+// Overrides the default neverallow rules for the supplied config.
+//
+// For testing only.
+func setTestNeverallowRules(config Config, testRules []Rule) {
+	config.Once(neverallowRulesKey, func() interface{} { return testRules })
+}
diff --git a/android/neverallow_test.go b/android/neverallow_test.go
index ee3c94f..6f07a4a 100644
--- a/android/neverallow_test.go
+++ b/android/neverallow_test.go
@@ -16,17 +16,80 @@
 
 import (
 	"testing"
+
+	"github.com/google/blueprint"
 )
 
 var neverallowTests = []struct {
-	name          string
-	fs            map[string][]byte
-	expectedError string
+	// The name of the test.
+	name string
+
+	// Optional test specific rules. If specified then they are used instead of the default rules.
+	rules []Rule
+
+	// Additional contents to add to the virtual filesystem used by the tests.
+	fs map[string][]byte
+
+	// The expected error patterns. If empty then no errors are expected, otherwise each error
+	// reported must be matched by at least one of these patterns. A pattern matches if the error
+	// message contains the pattern. A pattern does not have to match the whole error message.
+	expectedErrors []string
 }{
+	// Test General Functionality
+
+	// in direct deps tests
+	{
+		name: "not_allowed_in_direct_deps",
+		rules: []Rule{
+			NeverAllow().InDirectDeps("not_allowed_in_direct_deps"),
+		},
+		fs: map[string][]byte{
+			"top/Android.bp": []byte(`
+				cc_library {
+					name: "not_allowed_in_direct_deps",
+				}`),
+			"other/Android.bp": []byte(`
+				cc_library {
+					name: "libother",
+					static_libs: ["not_allowed_in_direct_deps"],
+				}`),
+		},
+		expectedErrors: []string{
+			`module "libother": violates neverallow deps:not_allowed_in_direct_deps`,
+		},
+	},
+
+	// Test android specific rules
+
+	// include_dir rule tests
+	{
+		name: "include_dir not allowed to reference art",
+		fs: map[string][]byte{
+			"other/Android.bp": []byte(`
+				cc_library {
+					name: "libother",
+					include_dirs: ["art/libdexfile/include"],
+				}`),
+		},
+		expectedErrors: []string{
+			"all usages of 'art' have been migrated",
+		},
+	},
+	{
+		name: "include_dir can reference another location",
+		fs: map[string][]byte{
+			"other/Android.bp": []byte(`
+				cc_library {
+					name: "libother",
+					include_dirs: ["another/include"],
+				}`),
+		},
+	},
+	// Treble rule tests
 	{
 		name: "no vndk.enabled under vendor directory",
 		fs: map[string][]byte{
-			"vendor/Blueprints": []byte(`
+			"vendor/Android.bp": []byte(`
 				cc_library {
 					name: "libvndk",
 					vendor_available: true,
@@ -35,12 +98,14 @@
 					},
 				}`),
 		},
-		expectedError: "VNDK can never contain a library that is device dependent",
+		expectedErrors: []string{
+			"VNDK can never contain a library that is device dependent",
+		},
 	},
 	{
 		name: "no vndk.enabled under device directory",
 		fs: map[string][]byte{
-			"device/Blueprints": []byte(`
+			"device/Android.bp": []byte(`
 				cc_library {
 					name: "libvndk",
 					vendor_available: true,
@@ -49,12 +114,14 @@
 					},
 				}`),
 		},
-		expectedError: "VNDK can never contain a library that is device dependent",
+		expectedErrors: []string{
+			"VNDK can never contain a library that is device dependent",
+		},
 	},
 	{
 		name: "vndk-ext under vendor or device directory",
 		fs: map[string][]byte{
-			"device/Blueprints": []byte(`
+			"device/Android.bp": []byte(`
 				cc_library {
 					name: "libvndk1_ext",
 					vendor: true,
@@ -62,7 +129,7 @@
 						enabled: true,
 					},
 				}`),
-			"vendor/Blueprints": []byte(`
+			"vendor/Android.bp": []byte(`
 				cc_library {
 					name: "libvndk2_ext",
 					vendor: true,
@@ -71,13 +138,12 @@
 					},
 				}`),
 		},
-		expectedError: "",
 	},
 
 	{
 		name: "no enforce_vintf_manifest.cflags",
 		fs: map[string][]byte{
-			"Blueprints": []byte(`
+			"Android.bp": []byte(`
 				cc_library {
 					name: "libexample",
 					product_variables: {
@@ -87,28 +153,15 @@
 					},
 				}`),
 		},
-		expectedError: "manifest enforcement should be independent",
-	},
-	{
-		name: "libhidltransport enforce_vintf_manifest.cflags",
-		fs: map[string][]byte{
-			"Blueprints": []byte(`
-				cc_library {
-					name: "libhidltransport",
-					product_variables: {
-						enforce_vintf_manifest: {
-							cflags: ["-DSHOULD_NOT_EXIST"],
-						},
-					},
-				}`),
+		expectedErrors: []string{
+			"manifest enforcement should be independent",
 		},
-		expectedError: "",
 	},
 
 	{
 		name: "no treble_linker_namespaces.cflags",
 		fs: map[string][]byte{
-			"Blueprints": []byte(`
+			"Android.bp": []byte(`
 				cc_library {
 					name: "libexample",
 					product_variables: {
@@ -118,12 +171,14 @@
 					},
 				}`),
 		},
-		expectedError: "nothing should care if linker namespaces are enabled or not",
+		expectedErrors: []string{
+			"nothing should care if linker namespaces are enabled or not",
+		},
 	},
 	{
 		name: "libc_bionic_ndk treble_linker_namespaces.cflags",
 		fs: map[string][]byte{
-			"Blueprints": []byte(`
+			"Android.bp": []byte(`
 				cc_library {
 					name: "libc_bionic_ndk",
 					product_variables: {
@@ -133,24 +188,38 @@
 					},
 				}`),
 		},
-		expectedError: "",
+	},
+	{
+		name: "dependency on updatable-media",
+		fs: map[string][]byte{
+			"Android.bp": []byte(`
+				java_library {
+					name: "needs_updatable_media",
+					libs: ["updatable-media"],
+				}`),
+		},
+		expectedErrors: []string{
+			"updatable-media includes private APIs. Use updatable_media_stubs instead.",
+		},
 	},
 	{
 		name: "java_device_for_host",
 		fs: map[string][]byte{
-			"Blueprints": []byte(`
+			"Android.bp": []byte(`
 				java_device_for_host {
 					name: "device_for_host",
 					libs: ["core-libart"],
 				}`),
 		},
-		expectedError: "java_device_for_host can only be used in whitelisted projects",
+		expectedErrors: []string{
+			"java_device_for_host can only be used in whitelisted projects",
+		},
 	},
 	// Libcore rule tests
 	{
 		name: "sdk_version: \"none\" inside core libraries",
 		fs: map[string][]byte{
-			"libcore/Blueprints": []byte(`
+			"libcore/Android.bp": []byte(`
 				java_library {
 					name: "inside_core_libraries",
 					sdk_version: "none",
@@ -160,18 +229,20 @@
 	{
 		name: "sdk_version: \"none\" outside core libraries",
 		fs: map[string][]byte{
-			"Blueprints": []byte(`
+			"Android.bp": []byte(`
 				java_library {
 					name: "outside_core_libraries",
 					sdk_version: "none",
 				}`),
 		},
-		expectedError: "module \"outside_core_libraries\": violates neverallow",
+		expectedErrors: []string{
+			"module \"outside_core_libraries\": violates neverallow",
+		},
 	},
 	{
 		name: "sdk_version: \"current\"",
 		fs: map[string][]byte{
-			"Blueprints": []byte(`
+			"Android.bp": []byte(`
 				java_library {
 					name: "outside_core_libraries",
 					sdk_version: "current",
@@ -181,33 +252,31 @@
 }
 
 func TestNeverallow(t *testing.T) {
-	config := TestConfig(buildDir, nil)
-
 	for _, test := range neverallowTests {
-		t.Run(test.name, func(t *testing.T) {
-			_, errs := testNeverallow(t, config, test.fs)
+		// Create a test per config to allow for test specific config, e.g. test rules.
+		config := TestConfig(buildDir, nil, "", test.fs)
 
-			if test.expectedError == "" {
-				FailIfErrored(t, errs)
-			} else {
-				FailIfNoMatchingErrors(t, test.expectedError, errs)
+		t.Run(test.name, func(t *testing.T) {
+			// If the test has its own rules then use them instead of the default ones.
+			if test.rules != nil {
+				setTestNeverallowRules(config, test.rules)
 			}
+			_, errs := testNeverallow(config)
+			CheckErrorsAgainstExpectations(t, errs, test.expectedErrors)
 		})
 	}
 }
 
-func testNeverallow(t *testing.T, config Config, fs map[string][]byte) (*TestContext, []error) {
+func testNeverallow(config Config) (*TestContext, []error) {
 	ctx := NewTestContext()
-	ctx.RegisterModuleType("cc_library", ModuleFactoryAdaptor(newMockCcLibraryModule))
-	ctx.RegisterModuleType("java_library", ModuleFactoryAdaptor(newMockJavaLibraryModule))
-	ctx.RegisterModuleType("java_library_host", ModuleFactoryAdaptor(newMockJavaLibraryModule))
-	ctx.RegisterModuleType("java_device_for_host", ModuleFactoryAdaptor(newMockJavaLibraryModule))
+	ctx.RegisterModuleType("cc_library", newMockCcLibraryModule)
+	ctx.RegisterModuleType("java_library", newMockJavaLibraryModule)
+	ctx.RegisterModuleType("java_library_host", newMockJavaLibraryModule)
+	ctx.RegisterModuleType("java_device_for_host", newMockJavaLibraryModule)
 	ctx.PostDepsMutators(registerNeverallowMutator)
-	ctx.Register()
+	ctx.Register(config)
 
-	ctx.MockFileSystem(fs)
-
-	_, errs := ctx.ParseBlueprintsFiles("Blueprints")
+	_, errs := ctx.ParseBlueprintsFiles("Android.bp")
 	if len(errs) > 0 {
 		return ctx, errs
 	}
@@ -217,7 +286,9 @@
 }
 
 type mockCcLibraryProperties struct {
+	Include_dirs     []string
 	Vendor_available *bool
+	Static_libs      []string
 
 	Vndk struct {
 		Enabled                *bool
@@ -248,6 +319,19 @@
 	return m
 }
 
+type neverallowTestDependencyTag struct {
+	blueprint.BaseDependencyTag
+	name string
+}
+
+var staticDepTag = neverallowTestDependencyTag{name: "static"}
+
+func (c *mockCcLibraryModule) DepsMutator(ctx BottomUpMutatorContext) {
+	for _, lib := range c.properties.Static_libs {
+		ctx.AddDependency(ctx.Module(), staticDepTag, lib)
+	}
+}
+
 func (p *mockCcLibraryModule) GenerateAndroidBuildActions(ModuleContext) {
 }
 
diff --git a/android/notices.go b/android/notices.go
new file mode 100644
index 0000000..bf273b5
--- /dev/null
+++ b/android/notices.go
@@ -0,0 +1,102 @@
+// 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 (
+	"path/filepath"
+
+	"github.com/google/blueprint"
+)
+
+func init() {
+	pctx.SourcePathVariable("merge_notices", "build/soong/scripts/mergenotice.py")
+	pctx.SourcePathVariable("generate_notice", "build/make/tools/generate-notice-files.py")
+
+	pctx.HostBinToolVariable("minigzip", "minigzip")
+}
+
+type NoticeOutputs struct {
+	Merged       OptionalPath
+	TxtOutput    OptionalPath
+	HtmlOutput   OptionalPath
+	HtmlGzOutput OptionalPath
+}
+
+var (
+	mergeNoticesRule = pctx.AndroidStaticRule("mergeNoticesRule", blueprint.RuleParams{
+		Command:     `${merge_notices} --output $out $in`,
+		CommandDeps: []string{"${merge_notices}"},
+		Description: "merge notice files into $out",
+	})
+
+	generateNoticeRule = pctx.AndroidStaticRule("generateNoticeRule", blueprint.RuleParams{
+		Command: `rm -rf $$(dirname $txtOut) $$(dirname $htmlOut) $$(dirname $out) && ` +
+			`mkdir -p $$(dirname $txtOut) $$(dirname $htmlOut)  $$(dirname $out) && ` +
+			`${generate_notice} --text-output $txtOut --html-output $htmlOut -t "$title" -s $inputDir && ` +
+			`${minigzip} -c $htmlOut > $out`,
+		CommandDeps: []string{"${generate_notice}", "${minigzip}"},
+		Description: "produce notice file $out",
+	}, "txtOut", "htmlOut", "title", "inputDir")
+)
+
+func MergeNotices(ctx ModuleContext, mergedNotice WritablePath, noticePaths []Path) {
+	ctx.Build(pctx, BuildParams{
+		Rule:        mergeNoticesRule,
+		Description: "merge notices",
+		Inputs:      noticePaths,
+		Output:      mergedNotice,
+	})
+}
+
+func BuildNoticeOutput(ctx ModuleContext, installPath InstallPath, installFilename string,
+	noticePaths []Path) NoticeOutputs {
+	// Merge all NOTICE files into one.
+	// TODO(jungjw): We should just produce a well-formatted NOTICE.html file in a single pass.
+	//
+	// generate-notice-files.py, which processes the merged NOTICE file, has somewhat strict rules
+	// about input NOTICE file paths.
+	// 1. Their relative paths to the src root become their NOTICE index titles. We want to use
+	// on-device paths as titles, and so output the merged NOTICE file the corresponding location.
+	// 2. They must end with .txt extension. Otherwise, they're ignored.
+	noticeRelPath := InstallPathToOnDevicePath(ctx, installPath.Join(ctx, installFilename+".txt"))
+	mergedNotice := PathForModuleOut(ctx, filepath.Join("NOTICE_FILES/src", noticeRelPath))
+	MergeNotices(ctx, mergedNotice, noticePaths)
+
+	// Transform the merged NOTICE file into a gzipped HTML file.
+	txtOuptut := PathForModuleOut(ctx, "NOTICE_txt", "NOTICE.txt")
+	htmlOutput := PathForModuleOut(ctx, "NOTICE_html", "NOTICE.html")
+	htmlGzOutput := PathForModuleOut(ctx, "NOTICE", "NOTICE.html.gz")
+	title := "Notices for " + ctx.ModuleName()
+	ctx.Build(pctx, BuildParams{
+		Rule:            generateNoticeRule,
+		Description:     "generate notice output",
+		Input:           mergedNotice,
+		Output:          htmlGzOutput,
+		ImplicitOutputs: WritablePaths{txtOuptut, htmlOutput},
+		Args: map[string]string{
+			"txtOut":   txtOuptut.String(),
+			"htmlOut":  htmlOutput.String(),
+			"title":    title,
+			"inputDir": PathForModuleOut(ctx, "NOTICE_FILES/src").String(),
+		},
+	})
+
+	return NoticeOutputs{
+		Merged:       OptionalPathForPath(mergedNotice),
+		TxtOutput:    OptionalPathForPath(txtOuptut),
+		HtmlOutput:   OptionalPathForPath(htmlOutput),
+		HtmlGzOutput: OptionalPathForPath(htmlGzOutput),
+	}
+}
diff --git a/android/onceper.go b/android/onceper.go
index ff865c2..481cdea 100644
--- a/android/onceper.go
+++ b/android/onceper.go
@@ -95,6 +95,16 @@
 	return s[0], s[1]
 }
 
+// OncePath is the same as Once, but returns the value cast to a Path
+func (once *OncePer) OncePath(key OnceKey, value func() Path) Path {
+	return once.Once(key, func() interface{} { return value() }).(Path)
+}
+
+// OncePath is the same as Once, but returns the value cast to a SourcePath
+func (once *OncePer) OnceSourcePath(key OnceKey, value func() SourcePath) SourcePath {
+	return once.Once(key, func() interface{} { return value() }).(SourcePath)
+}
+
 // OnceKey is an opaque type to be used as the key in calls to Once.
 type OnceKey struct {
 	key interface{}
diff --git a/android/override_module.go b/android/override_module.go
index 5a57c93..9f5127d 100644
--- a/android/override_module.go
+++ b/android/override_module.go
@@ -70,6 +70,10 @@
 	return &o.moduleProperties
 }
 
+func (o *OverrideModuleBase) GetOverriddenModuleName() string {
+	return proptools.String(o.moduleProperties.Base)
+}
+
 func InitOverrideModule(m OverrideModule) {
 	m.setOverridingProperties(m.GetProperties())
 
@@ -78,13 +82,16 @@
 
 // Interface for overridable module types, e.g. android_app, apex
 type OverridableModule interface {
+	Module
+	moduleBase() *OverridableModuleBase
+
 	setOverridableProperties(prop []interface{})
 
 	addOverride(o OverrideModule)
 	getOverrides() []OverrideModule
 
 	override(ctx BaseModuleContext, o OverrideModule)
-	getOverriddenBy() string
+	GetOverriddenBy() string
 
 	setOverridesProperty(overridesProperties *[]string)
 
@@ -93,10 +100,12 @@
 	OverridablePropertiesDepsMutator(ctx BottomUpMutatorContext)
 }
 
+type overridableModuleProperties struct {
+	OverriddenBy string `blueprint:"mutated"`
+}
+
 // Base module struct for overridable module types
 type OverridableModuleBase struct {
-	ModuleBase
-
 	// List of OverrideModules that override this base module
 	overrides []OverrideModule
 	// Used to parallelize registerOverrideMutator executions. Note that only addOverride locks this
@@ -112,12 +121,17 @@
 	// override information is propagated and aggregated correctly.
 	overridesProperty *[]string
 
-	overriddenBy string
+	overridableModuleProperties overridableModuleProperties
 }
 
 func InitOverridableModule(m OverridableModule, overridesProperty *[]string) {
 	m.setOverridableProperties(m.(Module).GetProperties())
 	m.setOverridesProperty(overridesProperty)
+	m.AddProperties(&m.moduleBase().overridableModuleProperties)
+}
+
+func (o *OverridableModuleBase) moduleBase() *OverridableModuleBase {
+	return o
 }
 
 func (b *OverridableModuleBase) setOverridableProperties(prop []interface{}) {
@@ -141,15 +155,10 @@
 
 // Overrides a base module with the given OverrideModule.
 func (b *OverridableModuleBase) override(ctx BaseModuleContext, o OverrideModule) {
-	// Adds the base module to the overrides property, if exists, of the overriding module. See the
-	// comment on OverridableModuleBase.overridesProperty for details.
-	if b.overridesProperty != nil {
-		*b.overridesProperty = append(*b.overridesProperty, b.Name())
-	}
 	for _, p := range b.overridableProperties {
 		for _, op := range o.getOverridingProperties() {
 			if proptools.TypeEqual(p, op) {
-				err := proptools.AppendProperties(p, op, nil)
+				err := proptools.ExtendProperties(p, op, nil, proptools.OrderReplace)
 				if err != nil {
 					if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
 						ctx.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
@@ -160,11 +169,20 @@
 			}
 		}
 	}
-	b.overriddenBy = o.Name()
+	// Adds the base module to the overrides property, if exists, of the overriding module. See the
+	// comment on OverridableModuleBase.overridesProperty for details.
+	if b.overridesProperty != nil {
+		*b.overridesProperty = append(*b.overridesProperty, ctx.ModuleName())
+	}
+	b.overridableModuleProperties.OverriddenBy = o.Name()
 }
 
-func (b *OverridableModuleBase) getOverriddenBy() string {
-	return b.overriddenBy
+// GetOverriddenBy returns the name of the override module that has overridden this module.
+// For example, if an override module foo has its 'base' property set to bar, then another local variant
+// of bar is created and its properties are overriden by foo. This method returns bar when called from
+// the new local variant. It returns "" when called from the original variant of bar.
+func (b *OverridableModuleBase) GetOverriddenBy() string {
+	return b.overridableModuleProperties.OverriddenBy
 }
 
 func (b *OverridableModuleBase) OverridablePropertiesDepsMutator(ctx BottomUpMutatorContext) {
@@ -177,6 +195,7 @@
 	ctx.TopDown("register_override", registerOverrideMutator).Parallel()
 	ctx.BottomUp("perform_override", performOverrideMutator).Parallel()
 	ctx.BottomUp("overridable_deps", overridableModuleDepsMutator).Parallel()
+	ctx.BottomUp("replace_deps_on_override", replaceDepsOnOverridingModuleMutator).Parallel()
 }
 
 type overrideBaseDependencyTag struct {
@@ -220,6 +239,9 @@
 			variants[i+1] = o.(Module).Name()
 		}
 		mods := ctx.CreateLocalVariations(variants...)
+		// Make the original variation the default one to depend on if no other override module variant
+		// is specified.
+		ctx.AliasVariation(variants[0])
 		for i, o := range overrides {
 			mods[i+1].(OverridableModule).override(ctx, o)
 		}
@@ -228,17 +250,24 @@
 		// variant name rule for overridden modules, and thus allows ReplaceDependencies to match the
 		// two.
 		ctx.CreateLocalVariations(o.Name())
+		// To allow dependencies to be added without having to know the above variation.
+		ctx.AliasVariation(o.Name())
 	}
 }
 
 func overridableModuleDepsMutator(ctx BottomUpMutatorContext) {
 	if b, ok := ctx.Module().(OverridableModule); ok {
-		if o := b.getOverriddenBy(); o != "" {
+		b.OverridablePropertiesDepsMutator(ctx)
+	}
+}
+
+func replaceDepsOnOverridingModuleMutator(ctx BottomUpMutatorContext) {
+	if b, ok := ctx.Module().(OverridableModule); ok {
+		if o := b.GetOverriddenBy(); o != "" {
 			// Redirect dependencies on the overriding module to this overridden module. Overriding
 			// modules are basically pseudo modules, and all build actions are associated to overridden
 			// modules. Therefore, dependencies on overriding modules need to be forwarded there as well.
 			ctx.ReplaceDependencies(o)
 		}
-		b.OverridablePropertiesDepsMutator(ctx)
 	}
 }
diff --git a/android/package.go b/android/package.go
new file mode 100644
index 0000000..ed604c6
--- /dev/null
+++ b/android/package.go
@@ -0,0 +1,191 @@
+// 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"
+	"sync/atomic"
+
+	"github.com/google/blueprint"
+)
+
+func init() {
+	RegisterModuleType("package", PackageFactory)
+}
+
+// The information maintained about each package.
+type packageInfo struct {
+	// The module from which this information was populated. If `duplicated` = true then this is the
+	// module that has been renamed and must be used to report errors.
+	module *packageModule
+
+	// If true this indicates that there are two package statements in the same package which is not
+	// allowed and will cause the build to fail. This flag is set by packageRenamer and checked in
+	// packageErrorReporter
+	duplicated bool
+}
+
+type packageProperties struct {
+	Name string `blueprint:"mutated"`
+
+	// Specifies the default visibility for all modules defined in this package.
+	Default_visibility []string
+}
+
+type packageModule struct {
+	ModuleBase
+
+	properties  packageProperties
+	packageInfo *packageInfo
+}
+
+func (p *packageModule) GenerateAndroidBuildActions(ModuleContext) {
+	// Nothing to do.
+}
+
+func (p *packageModule) GenerateBuildActions(ctx blueprint.ModuleContext) {
+	// Nothing to do.
+}
+
+func (p *packageModule) qualifiedModuleId(ctx BaseModuleContext) qualifiedModuleName {
+	// Override to create a package id.
+	return newPackageId(ctx.ModuleDir())
+}
+
+func (p *packageModule) Name() string {
+	return p.properties.Name
+}
+
+func (p *packageModule) setName(name string) {
+	p.properties.Name = name
+}
+
+// Counter to ensure package modules are created with a unique name within whatever namespace they
+// belong.
+var packageCount uint32 = 0
+
+func PackageFactory() Module {
+	module := &packageModule{}
+
+	// Get a unique if for the package. Has to be done atomically as the creation of the modules are
+	// done in parallel.
+	id := atomic.AddUint32(&packageCount, 1)
+	name := fmt.Sprintf("soong_package_%d", id)
+
+	module.properties.Name = name
+
+	module.AddProperties(&module.properties)
+
+	// The default_visibility property needs to be checked and parsed by the visibility module during
+	// its checking and parsing phases.
+	module.primaryVisibilityProperty =
+		newVisibilityProperty("default_visibility", &module.properties.Default_visibility)
+	module.visibilityPropertyInfo = []visibilityProperty{module.primaryVisibilityProperty}
+
+	return module
+}
+
+// Registers the function that renames the packages.
+func RegisterPackageRenamer(ctx RegisterMutatorsContext) {
+	ctx.BottomUp("packageRenamer", packageRenamer).Parallel()
+	ctx.BottomUp("packageErrorReporter", packageErrorReporter).Parallel()
+}
+
+// Renames the package to match the package directory.
+//
+// This also creates a PackageInfo object for each package and uses that to detect and remember
+// duplicates for later error reporting.
+func packageRenamer(ctx BottomUpMutatorContext) {
+	m, ok := ctx.Module().(*packageModule)
+	if !ok {
+		return
+	}
+
+	packageName := "//" + ctx.ModuleDir()
+
+	pi := newPackageInfo(ctx, packageName, m)
+	if pi.module != m {
+		// Remember that the package was duplicated but do not rename as that will cause an error to
+		// be logged with the generated name. Similarly, reporting the error here will use the generated
+		// name as renames are only processed after this phase.
+		pi.duplicated = true
+	} else {
+		// This is the first package module in this package so rename it to match the package name.
+		m.setName(packageName)
+		ctx.Rename(packageName)
+
+		// Store a package info reference in the module.
+		m.packageInfo = pi
+	}
+}
+
+// Logs any deferred errors.
+func packageErrorReporter(ctx BottomUpMutatorContext) {
+	m, ok := ctx.Module().(*packageModule)
+	if !ok {
+		return
+	}
+
+	packageDir := ctx.ModuleDir()
+	packageName := "//" + packageDir
+
+	// Get the PackageInfo for the package. Should have been populated in the packageRenamer phase.
+	pi := findPackageInfo(ctx, packageName)
+	if pi == nil {
+		ctx.ModuleErrorf("internal error, expected package info to be present for package '%s'",
+			packageName)
+		return
+	}
+
+	if pi.module != m {
+		// The package module has been duplicated but this is not the module that has been renamed so
+		// ignore it. An error will be logged for the renamed module which will ensure that the error
+		// message uses the correct name.
+		return
+	}
+
+	// Check to see whether there are duplicate package modules in the package.
+	if pi.duplicated {
+		ctx.ModuleErrorf("package {...} specified multiple times")
+		return
+	}
+}
+
+type defaultPackageInfoKey string
+
+func newPackageInfo(
+	ctx BaseModuleContext, packageName string, module *packageModule) *packageInfo {
+	key := NewCustomOnceKey(defaultPackageInfoKey(packageName))
+
+	return ctx.Config().Once(key, func() interface{} {
+		return &packageInfo{module: module}
+	}).(*packageInfo)
+}
+
+// Get the PackageInfo for the package name (starts with //, no trailing /), is nil if no package
+// module type was specified.
+func findPackageInfo(ctx BaseModuleContext, packageName string) *packageInfo {
+	key := NewCustomOnceKey(defaultPackageInfoKey(packageName))
+
+	pi := ctx.Config().Once(key, func() interface{} {
+		return nil
+	})
+
+	if pi == nil {
+		return nil
+	} else {
+		return pi.(*packageInfo)
+	}
+}
diff --git a/android/package_ctx.go b/android/package_ctx.go
index 00b99ff..a228910 100644
--- a/android/package_ctx.go
+++ b/android/package_ctx.go
@@ -16,11 +16,9 @@
 
 import (
 	"fmt"
-	"runtime"
 	"strings"
 
 	"github.com/google/blueprint"
-	"github.com/google/blueprint/pathtools"
 )
 
 // PackageContext is a wrapper for blueprint.PackageContext that adds
@@ -61,10 +59,6 @@
 	e.pctx.AddNinjaFileDeps(deps...)
 }
 
-func (e *configErrorWrapper) Fs() pathtools.FileSystem {
-	return nil
-}
-
 type PackageVarContext interface {
 	PathContext
 	errorfContext
@@ -104,7 +98,8 @@
 }
 
 // RuleFunc wraps blueprint.PackageContext.RuleFunc, converting the interface{} config
-// argument to a Context that supports Config().
+// argument to a Context that supports Config(), and provides a default Pool if none is
+// specified.
 func (p PackageContext) RuleFunc(name string,
 	f func(PackageRuleContext) blueprint.RuleParams, argNames ...string) blueprint.Rule {
 
@@ -114,6 +109,11 @@
 		if len(ctx.errors) > 0 {
 			return params, ctx.errors[0]
 		}
+		if (ctx.Config().UseGoma() || ctx.Config().UseRBE()) && params.Pool == nil {
+			// When USE_GOMA=true or USE_RBE=true are set and the rule is not supported by
+			// goma/RBE, restrict jobs to the local parallelism value
+			params.Pool = localPool
+		}
 		return params, nil
 	}, argNames...)
 }
@@ -171,46 +171,30 @@
 // package-scoped variable's initialization.
 func (p PackageContext) HostBinToolVariable(name, path string) blueprint.Variable {
 	return p.VariableFunc(name, func(ctx PackageVarContext) string {
-		return p.HostBinToolPath(ctx, path).String()
+		return ctx.Config().HostToolPath(ctx, path).String()
 	})
 }
 
-func (p PackageContext) HostBinToolPath(ctx PackageVarContext, path string) Path {
-	return PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "bin", path)
-}
-
 // HostJNIToolVariable returns a Variable whose value is the path to a host tool
 // in the lib directory for host targets. It may only be called during a Go
 // package's initialization - either from the init() function or as part of a
 // package-scoped variable's initialization.
 func (p PackageContext) HostJNIToolVariable(name, path string) blueprint.Variable {
 	return p.VariableFunc(name, func(ctx PackageVarContext) string {
-		return p.HostJNIToolPath(ctx, path).String()
+		return ctx.Config().HostJNIToolPath(ctx, path).String()
 	})
 }
 
-func (p PackageContext) HostJNIToolPath(ctx PackageVarContext, path string) Path {
-	ext := ".so"
-	if runtime.GOOS == "darwin" {
-		ext = ".dylib"
-	}
-	return PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "lib64", path+ext)
-}
-
 // HostJavaToolVariable returns a Variable whose value is the path to a host
 // tool in the frameworks directory for host targets. It may only be called
 // during a Go package's initialization - either from the init() function or as
 // part of a package-scoped variable's initialization.
 func (p PackageContext) HostJavaToolVariable(name, path string) blueprint.Variable {
 	return p.VariableFunc(name, func(ctx PackageVarContext) string {
-		return p.HostJavaToolPath(ctx, path).String()
+		return ctx.Config().HostJavaToolPath(ctx, path).String()
 	})
 }
 
-func (p PackageContext) HostJavaToolPath(ctx PackageVarContext, path string) Path {
-	return PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "framework", path)
-}
-
 // IntermediatesPathVariable returns a Variable whose value is the intermediate
 // directory appended with the supplied path. It may only be called during a Go
 // package's initialization - either from the init() function or as part of a
@@ -234,29 +218,49 @@
 	})
 }
 
-// AndroidStaticRule wraps blueprint.StaticRule and provides a default Pool if none is specified
+// AndroidStaticRule is an alias for StaticRule.
 func (p PackageContext) AndroidStaticRule(name string, params blueprint.RuleParams,
 	argNames ...string) blueprint.Rule {
-	return p.AndroidRuleFunc(name, func(PackageRuleContext) blueprint.RuleParams {
-		return params
-	}, argNames...)
-}
-
-// AndroidGomaStaticRule wraps blueprint.StaticRule but uses goma's parallelism if goma is enabled
-func (p PackageContext) AndroidGomaStaticRule(name string, params blueprint.RuleParams,
-	argNames ...string) blueprint.Rule {
 	return p.StaticRule(name, params, argNames...)
 }
 
-func (p PackageContext) AndroidRuleFunc(name string,
-	f func(PackageRuleContext) blueprint.RuleParams, argNames ...string) blueprint.Rule {
-	return p.RuleFunc(name, func(ctx PackageRuleContext) blueprint.RuleParams {
-		params := f(ctx)
-		if ctx.Config().UseGoma() && params.Pool == nil {
+// StaticRule wraps blueprint.StaticRule and provides a default Pool if none is specified.
+func (p PackageContext) StaticRule(name string, params blueprint.RuleParams,
+	argNames ...string) blueprint.Rule {
+	return p.RuleFunc(name, func(PackageRuleContext) blueprint.RuleParams {
+		return params
+	}, argNames...)
+}
+
+// RemoteRuleSupports selects if a AndroidRemoteStaticRule supports goma, RBE, or both.
+type RemoteRuleSupports int
+
+const (
+	SUPPORTS_NONE = 0
+	SUPPORTS_GOMA = 1 << iota
+	SUPPORTS_RBE  = 1 << iota
+	SUPPORTS_BOTH = SUPPORTS_GOMA | SUPPORTS_RBE
+)
+
+// AndroidRemoteStaticRule wraps blueprint.StaticRule but uses goma or RBE's parallelism if goma or RBE are enabled
+// and the appropriate SUPPORTS_* flag is set.
+func (p PackageContext) AndroidRemoteStaticRule(name string, supports RemoteRuleSupports, params blueprint.RuleParams,
+	argNames ...string) blueprint.Rule {
+
+	return p.PackageContext.RuleFunc(name, func(config interface{}) (blueprint.RuleParams, error) {
+		ctx := &configErrorWrapper{p, config.(Config), nil}
+		if ctx.Config().UseGoma() && supports&SUPPORTS_GOMA == 0 {
 			// When USE_GOMA=true is set and the rule is not supported by goma, restrict jobs to the
 			// local parallelism value
 			params.Pool = localPool
 		}
-		return params
+
+		if ctx.Config().UseRBE() && supports&SUPPORTS_RBE == 0 {
+			// When USE_RBE=true is set and the rule is not supported by RBE, restrict jobs to the
+			// local parallelism value
+			params.Pool = localPool
+		}
+
+		return params, nil
 	}, argNames...)
 }
diff --git a/android/package_test.go b/android/package_test.go
new file mode 100644
index 0000000..bc66928
--- /dev/null
+++ b/android/package_test.go
@@ -0,0 +1,101 @@
+package android
+
+import (
+	"testing"
+)
+
+var packageTests = []struct {
+	name           string
+	fs             map[string][]byte
+	expectedErrors []string
+}{
+	// Package default_visibility handling is tested in visibility_test.go
+	{
+		name: "package must not accept visibility and name properties",
+		fs: map[string][]byte{
+			"top/Blueprints": []byte(`
+				package {
+					name: "package",
+					visibility: ["//visibility:private"],
+				}`),
+		},
+		expectedErrors: []string{
+			`top/Blueprints:3:10: mutated field name cannot be set in a Blueprint file`,
+			`top/Blueprints:4:16: unrecognized property "visibility"`,
+		},
+	},
+	{
+		name: "multiple packages in separate directories",
+		fs: map[string][]byte{
+			"top/Blueprints": []byte(`
+				package {
+				}`),
+			"other/Blueprints": []byte(`
+				package {
+				}`),
+			"other/nested/Blueprints": []byte(`
+				package {
+				}`),
+		},
+	},
+	{
+		name: "package must not be specified more than once per package",
+		fs: map[string][]byte{
+			"top/Blueprints": []byte(`
+				package {
+					default_visibility: ["//visibility:private"],
+				}
+
+        package {
+				}`),
+		},
+		expectedErrors: []string{
+			`module "//top": package {...} specified multiple times`,
+		},
+	},
+}
+
+func TestPackage(t *testing.T) {
+	for _, test := range packageTests {
+		t.Run(test.name, func(t *testing.T) {
+			_, errs := testPackage(test.fs)
+
+			expectedErrors := test.expectedErrors
+			if expectedErrors == nil {
+				FailIfErrored(t, errs)
+			} else {
+				for _, expectedError := range expectedErrors {
+					FailIfNoMatchingErrors(t, expectedError, errs)
+				}
+				if len(errs) > len(expectedErrors) {
+					t.Errorf("additional errors found, expected %d, found %d", len(expectedErrors), len(errs))
+					for i, expectedError := range expectedErrors {
+						t.Errorf("expectedErrors[%d] = %s", i, expectedError)
+					}
+					for i, err := range errs {
+						t.Errorf("errs[%d] = %s", i, err)
+					}
+				}
+			}
+		})
+	}
+}
+
+func testPackage(fs map[string][]byte) (*TestContext, []error) {
+
+	// Create a new config per test as visibility information is stored in the config.
+	config := TestArchConfig(buildDir, nil, "", fs)
+
+	ctx := NewTestArchContext()
+	ctx.RegisterModuleType("package", PackageFactory)
+	ctx.PreArchMutators(RegisterPackageRenamer)
+	ctx.Register(config)
+
+	_, errs := ctx.ParseBlueprintsFiles(".")
+	if len(errs) > 0 {
+		return ctx, errs
+	}
+
+	_, errs = ctx.PrepareBuildActions(config)
+	return ctx, errs
+}
diff --git a/android/path_properties.go b/android/path_properties.go
index af7af59..6b1cdb3 100644
--- a/android/path_properties.go
+++ b/android/path_properties.go
@@ -35,16 +35,17 @@
 
 	props := m.base().generalProperties
 
+	var pathProperties []string
 	for _, ps := range props {
-		pathProperties := pathPropertiesForPropertyStruct(ctx, ps)
-		pathProperties = FirstUniqueStrings(pathProperties)
+		pathProperties = append(pathProperties, pathPropertiesForPropertyStruct(ctx, ps)...)
+	}
 
-		for _, s := range pathProperties {
-			if m, t := SrcIsModuleWithTag(s); m != "" {
-				ctx.AddDependency(ctx.Module(), sourceOrOutputDepTag(t), m)
-			}
+	pathProperties = FirstUniqueStrings(pathProperties)
+
+	for _, s := range pathProperties {
+		if m, t := SrcIsModuleWithTag(s); m != "" {
+			ctx.AddDependency(ctx.Module(), sourceOrOutputDepTag(t), m)
 		}
-
 	}
 }
 
diff --git a/android/path_properties_test.go b/android/path_properties_test.go
index fa187fa..f367b82 100644
--- a/android/path_properties_test.go
+++ b/android/path_properties_test.go
@@ -15,8 +15,6 @@
 package android
 
 import (
-	"io/ioutil"
-	"os"
 	"reflect"
 	"testing"
 )
@@ -30,12 +28,17 @@
 		Qux string
 	}
 
+	// A second property struct with a duplicate property name
+	props2 struct {
+		Foo string `android:"path"`
+	}
+
 	sourceDeps []string
 }
 
 func pathDepsMutatorTestModuleFactory() Module {
 	module := &pathDepsMutatorTestModule{}
-	module.AddProperties(&module.props)
+	module.AddProperties(&module.props, &module.props2)
 	InitAndroidArchModule(module, DeviceSupported, MultilibBoth)
 	return module
 }
@@ -46,6 +49,13 @@
 			p.sourceDeps = append(p.sourceDeps, ctx.OtherModuleName(dep))
 		}
 	})
+
+	if p.props.Foo != "" {
+		// Make sure there is only one dependency on a module listed in a property present in multiple property structs
+		if ctx.GetDirectDepWithTag(SrcIsModule(p.props.Foo), sourceOrOutputDepTag("")) == nil {
+			ctx.ModuleErrorf("GetDirectDepWithTag failed")
+		}
+	}
 }
 
 func TestPathDepsMutator(t *testing.T) {
@@ -85,20 +95,8 @@
 		},
 	}
 
-	buildDir, err := ioutil.TempDir("", "soong_path_properties_test")
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer os.RemoveAll(buildDir)
-
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
-			config := TestArchConfig(buildDir, nil)
-			ctx := NewTestArchContext()
-
-			ctx.RegisterModuleType("test", ModuleFactoryAdaptor(pathDepsMutatorTestModuleFactory))
-			ctx.RegisterModuleType("filegroup", ModuleFactoryAdaptor(FileGroupFactory))
-
 			bp := test.bp + `
 				filegroup {
 					name: "a",
@@ -117,13 +115,13 @@
 				}
 			`
 
-			mockFS := map[string][]byte{
-				"Android.bp": []byte(bp),
-			}
+			config := TestArchConfig(buildDir, nil, bp, nil)
+			ctx := NewTestArchContext()
 
-			ctx.MockFileSystem(mockFS)
+			ctx.RegisterModuleType("test", pathDepsMutatorTestModuleFactory)
+			ctx.RegisterModuleType("filegroup", FileGroupFactory)
 
-			ctx.Register()
+			ctx.Register(config)
 			_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
 			FailIfErrored(t, errs)
 			_, errs = ctx.PrepareBuildActions(config)
diff --git a/android/paths.go b/android/paths.go
index 20b8b82..02f56d0 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -16,6 +16,8 @@
 
 import (
 	"fmt"
+	"io/ioutil"
+	"os"
 	"path/filepath"
 	"reflect"
 	"sort"
@@ -25,10 +27,11 @@
 	"github.com/google/blueprint/pathtools"
 )
 
+var absSrcDir string
+
 // PathContext is the subset of a (Module|Singleton)Context required by the
 // Path methods.
 type PathContext interface {
-	Fs() pathtools.FileSystem
 	Config() Config
 	AddNinjaFileDeps(deps ...string)
 }
@@ -44,8 +47,11 @@
 	BaseModuleContext
 
 	InstallInData() bool
+	InstallInTestcases() bool
 	InstallInSanitizerDir() bool
 	InstallInRecovery() bool
+	InstallInRoot() bool
+	InstallBypassMake() bool
 }
 
 var _ ModuleInstallPathContext = ModuleContext(nil)
@@ -86,6 +92,15 @@
 	}
 }
 
+func pathContextName(ctx PathContext, module blueprint.Module) string {
+	if x, ok := ctx.(interface{ ModuleName(blueprint.Module) string }); ok {
+		return x.ModuleName(module)
+	} else if x, ok := ctx.(interface{ OtherModuleName(blueprint.Module) string }); ok {
+		return x.OtherModuleName(module)
+	}
+	return "unknown"
+}
+
 type Path interface {
 	// Returns the path in string form
 	String() string
@@ -106,6 +121,9 @@
 type WritablePath interface {
 	Path
 
+	// return the path to the build directory.
+	buildDir() string
+
 	// the writablePath method doesn't directly do anything,
 	// but it allows a struct to distinguish between whether or not it implements the WritablePath interface
 	writablePath()
@@ -230,7 +248,7 @@
 // references to OutputFileProducer modules using the ":name{.tag}" syntax.  Properties passed as the paths or excludes
 // argument must have been annotated with struct tag `android:"path"` so that dependencies on SourceFileProducer modules
 // will have already been handled by the path_properties mutator.  If ctx.Config().AllowMissingDependencies() is
-// truethen any missing SourceFileProducer or OutputFileProducer dependencies will cause the module to be marked as
+// true then any missing SourceFileProducer or OutputFileProducer dependencies will cause the module to be marked as
 // having missing dependencies.
 func PathsForModuleSrcExcludes(ctx ModuleContext, paths, excludes []string) Paths {
 	ret, missingDeps := PathsAndMissingDepsForModuleSrcExcludes(ctx, paths, excludes)
@@ -244,6 +262,33 @@
 	return ret
 }
 
+// OutputPaths is a slice of OutputPath objects, with helpers to operate on the collection.
+type OutputPaths []OutputPath
+
+// Paths returns the OutputPaths as a Paths
+func (p OutputPaths) Paths() Paths {
+	if p == nil {
+		return nil
+	}
+	ret := make(Paths, len(p))
+	for i, path := range p {
+		ret[i] = path
+	}
+	return ret
+}
+
+// Strings returns the string forms of the writable paths.
+func (p OutputPaths) Strings() []string {
+	if p == nil {
+		return nil
+	}
+	ret := make([]string, len(p))
+	for i, path := range p {
+		ret[i] = path.String()
+	}
+	return ret
+}
+
 // PathsAndMissingDepsForModuleSrcExcludes returns Paths rooted from the module's local source directory, excluding
 // paths listed in the excludes arguments, and a list of missing dependencies.  It expands globs, references to
 // SourceFileProducer modules using the ":name" syntax, and references to OutputFileProducer modules using the
@@ -348,7 +393,7 @@
 		return PathsWithModuleSrcSubDir(ctx, paths, ""), nil
 	} else {
 		p := pathForModuleSrc(ctx, s)
-		if exists, _, err := ctx.Fs().Exists(p.String()); err != nil {
+		if exists, _, err := ctx.Config().fs.Exists(p.String()); err != nil {
 			reportPathErrorf(ctx, "%s: %s", p, err.Error())
 		} else if !exists {
 			reportPathErrorf(ctx, "module source path %q does not exist", p)
@@ -367,7 +412,7 @@
 // each string. If incDirs is false, strip paths with a trailing '/' from the list.
 // It intended for use in globs that only list files that exist, so it allows '$' in
 // filenames.
-func pathsForModuleSrcFromFullPath(ctx BaseModuleContext, paths []string, incDirs bool) Paths {
+func pathsForModuleSrcFromFullPath(ctx EarlyModuleContext, paths []string, incDirs bool) Paths {
 	prefix := filepath.Join(ctx.Config().srcDir, ctx.ModuleDir()) + "/"
 	if prefix == "./" {
 		prefix = ""
@@ -482,8 +527,12 @@
 }
 
 func FilterPathList(list []Path, filter []Path) (remainder []Path, filtered []Path) {
+	return FilterPathListPredicate(list, func(p Path) bool { return inPathList(p, filter) })
+}
+
+func FilterPathListPredicate(list []Path, predicate func(Path) bool) (remainder []Path, filtered []Path) {
 	for _, l := range list {
-		if inPathList(l, filter) {
+		if predicate(l) {
 			filtered = append(filtered, l)
 		} else {
 			remainder = append(remainder, l)
@@ -674,7 +723,7 @@
 		var deps []string
 		// We cannot add build statements in this context, so we fall back to
 		// AddNinjaFileDeps
-		files, deps, err = pathtools.Glob(path.String(), nil, pathtools.FollowSymlinks)
+		files, deps, err = ctx.Config().fs.Glob(path.String(), nil, pathtools.FollowSymlinks)
 		ctx.AddNinjaFileDeps(deps...)
 	}
 
@@ -706,7 +755,7 @@
 		if !exists {
 			modCtx.AddMissingDependencies([]string{path.String()})
 		}
-	} else if exists, _, err := ctx.Fs().Exists(path.String()); err != nil {
+	} else if exists, _, err := ctx.Config().fs.Exists(path.String()); err != nil {
 		reportPathErrorf(ctx, "%s: %s", path, err.Error())
 	} else if !exists {
 		reportPathErrorf(ctx, "source path %q does not exist", path)
@@ -790,7 +839,7 @@
 	return OptionalPathForPath(PathForSource(ctx, relPath))
 }
 
-// OutputPath is a Path representing a file path rooted from the build directory
+// OutputPath is a Path representing an intermediates file path rooted from the build directory
 type OutputPath struct {
 	basePath
 }
@@ -805,7 +854,12 @@
 	return p
 }
 
+func (p OutputPath) buildDir() string {
+	return p.config.buildDir
+}
+
 var _ Path = OutputPath{}
+var _ WritablePath = OutputPath{}
 
 // PathForOutput joins the provided paths and returns an OutputPath that is
 // validated to not escape the build dir.
@@ -833,10 +887,6 @@
 	return filepath.Join(p.config.buildDir, p.path)
 }
 
-func (p OutputPath) RelPathString() string {
-	return p.path
-}
-
 // Join creates a new OutputPath with paths... joined with the current path. The
 // provided paths... may not use '..' to escape from the current path.
 func (p OutputPath) Join(ctx PathContext, paths ...string) OutputPath {
@@ -977,6 +1027,10 @@
 
 var _ Path = ModuleOutPath{}
 
+func (p ModuleOutPath) objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath {
+	return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
+}
+
 func pathForModule(ctx ModuleContext) OutputPath {
 	return PathForOutput(ctx, ".intermediates", ctx.ModuleDir(), ctx.ModuleName(), ctx.ModuleSubDir())
 }
@@ -984,7 +1038,7 @@
 // PathForVndkRefAbiDump returns an OptionalPath representing the path of the
 // reference abi dump for the given module. This is not guaranteed to be valid.
 func PathForVndkRefAbiDump(ctx ModuleContext, version, fileName string,
-	isLlndkOrNdk, isVndk, isGzip bool) OptionalPath {
+	isNdk, isLlndkOrVndk, isGzip bool) OptionalPath {
 
 	arches := ctx.DeviceConfig().Arches()
 	if len(arches) == 0 {
@@ -997,9 +1051,9 @@
 	}
 
 	var dirName string
-	if isLlndkOrNdk {
+	if isNdk {
 		dirName = "ndk"
-	} else if isVndk {
+	} else if isLlndkOrVndk {
 		dirName = "vndk"
 	} else {
 		dirName = "platform" // opt-in libs
@@ -1101,9 +1155,51 @@
 	return ModuleResPath{PathForModuleOut(ctx, "res", p)}
 }
 
+// InstallPath is a Path representing a installed file path rooted from the build directory
+type InstallPath struct {
+	basePath
+
+	baseDir string // "../" for Make paths to convert "out/soong" to "out", "" for Soong paths
+}
+
+func (p InstallPath) buildDir() string {
+	return p.config.buildDir
+}
+
+var _ Path = InstallPath{}
+var _ WritablePath = InstallPath{}
+
+func (p InstallPath) writablePath() {}
+
+func (p InstallPath) String() string {
+	return filepath.Join(p.config.buildDir, p.baseDir, p.path)
+}
+
+// Join creates a new InstallPath with paths... joined with the current path. The
+// provided paths... may not use '..' to escape from the current path.
+func (p InstallPath) Join(ctx PathContext, paths ...string) InstallPath {
+	path, err := validatePath(paths...)
+	if err != nil {
+		reportPathError(ctx, err)
+	}
+	return p.withRel(path)
+}
+
+func (p InstallPath) withRel(rel string) InstallPath {
+	p.basePath = p.basePath.withRel(rel)
+	return p
+}
+
+// ToMakePath returns a new InstallPath that points to Make's install directory instead of Soong's,
+// i.e. out/ instead of out/soong/.
+func (p InstallPath) ToMakePath() InstallPath {
+	p.baseDir = "../"
+	return p
+}
+
 // PathForModuleInstall returns a Path representing the install path for the
 // module appended with paths...
-func PathForModuleInstall(ctx ModuleInstallPathContext, pathComponents ...string) OutputPath {
+func PathForModuleInstall(ctx ModuleInstallPathContext, pathComponents ...string) InstallPath {
 	var outPaths []string
 	if ctx.Device() {
 		partition := modulePartition(ctx)
@@ -1123,10 +1219,30 @@
 		outPaths = append([]string{"debug"}, outPaths...)
 	}
 	outPaths = append(outPaths, pathComponents...)
-	return PathForOutput(ctx, outPaths...)
+
+	path, err := validatePath(outPaths...)
+	if err != nil {
+		reportPathError(ctx, err)
+	}
+
+	ret := InstallPath{basePath{path, ctx.Config(), ""}, ""}
+	if ctx.InstallBypassMake() && ctx.Config().EmbeddedInMake() {
+		ret = ret.ToMakePath()
+	}
+
+	return ret
 }
 
-func InstallPathToOnDevicePath(ctx PathContext, path OutputPath) string {
+func PathForNdkInstall(ctx PathContext, paths ...string) InstallPath {
+	paths = append([]string{"ndk"}, paths...)
+	path, err := validatePath(paths...)
+	if err != nil {
+		reportPathError(ctx, err)
+	}
+	return InstallPath{basePath{path, ctx.Config(), ""}, ""}
+}
+
+func InstallPathToOnDevicePath(ctx PathContext, path InstallPath) string {
 	rel := Rel(ctx, PathForOutput(ctx, "target", "product", ctx.Config().DeviceName()).String(), path.String())
 
 	return "/" + rel
@@ -1136,17 +1252,25 @@
 	var partition string
 	if ctx.InstallInData() {
 		partition = "data"
+	} else if ctx.InstallInTestcases() {
+		partition = "testcases"
 	} else if ctx.InstallInRecovery() {
-		// the layout of recovery partion is the same as that of system partition
-		partition = "recovery/root/system"
+		if ctx.InstallInRoot() {
+			partition = "recovery/root"
+		} else {
+			// the layout of recovery partion is the same as that of system partition
+			partition = "recovery/root/system"
+		}
 	} else if ctx.SocSpecific() {
 		partition = ctx.DeviceConfig().VendorPath()
 	} else if ctx.DeviceSpecific() {
 		partition = ctx.DeviceConfig().OdmPath()
 	} else if ctx.ProductSpecific() {
 		partition = ctx.DeviceConfig().ProductPath()
-	} else if ctx.ProductServicesSpecific() {
-		partition = ctx.DeviceConfig().ProductServicesPath()
+	} else if ctx.SystemExtSpecific() {
+		partition = ctx.DeviceConfig().SystemExtPath()
+	} else if ctx.InstallInRoot() {
+		partition = "root"
 	} else {
 		partition = "system"
 	}
@@ -1196,6 +1320,10 @@
 
 func (p PhonyPath) writablePath() {}
 
+func (p PhonyPath) buildDir() string {
+	return p.config.buildDir
+}
+
 var _ Path = PhonyPath{}
 var _ WritablePath = PhonyPath{}
 
@@ -1207,12 +1335,6 @@
 	return p.path
 }
 
-type testWritablePath struct {
-	testPath
-}
-
-func (p testPath) writablePath() {}
-
 // PathForTesting returns a Path constructed from joining the elements of paths with '/'.  It should only be used from
 // within tests.
 func PathForTesting(paths ...string) Path {
@@ -1233,42 +1355,18 @@
 	return p
 }
 
-// WritablePathForTesting returns a Path constructed from joining the elements of paths with '/'.  It should only be
-// used from within tests.
-func WritablePathForTesting(paths ...string) WritablePath {
-	p, err := validateSafePath(paths...)
-	if err != nil {
-		panic(err)
-	}
-	return testWritablePath{testPath{basePath{path: p, rel: p}}}
-}
-
-// WritablePathsForTesting returns a Path constructed from each element in strs. It should only be used from within
-// tests.
-func WritablePathsForTesting(strs ...string) WritablePaths {
-	p := make(WritablePaths, len(strs))
-	for i, s := range strs {
-		p[i] = WritablePathForTesting(s)
-	}
-
-	return p
-}
-
 type testPathContext struct {
 	config Config
-	fs     pathtools.FileSystem
 }
 
-func (x *testPathContext) Fs() pathtools.FileSystem   { return x.fs }
 func (x *testPathContext) Config() Config             { return x.config }
 func (x *testPathContext) AddNinjaFileDeps(...string) {}
 
 // PathContextForTesting returns a PathContext that can be used in tests, for example to create an OutputPath with
 // PathForOutput.
-func PathContextForTesting(config Config, fs map[string][]byte) PathContext {
+func PathContextForTesting(config Config) PathContext {
 	return &testPathContext{
 		config: config,
-		fs:     pathtools.MockFs(fs),
 	}
 }
 
@@ -1306,3 +1404,16 @@
 	}
 	return rel, true, nil
 }
+
+// Writes a file to the output directory.  Attempting to write directly to the output directory
+// will fail due to the sandbox of the soong_build process.
+func WriteFileToOutputDir(path WritablePath, data []byte, perm os.FileMode) error {
+	return ioutil.WriteFile(absolutePath(path.String()), data, perm)
+}
+
+func absolutePath(path string) string {
+	if filepath.IsAbs(path) {
+		return path
+	}
+	return filepath.Join(absSrcDir, path)
+}
diff --git a/android/paths_test.go b/android/paths_test.go
index 7bcfe41..46e3e1f 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -21,7 +21,6 @@
 	"strings"
 	"testing"
 
-	"github.com/google/blueprint/pathtools"
 	"github.com/google/blueprint/proptools"
 )
 
@@ -201,12 +200,10 @@
 	baseModuleContext
 
 	inData         bool
+	inTestcases    bool
 	inSanitizerDir bool
 	inRecovery     bool
-}
-
-func (moduleInstallPathContextImpl) Fs() pathtools.FileSystem {
-	return pathtools.MockFs(nil)
+	inRoot         bool
 }
 
 func (m moduleInstallPathContextImpl) Config() Config {
@@ -219,6 +216,10 @@
 	return m.inData
 }
 
+func (m moduleInstallPathContextImpl) InstallInTestcases() bool {
+	return m.inTestcases
+}
+
 func (m moduleInstallPathContextImpl) InstallInSanitizerDir() bool {
 	return m.inSanitizerDir
 }
@@ -227,8 +228,20 @@
 	return m.inRecovery
 }
 
+func (m moduleInstallPathContextImpl) InstallInRoot() bool {
+	return m.inRoot
+}
+
+func (m moduleInstallPathContextImpl) InstallBypassMake() bool {
+	return false
+}
+
+func pathTestConfig(buildDir string) Config {
+	return TestConfig(buildDir, nil, "", nil)
+}
+
 func TestPathForModuleInstall(t *testing.T) {
-	testConfig := TestConfig("", nil)
+	testConfig := pathTestConfig("")
 
 	hostTarget := Target{Os: Linux}
 	deviceTarget := Target{Os: Android}
@@ -243,6 +256,7 @@
 			name: "host binary",
 			ctx: &moduleInstallPathContextImpl{
 				baseModuleContext: baseModuleContext{
+					os:     hostTarget.Os,
 					target: hostTarget,
 				},
 			},
@@ -254,6 +268,7 @@
 			name: "system binary",
 			ctx: &moduleInstallPathContextImpl{
 				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
 					target: deviceTarget,
 				},
 			},
@@ -264,8 +279,11 @@
 			name: "vendor binary",
 			ctx: &moduleInstallPathContextImpl{
 				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
 					target: deviceTarget,
-					kind:   socSpecificModule,
+					earlyModuleContext: earlyModuleContext{
+						kind: socSpecificModule,
+					},
 				},
 			},
 			in:  []string{"bin", "my_test"},
@@ -275,8 +293,11 @@
 			name: "odm binary",
 			ctx: &moduleInstallPathContextImpl{
 				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
 					target: deviceTarget,
-					kind:   deviceSpecificModule,
+					earlyModuleContext: earlyModuleContext{
+						kind: deviceSpecificModule,
+					},
 				},
 			},
 			in:  []string{"bin", "my_test"},
@@ -286,29 +307,73 @@
 			name: "product binary",
 			ctx: &moduleInstallPathContextImpl{
 				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
 					target: deviceTarget,
-					kind:   productSpecificModule,
+					earlyModuleContext: earlyModuleContext{
+						kind: productSpecificModule,
+					},
 				},
 			},
 			in:  []string{"bin", "my_test"},
 			out: "target/product/test_device/product/bin/my_test",
 		},
 		{
-			name: "product_services binary",
+			name: "system_ext binary",
 			ctx: &moduleInstallPathContextImpl{
 				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
 					target: deviceTarget,
-					kind:   productServicesSpecificModule,
+					earlyModuleContext: earlyModuleContext{
+						kind: systemExtSpecificModule,
+					},
 				},
 			},
 			in:  []string{"bin", "my_test"},
-			out: "target/product/test_device/product_services/bin/my_test",
+			out: "target/product/test_device/system_ext/bin/my_test",
+		},
+		{
+			name: "root binary",
+			ctx: &moduleInstallPathContextImpl{
+				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
+					target: deviceTarget,
+				},
+				inRoot: true,
+			},
+			in:  []string{"my_test"},
+			out: "target/product/test_device/root/my_test",
+		},
+		{
+			name: "recovery binary",
+			ctx: &moduleInstallPathContextImpl{
+				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
+					target: deviceTarget,
+				},
+				inRecovery: true,
+			},
+			in:  []string{"bin/my_test"},
+			out: "target/product/test_device/recovery/root/system/bin/my_test",
+		},
+		{
+			name: "recovery root binary",
+			ctx: &moduleInstallPathContextImpl{
+				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
+					target: deviceTarget,
+				},
+				inRecovery: true,
+				inRoot:     true,
+			},
+			in:  []string{"my_test"},
+			out: "target/product/test_device/recovery/root/my_test",
 		},
 
 		{
 			name: "system native test binary",
 			ctx: &moduleInstallPathContextImpl{
 				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
 					target: deviceTarget,
 				},
 				inData: true,
@@ -320,8 +385,11 @@
 			name: "vendor native test binary",
 			ctx: &moduleInstallPathContextImpl{
 				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
 					target: deviceTarget,
-					kind:   socSpecificModule,
+					earlyModuleContext: earlyModuleContext{
+						kind: socSpecificModule,
+					},
 				},
 				inData: true,
 			},
@@ -332,8 +400,11 @@
 			name: "odm native test binary",
 			ctx: &moduleInstallPathContextImpl{
 				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
 					target: deviceTarget,
-					kind:   deviceSpecificModule,
+					earlyModuleContext: earlyModuleContext{
+						kind: deviceSpecificModule,
+					},
 				},
 				inData: true,
 			},
@@ -344,8 +415,11 @@
 			name: "product native test binary",
 			ctx: &moduleInstallPathContextImpl{
 				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
 					target: deviceTarget,
-					kind:   productSpecificModule,
+					earlyModuleContext: earlyModuleContext{
+						kind: productSpecificModule,
+					},
 				},
 				inData: true,
 			},
@@ -354,11 +428,14 @@
 		},
 
 		{
-			name: "product_services native test binary",
+			name: "system_ext native test binary",
 			ctx: &moduleInstallPathContextImpl{
 				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
 					target: deviceTarget,
-					kind:   productServicesSpecificModule,
+					earlyModuleContext: earlyModuleContext{
+						kind: systemExtSpecificModule,
+					},
 				},
 				inData: true,
 			},
@@ -370,6 +447,7 @@
 			name: "sanitized system binary",
 			ctx: &moduleInstallPathContextImpl{
 				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
 					target: deviceTarget,
 				},
 				inSanitizerDir: true,
@@ -381,8 +459,11 @@
 			name: "sanitized vendor binary",
 			ctx: &moduleInstallPathContextImpl{
 				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
 					target: deviceTarget,
-					kind:   socSpecificModule,
+					earlyModuleContext: earlyModuleContext{
+						kind: socSpecificModule,
+					},
 				},
 				inSanitizerDir: true,
 			},
@@ -393,8 +474,11 @@
 			name: "sanitized odm binary",
 			ctx: &moduleInstallPathContextImpl{
 				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
 					target: deviceTarget,
-					kind:   deviceSpecificModule,
+					earlyModuleContext: earlyModuleContext{
+						kind: deviceSpecificModule,
+					},
 				},
 				inSanitizerDir: true,
 			},
@@ -405,8 +489,11 @@
 			name: "sanitized product binary",
 			ctx: &moduleInstallPathContextImpl{
 				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
 					target: deviceTarget,
-					kind:   productSpecificModule,
+					earlyModuleContext: earlyModuleContext{
+						kind: productSpecificModule,
+					},
 				},
 				inSanitizerDir: true,
 			},
@@ -415,22 +502,26 @@
 		},
 
 		{
-			name: "sanitized product_services binary",
+			name: "sanitized system_ext binary",
 			ctx: &moduleInstallPathContextImpl{
 				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
 					target: deviceTarget,
-					kind:   productServicesSpecificModule,
+					earlyModuleContext: earlyModuleContext{
+						kind: systemExtSpecificModule,
+					},
 				},
 				inSanitizerDir: true,
 			},
 			in:  []string{"bin", "my_test"},
-			out: "target/product/test_device/data/asan/product_services/bin/my_test",
+			out: "target/product/test_device/data/asan/system_ext/bin/my_test",
 		},
 
 		{
 			name: "sanitized system native test binary",
 			ctx: &moduleInstallPathContextImpl{
 				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
 					target: deviceTarget,
 				},
 				inData:         true,
@@ -443,8 +534,11 @@
 			name: "sanitized vendor native test binary",
 			ctx: &moduleInstallPathContextImpl{
 				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
 					target: deviceTarget,
-					kind:   socSpecificModule,
+					earlyModuleContext: earlyModuleContext{
+						kind: socSpecificModule,
+					},
 				},
 				inData:         true,
 				inSanitizerDir: true,
@@ -456,8 +550,11 @@
 			name: "sanitized odm native test binary",
 			ctx: &moduleInstallPathContextImpl{
 				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
 					target: deviceTarget,
-					kind:   deviceSpecificModule,
+					earlyModuleContext: earlyModuleContext{
+						kind: deviceSpecificModule,
+					},
 				},
 				inData:         true,
 				inSanitizerDir: true,
@@ -469,8 +566,11 @@
 			name: "sanitized product native test binary",
 			ctx: &moduleInstallPathContextImpl{
 				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
 					target: deviceTarget,
-					kind:   productSpecificModule,
+					earlyModuleContext: earlyModuleContext{
+						kind: productSpecificModule,
+					},
 				},
 				inData:         true,
 				inSanitizerDir: true,
@@ -479,11 +579,14 @@
 			out: "target/product/test_device/data/asan/data/nativetest/my_test",
 		},
 		{
-			name: "sanitized product_services native test binary",
+			name: "sanitized system_ext native test binary",
 			ctx: &moduleInstallPathContextImpl{
 				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
 					target: deviceTarget,
-					kind:   productServicesSpecificModule,
+					earlyModuleContext: earlyModuleContext{
+						kind: systemExtSpecificModule,
+					},
 				},
 				inData:         true,
 				inSanitizerDir: true,
@@ -507,18 +610,19 @@
 }
 
 func TestDirectorySortedPaths(t *testing.T) {
-	config := TestConfig("out", nil)
-
-	ctx := PathContextForTesting(config, map[string][]byte{
-		"a.txt":   nil,
-		"a/txt":   nil,
-		"a/b/c":   nil,
-		"a/b/d":   nil,
-		"b":       nil,
-		"b/b.txt": nil,
-		"a/a.txt": nil,
+	config := TestConfig("out", nil, "", map[string][]byte{
+		"Android.bp": nil,
+		"a.txt":      nil,
+		"a/txt":      nil,
+		"a/b/c":      nil,
+		"a/b/d":      nil,
+		"b":          nil,
+		"b/b.txt":    nil,
+		"a/a.txt":    nil,
 	})
 
+	ctx := PathContextForTesting(config)
+
 	makePaths := func() Paths {
 		return Paths{
 			PathForSource(ctx, "a.txt"),
@@ -682,7 +786,7 @@
 		t.Run(f.name, func(t *testing.T) {
 			for _, test := range testCases {
 				t.Run(test.name, func(t *testing.T) {
-					testConfig := TestConfig(test.buildDir, nil)
+					testConfig := pathTestConfig(test.buildDir)
 					ctx := &configErrorWrapper{config: testConfig}
 					_, err := f.f(ctx, test.src)
 					if len(ctx.errors) > 0 {
@@ -814,12 +918,11 @@
 func testPathForModuleSrc(t *testing.T, buildDir string, tests []pathForModuleSrcTestCase) {
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
-			config := TestConfig(buildDir, nil)
 			ctx := NewTestContext()
 
-			ctx.RegisterModuleType("test", ModuleFactoryAdaptor(pathForModuleSrcTestModuleFactory))
-			ctx.RegisterModuleType("output_file_provider", ModuleFactoryAdaptor(pathForModuleSrcOutputFileProviderModuleFactory))
-			ctx.RegisterModuleType("filegroup", ModuleFactoryAdaptor(FileGroupFactory))
+			ctx.RegisterModuleType("test", pathForModuleSrcTestModuleFactory)
+			ctx.RegisterModuleType("output_file_provider", pathForModuleSrcOutputFileProviderModuleFactory)
+			ctx.RegisterModuleType("filegroup", FileGroupFactory)
 
 			fgBp := `
 				filegroup {
@@ -848,9 +951,9 @@
 				"foo/src_special/$": nil,
 			}
 
-			ctx.MockFileSystem(mockFS)
+			config := TestConfig(buildDir, nil, "", mockFS)
 
-			ctx.Register()
+			ctx.Register(config)
 			_, errs := ctx.ParseFileList(".", []string{"fg/Android.bp", "foo/Android.bp", "ofp/Android.bp"})
 			FailIfErrored(t, errs)
 			_, errs = ctx.PrepareBuildActions(config)
@@ -1025,14 +1128,6 @@
 }
 
 func TestPathsForModuleSrc_AllowMissingDependencies(t *testing.T) {
-	config := TestConfig(buildDir, nil)
-	config.TestProductVariables.Allow_missing_dependencies = proptools.BoolPtr(true)
-
-	ctx := NewTestContext()
-	ctx.SetAllowMissingDependencies(true)
-
-	ctx.RegisterModuleType("test", ModuleFactoryAdaptor(pathForModuleSrcTestModuleFactory))
-
 	bp := `
 		test {
 			name: "foo",
@@ -1049,13 +1144,16 @@
 		}
 	`
 
-	mockFS := map[string][]byte{
-		"Android.bp": []byte(bp),
-	}
+	config := TestConfig(buildDir, nil, bp, nil)
+	config.TestProductVariables.Allow_missing_dependencies = proptools.BoolPtr(true)
 
-	ctx.MockFileSystem(mockFS)
+	ctx := NewTestContext()
+	ctx.SetAllowMissingDependencies(true)
 
-	ctx.Register()
+	ctx.RegisterModuleType("test", pathForModuleSrcTestModuleFactory)
+
+	ctx.Register(config)
+
 	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
 	FailIfErrored(t, errs)
 	_, errs = ctx.PrepareBuildActions(config)
@@ -1088,7 +1186,7 @@
 
 func ExampleOutputPath_ReplaceExtension() {
 	ctx := &configErrorWrapper{
-		config: TestConfig("out", nil),
+		config: TestConfig("out", nil, "", nil),
 	}
 	p := PathForOutput(ctx, "system/framework").Join(ctx, "boot.art")
 	p2 := p.ReplaceExtension(ctx, "oat")
@@ -1102,7 +1200,7 @@
 
 func ExampleOutputPath_FileInSameDir() {
 	ctx := &configErrorWrapper{
-		config: TestConfig("out", nil),
+		config: TestConfig("out", nil, "", nil),
 	}
 	p := PathForOutput(ctx, "system/framework").Join(ctx, "boot.art")
 	p2 := p.InSameDir(ctx, "oat", "arm", "boot.vdex")
diff --git a/android/prebuilt.go b/android/prebuilt.go
index 5087b18..2c99f1f 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -16,6 +16,7 @@
 
 import (
 	"fmt"
+	"reflect"
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
@@ -24,11 +25,16 @@
 // This file implements common functionality for handling modules that may exist as prebuilts,
 // source, or both.
 
+func RegisterPrebuiltMutators(ctx RegistrationContext) {
+	ctx.PreArchMutators(RegisterPrebuiltsPreArchMutators)
+	ctx.PostDepsMutators(RegisterPrebuiltsPostDepsMutators)
+}
+
 type prebuiltDependencyTag struct {
 	blueprint.BaseDependencyTag
 }
 
-var prebuiltDepTag prebuiltDependencyTag
+var PrebuiltDepTag prebuiltDependencyTag
 
 type PrebuiltProperties struct {
 	// When prefer is set to true the prebuilt will be used instead of any source module with
@@ -43,7 +49,10 @@
 	properties PrebuiltProperties
 	module     Module
 	srcs       *[]string
-	src        *string
+
+	// Metadata for single source Prebuilt modules.
+	srcProps reflect.Value
+	srcField reflect.StructField
 }
 
 func (p *Prebuilt) Name(name string) string {
@@ -71,11 +80,16 @@
 		// sources.
 		return PathForModuleSrc(ctx, (*p.srcs)[0])
 	} else {
-		if proptools.String(p.src) == "" {
-			ctx.PropertyErrorf("src", "missing prebuilt source file")
+		if !p.srcProps.IsValid() {
+			ctx.ModuleErrorf("prebuilt source was not set")
+		}
+		src := p.getSingleSourceFieldValue()
+		if src == "" {
+			ctx.PropertyErrorf(proptools.FieldNameForProperty(p.srcField.Name),
+				"missing prebuilt source file")
 			return nil
 		}
-		return PathForModuleSrc(ctx, *p.src)
+		return PathForModuleSrc(ctx, src)
 	}
 }
 
@@ -89,10 +103,12 @@
 	p.srcs = srcs
 }
 
-func InitSingleSourcePrebuiltModule(module PrebuiltInterface, src *string) {
+func InitSingleSourcePrebuiltModule(module PrebuiltInterface, srcProps interface{}, srcField string) {
 	p := module.Prebuilt()
 	module.AddProperties(&p.properties)
-	p.src = src
+	p.srcProps = reflect.ValueOf(srcProps).Elem()
+	p.srcField, _ = p.srcProps.Type().FieldByName(srcField)
+	p.checkSingleSourceProperties()
 }
 
 type PrebuiltInterface interface {
@@ -116,7 +132,7 @@
 		p := m.Prebuilt()
 		name := m.base().BaseModuleName()
 		if ctx.OtherModuleExists(name) {
-			ctx.AddReverseDependency(ctx.Module(), prebuiltDepTag, name)
+			ctx.AddReverseDependency(ctx.Module(), PrebuiltDepTag, name)
 			p.properties.SourceExists = true
 		} else {
 			ctx.Rename(name)
@@ -129,14 +145,14 @@
 func PrebuiltSelectModuleMutator(ctx TopDownMutatorContext) {
 	if m, ok := ctx.Module().(PrebuiltInterface); ok && m.Prebuilt() != nil {
 		p := m.Prebuilt()
-		if p.srcs == nil && p.src == nil {
+		if p.srcs == nil && !p.srcProps.IsValid() {
 			panic(fmt.Errorf("prebuilt module did not have InitPrebuiltModule called on it"))
 		}
 		if !p.properties.SourceExists {
 			p.properties.UsePrebuilt = p.usePrebuilt(ctx, nil)
 		}
 	} else if s, ok := ctx.Module().(Module); ok {
-		ctx.VisitDirectDepsWithTag(prebuiltDepTag, func(m Module) {
+		ctx.VisitDirectDepsWithTag(PrebuiltDepTag, func(m Module) {
 			p := m.(PrebuiltInterface).Prebuilt()
 			if p.usePrebuilt(ctx, s) {
 				p.properties.UsePrebuilt = true
@@ -172,7 +188,7 @@
 		return false
 	}
 
-	if p.src != nil && *p.src == "" {
+	if p.srcProps.IsValid() && p.getSingleSourceFieldValue() == "" {
 		return false
 	}
 
@@ -183,3 +199,28 @@
 
 	return source == nil || !source.Enabled()
 }
+
+func (p *Prebuilt) SourceExists() bool {
+	return p.properties.SourceExists
+}
+
+func (p *Prebuilt) checkSingleSourceProperties() {
+	if !p.srcProps.IsValid() || p.srcField.Name == "" {
+		panic(fmt.Errorf("invalid single source prebuilt %+v", p))
+	}
+
+	if p.srcProps.Kind() != reflect.Struct && p.srcProps.Kind() != reflect.Interface {
+		panic(fmt.Errorf("invalid single source prebuilt %+v", p.srcProps))
+	}
+}
+
+func (p *Prebuilt) getSingleSourceFieldValue() string {
+	value := p.srcProps.FieldByIndex(p.srcField.Index)
+	if value.Kind() == reflect.Ptr {
+		value = value.Elem()
+	}
+	if value.Kind() != reflect.String {
+		panic(fmt.Errorf("prebuilt src field %q should be a string or a pointer to one", p.srcField.Name))
+	}
+	return value.String()
+}
diff --git a/android/prebuilt_etc.go b/android/prebuilt_etc.go
index 069e1f5..388d17f 100644
--- a/android/prebuilt_etc.go
+++ b/android/prebuilt_etc.go
@@ -25,10 +25,6 @@
 	RegisterModuleType("prebuilt_usr_share_host", PrebuiltUserShareHostFactory)
 	RegisterModuleType("prebuilt_font", PrebuiltFontFactory)
 	RegisterModuleType("prebuilt_firmware", PrebuiltFirmwareFactory)
-
-	PreDepsMutators(func(ctx RegisterMutatorsContext) {
-		ctx.BottomUp("prebuilt_etc", prebuiltEtcMutator).Parallel()
-	})
 }
 
 type prebuiltEtcProperties struct {
@@ -48,12 +44,16 @@
 	// Make this module available when building for recovery.
 	Recovery_available *bool
 
-	InRecovery bool `blueprint:"mutated"`
-
 	// 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
 
@@ -65,12 +65,12 @@
 	installDirBase string
 	// The base install location when soc_specific property is set to true, e.g. "firmware" for prebuilt_firmware.
 	socInstallDirBase      string
-	installDirPath         OutputPath
+	installDirPath         InstallPath
 	additionalDependencies *Paths
 }
 
 func (p *PrebuiltEtc) inRecovery() bool {
-	return p.properties.InRecovery || p.ModuleBase.InstallInRecovery()
+	return p.ModuleBase.InRecovery() || p.ModuleBase.InstallInRecovery()
 }
 
 func (p *PrebuiltEtc) onlyInRecovery() bool {
@@ -81,6 +81,25 @@
 	return p.inRecovery()
 }
 
+var _ ImageInterface = (*PrebuiltEtc)(nil)
+
+func (p *PrebuiltEtc) ImageMutatorBegin(ctx BaseModuleContext) {}
+
+func (p *PrebuiltEtc) CoreVariantNeeded(ctx BaseModuleContext) bool {
+	return !p.ModuleBase.InstallInRecovery()
+}
+
+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")
@@ -91,6 +110,10 @@
 	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) {
@@ -142,38 +165,41 @@
 	})
 }
 
-func (p *PrebuiltEtc) AndroidMkEntries() AndroidMkEntries {
+func (p *PrebuiltEtc) AndroidMkEntries() []AndroidMkEntries {
 	nameSuffix := ""
 	if p.inRecovery() && !p.onlyInRecovery() {
 		nameSuffix = ".recovery"
 	}
-	return AndroidMkEntries{
+	return []AndroidMkEntries{AndroidMkEntries{
 		Class:      "ETC",
 		SubName:    nameSuffix,
 		OutputFile: OptionalPathForPath(p.outputFilePath),
-		AddCustomEntries: func(name, prefix, moduleDir string, entries *AndroidMkEntries) {
-			entries.SetString("LOCAL_MODULE_TAGS", "optional")
-			entries.SetString("LOCAL_MODULE_PATH", "$(OUT_DIR)/"+p.installDirPath.RelPathString())
-			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())
+		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) {
+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{installDirBase: "etc"}
-	InitPrebuiltEtcModule(module)
+	module := &PrebuiltEtc{}
+	InitPrebuiltEtcModule(module, "etc")
 	// This module is device-only
 	InitAndroidArchModule(module, DeviceSupported, MultilibFirst)
 	return module
@@ -182,8 +208,8 @@
 // prebuilt_etc_host is for a host prebuilt artifact that is installed in
 // $(HOST_OUT)/etc/<sub_dir> directory.
 func PrebuiltEtcHostFactory() Module {
-	module := &PrebuiltEtc{installDirBase: "etc"}
-	InitPrebuiltEtcModule(module)
+	module := &PrebuiltEtc{}
+	InitPrebuiltEtcModule(module, "etc")
 	// This module is host-only
 	InitAndroidArchModule(module, HostSupported, MultilibCommon)
 	return module
@@ -192,8 +218,8 @@
 // prebuilt_usr_share is for a prebuilt artifact that is installed in
 // <partition>/usr/share/<sub_dir> directory.
 func PrebuiltUserShareFactory() Module {
-	module := &PrebuiltEtc{installDirBase: "usr/share"}
-	InitPrebuiltEtcModule(module)
+	module := &PrebuiltEtc{}
+	InitPrebuiltEtcModule(module, "usr/share")
 	// This module is device-only
 	InitAndroidArchModule(module, DeviceSupported, MultilibFirst)
 	return module
@@ -202,60 +228,17 @@
 // 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{installDirBase: "usr/share"}
-	InitPrebuiltEtcModule(module)
+	module := &PrebuiltEtc{}
+	InitPrebuiltEtcModule(module, "usr/share")
 	// This module is host-only
 	InitAndroidArchModule(module, HostSupported, MultilibCommon)
 	return module
 }
 
-const (
-	// coreMode is the variant for modules to be installed to system.
-	coreMode = "core"
-
-	// recoveryMode means a module to be installed to recovery image.
-	recoveryMode = "recovery"
-)
-
-// prebuiltEtcMutator creates the needed variants to install the module to
-// system or recovery.
-func prebuiltEtcMutator(mctx BottomUpMutatorContext) {
-	m, ok := mctx.Module().(*PrebuiltEtc)
-	if !ok || m.Host() {
-		return
-	}
-
-	var coreVariantNeeded bool = true
-	var recoveryVariantNeeded bool = false
-	if Bool(m.properties.Recovery_available) {
-		recoveryVariantNeeded = true
-	}
-
-	if m.ModuleBase.InstallInRecovery() {
-		recoveryVariantNeeded = true
-		coreVariantNeeded = false
-	}
-
-	var variants []string
-	if coreVariantNeeded {
-		variants = append(variants, coreMode)
-	}
-	if recoveryVariantNeeded {
-		variants = append(variants, recoveryMode)
-	}
-	mod := mctx.CreateVariations(variants...)
-	for i, v := range variants {
-		if v == recoveryMode {
-			m := mod[i].(*PrebuiltEtc)
-			m.properties.InRecovery = true
-		}
-	}
-}
-
 // prebuilt_font installs a font in <partition>/fonts directory.
 func PrebuiltFontFactory() Module {
-	module := &PrebuiltEtc{installDirBase: "fonts"}
-	InitPrebuiltEtcModule(module)
+	module := &PrebuiltEtc{}
+	InitPrebuiltEtcModule(module, "fonts")
 	// This module is device-only
 	InitAndroidArchModule(module, DeviceSupported, MultilibFirst)
 	return module
@@ -265,8 +248,9 @@
 // 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{installDirBase: "etc/firmware", socInstallDirBase: "firmware"}
-	InitPrebuiltEtcModule(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_etc_test.go b/android/prebuilt_etc_test.go
index 0a2c7a4..6e751e7 100644
--- a/android/prebuilt_etc_test.go
+++ b/android/prebuilt_etc_test.go
@@ -21,25 +21,22 @@
 )
 
 func testPrebuiltEtc(t *testing.T, bp string) (*TestContext, Config) {
-	config := TestArchConfig(buildDir, nil)
-	ctx := NewTestArchContext()
-	ctx.RegisterModuleType("prebuilt_etc", ModuleFactoryAdaptor(PrebuiltEtcFactory))
-	ctx.RegisterModuleType("prebuilt_etc_host", ModuleFactoryAdaptor(PrebuiltEtcHostFactory))
-	ctx.RegisterModuleType("prebuilt_usr_share", ModuleFactoryAdaptor(PrebuiltUserShareFactory))
-	ctx.RegisterModuleType("prebuilt_usr_share_host", ModuleFactoryAdaptor(PrebuiltUserShareHostFactory))
-	ctx.RegisterModuleType("prebuilt_font", ModuleFactoryAdaptor(PrebuiltFontFactory))
-	ctx.RegisterModuleType("prebuilt_firmware", ModuleFactoryAdaptor(PrebuiltFirmwareFactory))
-	ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
-		ctx.BottomUp("prebuilt_etc", prebuiltEtcMutator).Parallel()
-	})
-	ctx.Register()
-	mockFiles := map[string][]byte{
-		"Android.bp": []byte(bp),
-		"foo.conf":   nil,
-		"bar.conf":   nil,
-		"baz.conf":   nil,
+	fs := map[string][]byte{
+		"foo.conf": nil,
+		"bar.conf": nil,
+		"baz.conf": nil,
 	}
-	ctx.MockFileSystem(mockFiles)
+
+	config := TestArchConfig(buildDir, nil, bp, fs)
+
+	ctx := NewTestArchContext()
+	ctx.RegisterModuleType("prebuilt_etc", PrebuiltEtcFactory)
+	ctx.RegisterModuleType("prebuilt_etc_host", PrebuiltEtcHostFactory)
+	ctx.RegisterModuleType("prebuilt_usr_share", PrebuiltUserShareFactory)
+	ctx.RegisterModuleType("prebuilt_usr_share_host", PrebuiltUserShareHostFactory)
+	ctx.RegisterModuleType("prebuilt_font", PrebuiltFontFactory)
+	ctx.RegisterModuleType("prebuilt_firmware", PrebuiltFirmwareFactory)
+	ctx.Register(config)
 	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
 	FailIfErrored(t, errs)
 	_, errs = ctx.PrepareBuildActions(config)
@@ -91,7 +88,7 @@
 		}
 	`)
 
-	p := ctx.ModuleForTests("foo.conf", "android_arm64_armv8-a_core").Module().(*PrebuiltEtc)
+	p := ctx.ModuleForTests("foo.conf", "android_arm64_armv8-a").Module().(*PrebuiltEtc)
 	if p.outputFilePath.Base() != "foo.installed.conf" {
 		t.Errorf("expected foo.installed.conf, got %q", p.outputFilePath.Base())
 	}
@@ -110,12 +107,12 @@
 		}
 	`)
 
-	p := ctx.ModuleForTests("my_foo", "android_arm64_armv8-a_core").Module().(*PrebuiltEtc)
+	p := ctx.ModuleForTests("my_foo", "android_arm64_armv8-a").Module().(*PrebuiltEtc)
 	if p.outputFilePath.Base() != "my_foo" {
 		t.Errorf("expected my_foo, got %q", p.outputFilePath.Base())
 	}
 
-	p = ctx.ModuleForTests("my_bar", "android_arm64_armv8-a_core").Module().(*PrebuiltEtc)
+	p = ctx.ModuleForTests("my_bar", "android_arm64_armv8-a").Module().(*PrebuiltEtc)
 	if p.outputFilePath.Base() != "bar.conf" {
 		t.Errorf("expected bar.conf, got %q", p.outputFilePath.Base())
 	}
@@ -144,8 +141,8 @@
 		"LOCAL_TARGET_REQUIRED_MODULES": {"targetModA"},
 	}
 
-	mod := ctx.ModuleForTests("foo", "android_arm64_armv8-a_core").Module().(*PrebuiltEtc)
-	entries := AndroidMkEntriesForTest(t, config, "", mod)
+	mod := ctx.ModuleForTests("foo", "android_arm64_armv8-a").Module().(*PrebuiltEtc)
+	entries := AndroidMkEntriesForTest(t, config, "", mod)[0]
 	for k, expectedValue := range expected {
 		if value, ok := entries.EntryMap[k]; ok {
 			if !reflect.DeepEqual(value, expectedValue) {
@@ -181,10 +178,10 @@
 		}
 	`)
 
-	p := ctx.ModuleForTests("foo.conf", "android_arm64_armv8-a_core").Module().(*PrebuiltEtc)
-	expected := "target/product/test_device/system/usr/share/bar"
-	if p.installDirPath.RelPathString() != expected {
-		t.Errorf("expected %q, got %q", expected, p.installDirPath.RelPathString())
+	p := ctx.ModuleForTests("foo.conf", "android_arm64_armv8-a").Module().(*PrebuiltEtc)
+	expected := buildDir + "/target/product/test_device/system/usr/share/bar"
+	if p.installDirPath.String() != expected {
+		t.Errorf("expected %q, got %q", expected, p.installDirPath.String())
 	}
 }
 
@@ -199,9 +196,9 @@
 
 	buildOS := BuildOs.String()
 	p := ctx.ModuleForTests("foo.conf", buildOS+"_common").Module().(*PrebuiltEtc)
-	expected := filepath.Join("host", config.PrebuiltOS(), "usr", "share", "bar")
-	if p.installDirPath.RelPathString() != expected {
-		t.Errorf("expected %q, got %q", expected, p.installDirPath.RelPathString())
+	expected := filepath.Join(buildDir, "host", config.PrebuiltOS(), "usr", "share", "bar")
+	if p.installDirPath.String() != expected {
+		t.Errorf("expected %q, got %q", expected, p.installDirPath.String())
 	}
 }
 
@@ -213,15 +210,15 @@
 		}
 	`)
 
-	p := ctx.ModuleForTests("foo.conf", "android_arm64_armv8-a_core").Module().(*PrebuiltEtc)
-	expected := "target/product/test_device/system/fonts"
-	if p.installDirPath.RelPathString() != expected {
-		t.Errorf("expected %q, got %q", expected, p.installDirPath.RelPathString())
+	p := ctx.ModuleForTests("foo.conf", "android_arm64_armv8-a").Module().(*PrebuiltEtc)
+	expected := buildDir + "/target/product/test_device/system/fonts"
+	if p.installDirPath.String() != expected {
+		t.Errorf("expected %q, got %q", expected, p.installDirPath.String())
 	}
 }
 
 func TestPrebuiltFirmwareDirPath(t *testing.T) {
-	targetPath := "target/product/test_device"
+	targetPath := buildDir + "/target/product/test_device"
 	tests := []struct {
 		description  string
 		config       string
@@ -248,8 +245,8 @@
 	for _, tt := range tests {
 		t.Run(tt.description, func(t *testing.T) {
 			ctx, _ := testPrebuiltEtc(t, tt.config)
-			p := ctx.ModuleForTests("foo.conf", "android_arm64_armv8-a_core").Module().(*PrebuiltEtc)
-			if p.installDirPath.RelPathString() != tt.expectedPath {
+			p := ctx.ModuleForTests("foo.conf", "android_arm64_armv8-a").Module().(*PrebuiltEtc)
+			if p.installDirPath.String() != tt.expectedPath {
 				t.Errorf("expected %q, got %q", tt.expectedPath, p.installDirPath)
 			}
 		})
diff --git a/android/prebuilt_test.go b/android/prebuilt_test.go
index 0a18e2c..8648b93 100644
--- a/android/prebuilt_test.go
+++ b/android/prebuilt_test.go
@@ -125,29 +125,29 @@
 }
 
 func TestPrebuilts(t *testing.T) {
-	config := TestConfig(buildDir, nil)
+	fs := map[string][]byte{
+		"prebuilt_file": nil,
+		"source_file":   nil,
+	}
 
 	for _, test := range prebuiltsTests {
 		t.Run(test.name, func(t *testing.T) {
-			ctx := NewTestContext()
-			ctx.PreArchMutators(RegisterPrebuiltsPreArchMutators)
-			ctx.PostDepsMutators(RegisterPrebuiltsPostDepsMutators)
-			ctx.RegisterModuleType("filegroup", ModuleFactoryAdaptor(FileGroupFactory))
-			ctx.RegisterModuleType("prebuilt", ModuleFactoryAdaptor(newPrebuiltModule))
-			ctx.RegisterModuleType("source", ModuleFactoryAdaptor(newSourceModule))
-			ctx.Register()
-			ctx.MockFileSystem(map[string][]byte{
-				"prebuilt_file": nil,
-				"source_file":   nil,
-				"Blueprints": []byte(`
-					source {
-						name: "foo",
-						deps: [":bar"],
-					}
-					` + test.modules),
-			})
+			bp := `
+				source {
+					name: "foo",
+					deps: [":bar"],
+				}
+				` + test.modules
+			config := TestConfig(buildDir, nil, bp, fs)
 
-			_, errs := ctx.ParseBlueprintsFiles("Blueprints")
+			ctx := NewTestContext()
+			RegisterPrebuiltMutators(ctx)
+			ctx.RegisterModuleType("filegroup", FileGroupFactory)
+			ctx.RegisterModuleType("prebuilt", newPrebuiltModule)
+			ctx.RegisterModuleType("source", newSourceModule)
+			ctx.Register(config)
+
+			_, errs := ctx.ParseBlueprintsFiles("Android.bp")
 			FailIfErrored(t, errs)
 			_, errs = ctx.PrepareBuildActions(config)
 			FailIfErrored(t, errs)
diff --git a/android/proto.go b/android/proto.go
index 5247c68..b712258 100644
--- a/android/proto.go
+++ b/android/proto.go
@@ -52,9 +52,8 @@
 	}
 
 	if plugin := String(p.Proto.Plugin); plugin != "" {
-		ctx.AddFarVariationDependencies([]blueprint.Variation{
-			{Mutator: "arch", Variation: ctx.Config().BuildOsVariant},
-		}, ProtoPluginDepTag, "protoc-gen-"+plugin)
+		ctx.AddFarVariationDependencies(ctx.Config().BuildOSTarget.Variations(),
+			ProtoPluginDepTag, "protoc-gen-"+plugin)
 	}
 }
 
@@ -135,7 +134,7 @@
 	}
 
 	rule.Command().
-		Tool(ctx.Config().HostToolPath(ctx, "aprotoc")).
+		BuiltTool(ctx, "aprotoc").
 		FlagWithArg(flags.OutTypeFlag+"=", strings.Join(flags.OutParams, ",")+":"+outDir.String()).
 		FlagWithDepFile("--dependency_out=", depFile).
 		FlagWithArg("-I ", protoBase).
@@ -145,5 +144,5 @@
 		ImplicitOutputs(outputs)
 
 	rule.Command().
-		Tool(ctx.Config().HostToolPath(ctx, "dep_fixer")).Flag(depFile.String())
+		BuiltTool(ctx, "dep_fixer").Flag(depFile.String())
 }
diff --git a/android/register.go b/android/register.go
index d79982a..b48d3d1 100644
--- a/android/register.go
+++ b/android/register.go
@@ -15,6 +15,8 @@
 package android
 
 import (
+	"fmt"
+
 	"github.com/google/blueprint"
 )
 
@@ -82,7 +84,9 @@
 }
 
 func NewContext() *Context {
-	return &Context{blueprint.NewContext()}
+	ctx := &Context{blueprint.NewContext()}
+	ctx.SetSrcDir(absSrcDir)
+	return ctx
 }
 
 func (ctx *Context) Register() {
@@ -114,3 +118,72 @@
 	}
 	return ret
 }
+
+// Interface for registering build components.
+//
+// Provided to allow registration of build components to be shared between the runtime
+// and test environments.
+type RegistrationContext interface {
+	RegisterModuleType(name string, factory ModuleFactory)
+	RegisterSingletonType(name string, factory SingletonFactory)
+	PreArchMutators(f RegisterMutatorFunc)
+	PreDepsMutators(f RegisterMutatorFunc)
+	PostDepsMutators(f RegisterMutatorFunc)
+}
+
+// Used to register build components from an init() method, e.g.
+//
+// init() {
+//   RegisterBuildComponents(android.InitRegistrationContext)
+// }
+//
+// func RegisterBuildComponents(ctx android.RegistrationContext) {
+//   ctx.RegisterModuleType(...)
+//   ...
+// }
+//
+// Extracting the actual registration into a separate RegisterBuildComponents(ctx) function
+// allows it to be used to initialize test context, e.g.
+//
+//   ctx := android.NewTestContext()
+//   RegisterBuildComponents(ctx)
+var InitRegistrationContext RegistrationContext = &initRegistrationContext{
+	moduleTypes:    make(map[string]ModuleFactory),
+	singletonTypes: make(map[string]SingletonFactory),
+}
+
+// Make sure the TestContext implements RegistrationContext.
+var _ RegistrationContext = (*TestContext)(nil)
+
+type initRegistrationContext struct {
+	moduleTypes    map[string]ModuleFactory
+	singletonTypes map[string]SingletonFactory
+}
+
+func (ctx *initRegistrationContext) RegisterModuleType(name string, factory ModuleFactory) {
+	if _, present := ctx.moduleTypes[name]; present {
+		panic(fmt.Sprintf("module type %q is already registered", name))
+	}
+	ctx.moduleTypes[name] = factory
+	RegisterModuleType(name, factory)
+}
+
+func (ctx *initRegistrationContext) RegisterSingletonType(name string, factory SingletonFactory) {
+	if _, present := ctx.singletonTypes[name]; present {
+		panic(fmt.Sprintf("singleton type %q is already registered", name))
+	}
+	ctx.singletonTypes[name] = factory
+	RegisterSingletonType(name, factory)
+}
+
+func (ctx *initRegistrationContext) PreArchMutators(f RegisterMutatorFunc) {
+	PreArchMutators(f)
+}
+
+func (ctx *initRegistrationContext) PreDepsMutators(f RegisterMutatorFunc) {
+	PreDepsMutators(f)
+}
+
+func (ctx *initRegistrationContext) PostDepsMutators(f RegisterMutatorFunc) {
+	PostDepsMutators(f)
+}
diff --git a/android/rule_builder.go b/android/rule_builder.go
index 8d7e74b..6f04672 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -263,11 +263,36 @@
 	return toolsList
 }
 
-// Commands returns a slice containing a the built command line for each call to RuleBuilder.Command.
+// RspFileInputs returns the list of paths that were passed to the RuleBuilderCommand.FlagWithRspFileInputList method.
+func (r *RuleBuilder) RspFileInputs() Paths {
+	var rspFileInputs Paths
+	for _, c := range r.commands {
+		if c.rspFileInputs != nil {
+			if rspFileInputs != nil {
+				panic("Multiple commands in a rule may not have rsp file inputs")
+			}
+			rspFileInputs = c.rspFileInputs
+		}
+	}
+
+	return rspFileInputs
+}
+
+// Commands returns a slice containing the built command line for each call to RuleBuilder.Command.
 func (r *RuleBuilder) Commands() []string {
 	var commands []string
 	for _, c := range r.commands {
-		commands = append(commands, string(c.buf))
+		commands = append(commands, c.String())
+	}
+	return commands
+}
+
+// NinjaEscapedCommands returns a slice containin the built command line after ninja escaping for each call to
+// RuleBuilder.Command.
+func (r *RuleBuilder) NinjaEscapedCommands() []string {
+	var commands []string
+	for _, c := range r.commands {
+		commands = append(commands, c.NinjaEscapedString())
 	}
 	return commands
 }
@@ -284,7 +309,7 @@
 
 func (r *RuleBuilder) depFileMergerCmd(ctx PathContext, depFiles WritablePaths) *RuleBuilderCommand {
 	return r.Command().
-		Tool(ctx.Config().HostToolPath(ctx, "dep_fixer")).
+		BuiltTool(ctx, "dep_fixer").
 		Inputs(depFiles.Paths())
 }
 
@@ -324,7 +349,7 @@
 	}
 
 	tools := r.Tools()
-	commands := r.Commands()
+	commands := r.NinjaEscapedCommands()
 	outputs := r.Outputs()
 
 	if len(commands) == 0 {
@@ -334,7 +359,7 @@
 		panic("No outputs specified from any Commands")
 	}
 
-	commandString := strings.Join(proptools.NinjaEscapeList(commands), " && ")
+	commandString := strings.Join(commands, " && ")
 
 	if r.sbox {
 		sboxOutputs := make([]string, len(outputs))
@@ -342,38 +367,49 @@
 			sboxOutputs[i] = "__SBOX_OUT_DIR__/" + Rel(ctx, r.sboxOutDir.String(), output.String())
 		}
 
-		if depFile != nil {
-			sboxOutputs = append(sboxOutputs, "__SBOX_OUT_DIR__/"+Rel(ctx, r.sboxOutDir.String(), depFile.String()))
-		}
-
 		commandString = proptools.ShellEscape(commandString)
 		if !strings.HasPrefix(commandString, `'`) {
 			commandString = `'` + commandString + `'`
 		}
 
 		sboxCmd := &RuleBuilderCommand{}
-		sboxCmd.Tool(ctx.Config().HostToolPath(ctx, "sbox")).
+		sboxCmd.BuiltTool(ctx, "sbox").
 			Flag("-c").Text(commandString).
 			Flag("--sandbox-path").Text(shared.TempDirForOutDir(PathForOutput(ctx).String())).
-			Flag("--output-root").Text(r.sboxOutDir.String()).
-			Flags(sboxOutputs)
+			Flag("--output-root").Text(r.sboxOutDir.String())
 
-		commandString = string(sboxCmd.buf)
+		if depFile != nil {
+			sboxCmd.Flag("--depfile-out").Text(depFile.String())
+		}
+
+		sboxCmd.Flags(sboxOutputs)
+
+		commandString = sboxCmd.buf.String()
 		tools = append(tools, sboxCmd.tools...)
 	}
 
 	// Ninja doesn't like multiple outputs when depfiles are enabled, move all but the first output to
-	// ImplicitOutputs.  RuleBuilder never uses "$out", so the distinction between Outputs and ImplicitOutputs
-	// doesn't matter.
+	// ImplicitOutputs.  RuleBuilder only uses "$out" for the rsp file location, so the distinction between Outputs and
+	// ImplicitOutputs doesn't matter.
 	output := outputs[0]
 	implicitOutputs := outputs[1:]
 
+	var rspFile, rspFileContent string
+	rspFileInputs := r.RspFileInputs()
+	if rspFileInputs != nil {
+		rspFile = "$out.rsp"
+		rspFileContent = "$in"
+	}
+
 	ctx.Build(pctx, BuildParams{
 		Rule: ctx.Rule(pctx, name, blueprint.RuleParams{
-			Command:     commandString,
-			CommandDeps: tools.Strings(),
-			Restat:      r.restat,
+			Command:        commandString,
+			CommandDeps:    tools.Strings(),
+			Restat:         r.restat,
+			Rspfile:        rspFile,
+			RspfileContent: rspFileContent,
 		}),
+		Inputs:          rspFileInputs,
 		Implicits:       r.Inputs(),
 		Output:          output,
 		ImplicitOutputs: implicitOutputs,
@@ -388,11 +424,15 @@
 // RuleBuilderCommand, so they can be used chained or unchained.  All methods that add text implicitly add a single
 // space as a separator from the previous method.
 type RuleBuilderCommand struct {
-	buf      []byte
-	inputs   Paths
-	outputs  WritablePaths
-	depFiles WritablePaths
-	tools    Paths
+	buf           strings.Builder
+	inputs        Paths
+	outputs       WritablePaths
+	depFiles      WritablePaths
+	tools         Paths
+	rspFileInputs Paths
+
+	// spans [start,end) of the command that should not be ninja escaped
+	unescapedSpans [][2]int
 
 	sbox       bool
 	sboxOutDir WritablePath
@@ -420,10 +460,10 @@
 // Text adds the specified raw text to the command line.  The text should not contain input or output paths or the
 // rule will not have them listed in its dependencies or outputs.
 func (c *RuleBuilderCommand) Text(text string) *RuleBuilderCommand {
-	if len(c.buf) > 0 {
-		c.buf = append(c.buf, ' ')
+	if c.buf.Len() > 0 {
+		c.buf.WriteByte(' ')
 	}
-	c.buf = append(c.buf, text...)
+	c.buf.WriteString(text)
 	return c
 }
 
@@ -439,6 +479,16 @@
 	return c.Text(flag)
 }
 
+// OptionalFlag adds the specified raw text to the command line if it is not nil.  The text should not contain input or
+// output paths or the rule will not have them listed in its dependencies or outputs.
+func (c *RuleBuilderCommand) OptionalFlag(flag *string) *RuleBuilderCommand {
+	if flag != nil {
+		c.Text(*flag)
+	}
+
+	return c
+}
+
 // Flags adds the specified raw text to the command line.  The text should not contain input or output paths or the
 // rule will not have them listed in its dependencies or outputs.
 func (c *RuleBuilderCommand) Flags(flags []string) *RuleBuilderCommand {
@@ -478,6 +528,24 @@
 	return c.Text(path.String())
 }
 
+// BuiltTool adds the specified tool path that was built using a host Soong module to the command line.  The path will
+// be also added to the dependencies returned by RuleBuilder.Tools.
+//
+// It is equivalent to:
+//  cmd.Tool(ctx.Config().HostToolPath(ctx, tool))
+func (c *RuleBuilderCommand) BuiltTool(ctx PathContext, tool string) *RuleBuilderCommand {
+	return c.Tool(ctx.Config().HostToolPath(ctx, tool))
+}
+
+// PrebuiltBuildTool adds the specified tool path from prebuils/build-tools.  The path will be also added to the
+// dependencies returned by RuleBuilder.Tools.
+//
+// It is equivalent to:
+//  cmd.Tool(ctx.Config().PrebuiltBuildTool(ctx, tool))
+func (c *RuleBuilderCommand) PrebuiltBuildTool(ctx PathContext, tool string) *RuleBuilderCommand {
+	return c.Tool(ctx.Config().PrebuiltBuildTool(ctx, tool))
+}
+
 // Input adds the specified input path to the command line.  The path will also be added to the dependencies returned by
 // RuleBuilder.Inputs.
 func (c *RuleBuilderCommand) Input(path Path) *RuleBuilderCommand {
@@ -606,9 +674,54 @@
 	return c.Text(flag + c.outputStr(path))
 }
 
+// FlagWithRspFileInputList adds the specified flag and path to an rspfile to the command line, with no separator
+// between them.  The paths will be written to the rspfile.
+func (c *RuleBuilderCommand) FlagWithRspFileInputList(flag string, paths Paths) *RuleBuilderCommand {
+	if c.rspFileInputs != nil {
+		panic("FlagWithRspFileInputList cannot be called if rsp file inputs have already been provided")
+	}
+
+	// Use an empty slice if paths is nil, the non-nil slice is used as an indicator that the rsp file must be
+	// generated.
+	if paths == nil {
+		paths = Paths{}
+	}
+
+	c.rspFileInputs = paths
+
+	rspFile := "$out.rsp"
+	c.FlagWithArg(flag, rspFile)
+	c.unescapedSpans = append(c.unescapedSpans, [2]int{c.buf.Len() - len(rspFile), c.buf.Len()})
+	return c
+}
+
 // String returns the command line.
 func (c *RuleBuilderCommand) String() string {
-	return string(c.buf)
+	return c.buf.String()
+}
+
+// String returns the command line.
+func (c *RuleBuilderCommand) NinjaEscapedString() string {
+	return ninjaEscapeExceptForSpans(c.String(), c.unescapedSpans)
+}
+
+func ninjaEscapeExceptForSpans(s string, spans [][2]int) string {
+	if len(spans) == 0 {
+		return proptools.NinjaEscape(s)
+	}
+
+	sb := strings.Builder{}
+	sb.Grow(len(s) * 11 / 10)
+
+	i := 0
+	for _, span := range spans {
+		sb.WriteString(proptools.NinjaEscape(s[i:span[0]]))
+		sb.WriteString(s[span[0]:span[1]])
+		i = span[1]
+	}
+	sb.WriteString(proptools.NinjaEscape(s[i:]))
+
+	return sb.String()
 }
 
 func ninjaNameEscape(s string) string {
diff --git a/android/rule_builder_test.go b/android/rule_builder_test.go
index cfbc2ab..acf8127 100644
--- a/android/rule_builder_test.go
+++ b/android/rule_builder_test.go
@@ -27,18 +27,18 @@
 )
 
 func pathContext() PathContext {
-	return PathContextForTesting(TestConfig("out", nil),
-		map[string][]byte{
-			"ld":      nil,
-			"a.o":     nil,
-			"b.o":     nil,
-			"cp":      nil,
-			"a":       nil,
-			"b":       nil,
-			"ls":      nil,
-			"turbine": nil,
-			"java":    nil,
-		})
+	return PathContextForTesting(TestConfig("out", nil, "", map[string][]byte{
+		"ld":      nil,
+		"a.o":     nil,
+		"b.o":     nil,
+		"cp":      nil,
+		"a":       nil,
+		"b":       nil,
+		"ls":      nil,
+		"turbine": nil,
+		"java":    nil,
+		"javac":   nil,
+	}))
 }
 
 func ExampleRuleBuilder() {
@@ -235,6 +235,34 @@
 	// ls --sort=time,size
 }
 
+func ExampleRuleBuilderCommand_FlagWithRspFileInputList() {
+	ctx := pathContext()
+	fmt.Println(NewRuleBuilder().Command().
+		Tool(PathForSource(ctx, "javac")).
+		FlagWithRspFileInputList("@", PathsForTesting("a.java", "b.java")).
+		NinjaEscapedString())
+	// Output:
+	// javac @$out.rsp
+}
+
+func ExampleRuleBuilderCommand_String() {
+	fmt.Println(NewRuleBuilder().Command().
+		Text("FOO=foo").
+		Text("echo $FOO").
+		String())
+	// Output:
+	// FOO=foo echo $FOO
+}
+
+func ExampleRuleBuilderCommand_NinjaEscapedString() {
+	fmt.Println(NewRuleBuilder().Command().
+		Text("FOO=foo").
+		Text("echo $FOO").
+		NinjaEscapedString())
+	// Output:
+	// FOO=foo echo $$FOO
+}
+
 func TestRuleBuilder(t *testing.T) {
 	fs := map[string][]byte{
 		"dep_fixer": nil,
@@ -247,7 +275,7 @@
 		"input3":    nil,
 	}
 
-	ctx := PathContextForTesting(TestConfig("out", nil), fs)
+	ctx := PathContextForTesting(TestConfig("out", nil, "", fs))
 
 	addCommands := func(rule *RuleBuilder) {
 		cmd := rule.Command().
@@ -416,6 +444,11 @@
 }
 
 func TestRuleBuilder_Build(t *testing.T) {
+	fs := map[string][]byte{
+		"bar": nil,
+		"cp":  nil,
+	}
+
 	bp := `
 		rule_builder_test {
 			name: "foo",
@@ -429,16 +462,11 @@
 		}
 	`
 
-	config := TestConfig(buildDir, nil)
+	config := TestConfig(buildDir, nil, bp, fs)
 	ctx := NewTestContext()
-	ctx.MockFileSystem(map[string][]byte{
-		"Android.bp": []byte(bp),
-		"bar":        nil,
-		"cp":         nil,
-	})
-	ctx.RegisterModuleType("rule_builder_test", ModuleFactoryAdaptor(testRuleBuilderFactory))
-	ctx.RegisterSingletonType("rule_builder_test", SingletonFactoryAdaptor(testRuleBuilderSingletonFactory))
-	ctx.Register()
+	ctx.RegisterModuleType("rule_builder_test", testRuleBuilderFactory)
+	ctx.RegisterSingletonType("rule_builder_test", testRuleBuilderSingletonFactory)
+	ctx.Register(config)
 
 	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
 	FailIfErrored(t, errs)
@@ -446,6 +474,7 @@
 	FailIfErrored(t, errs)
 
 	check := func(t *testing.T, params TestingBuildParams, wantCommand, wantOutput, wantDepfile string, wantRestat bool, extraCmdDeps []string) {
+		t.Helper()
 		if params.RuleParams.Command != wantCommand {
 			t.Errorf("\nwant RuleParams.Command = %q\n                      got %q", wantCommand, params.RuleParams.Command)
 		}
@@ -489,13 +518,14 @@
 	t.Run("sbox", func(t *testing.T) {
 		outDir := filepath.Join(buildDir, ".intermediates", "foo_sbox")
 		outFile := filepath.Join(outDir, "foo_sbox")
+		depFile := filepath.Join(outDir, "foo_sbox.d")
 		sbox := filepath.Join(buildDir, "host", config.PrebuiltOS(), "bin/sbox")
 		sandboxPath := shared.TempDirForOutDir(buildDir)
 
-		cmd := sbox + ` -c 'cp bar __SBOX_OUT_DIR__/foo_sbox' --sandbox-path ` + sandboxPath + " --output-root " + outDir + " __SBOX_OUT_DIR__/foo_sbox __SBOX_OUT_DIR__/foo_sbox.d"
+		cmd := sbox + ` -c 'cp bar __SBOX_OUT_DIR__/foo_sbox' --sandbox-path ` + sandboxPath + " --output-root " + outDir + " --depfile-out " + depFile + " __SBOX_OUT_DIR__/foo_sbox"
 
 		check(t, ctx.ModuleForTests("foo_sbox", "").Rule("rule"),
-			cmd, outFile, outFile+".d", false, []string{sbox})
+			cmd, outFile, depFile, false, []string{sbox})
 	})
 	t.Run("singleton", func(t *testing.T) {
 		outFile := filepath.Join(buildDir, "baz")
@@ -503,3 +533,77 @@
 			"cp bar "+outFile, outFile, outFile+".d", true, nil)
 	})
 }
+
+func Test_ninjaEscapeExceptForSpans(t *testing.T) {
+	type args struct {
+		s     string
+		spans [][2]int
+	}
+	tests := []struct {
+		name string
+		args args
+		want string
+	}{
+		{
+			name: "empty",
+			args: args{
+				s: "",
+			},
+			want: "",
+		},
+		{
+			name: "unescape none",
+			args: args{
+				s: "$abc",
+			},
+			want: "$$abc",
+		},
+		{
+			name: "unescape all",
+			args: args{
+				s:     "$abc",
+				spans: [][2]int{{0, 4}},
+			},
+			want: "$abc",
+		},
+		{
+			name: "unescape first",
+			args: args{
+				s:     "$abc$",
+				spans: [][2]int{{0, 1}},
+			},
+			want: "$abc$$",
+		},
+		{
+			name: "unescape last",
+			args: args{
+				s:     "$abc$",
+				spans: [][2]int{{4, 5}},
+			},
+			want: "$$abc$",
+		},
+		{
+			name: "unescape middle",
+			args: args{
+				s:     "$a$b$c$",
+				spans: [][2]int{{2, 5}},
+			},
+			want: "$$a$b$c$$",
+		},
+		{
+			name: "unescape multiple",
+			args: args{
+				s:     "$a$b$c$",
+				spans: [][2]int{{2, 3}, {4, 5}},
+			},
+			want: "$$a$b$c$$",
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if got := ninjaEscapeExceptForSpans(tt.args.s, tt.args.spans); got != tt.want {
+				t.Errorf("ninjaEscapeExceptForSpans() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
diff --git a/android/sandbox.go b/android/sandbox.go
new file mode 100644
index 0000000..ed022fb
--- /dev/null
+++ b/android/sandbox.go
@@ -0,0 +1,47 @@
+// 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 android
+
+import (
+	"fmt"
+	"os"
+)
+
+func init() {
+	// Stash the working directory in a private variable and then change the working directory
+	// to "/", which will prevent untracked accesses to files by Go Soong plugins. The
+	// SOONG_SANDBOX_SOONG_BUILD environment variable is set by soong_ui, and is not
+	// overrideable on the command line.
+
+	orig, err := os.Getwd()
+	if err != nil {
+		panic(fmt.Errorf("failed to get working directory: %s", err))
+	}
+	absSrcDir = orig
+
+	if getenv("SOONG_SANDBOX_SOONG_BUILD") == "true" {
+		err = os.Chdir("/")
+		if err != nil {
+			panic(fmt.Errorf("failed to change working directory to '/': %s", err))
+		}
+	}
+}
+
+// DO NOT USE THIS FUNCTION IN NEW CODE.
+// Deprecated: This function will be removed as soon as the existing use cases that use it have been
+// replaced.
+func AbsSrcDirForExistingUseCases() string {
+	return absSrcDir
+}
diff --git a/android/sdk.go b/android/sdk.go
new file mode 100644
index 0000000..9e6ad16
--- /dev/null
+++ b/android/sdk.go
@@ -0,0 +1,346 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import (
+	"sort"
+	"strings"
+
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+)
+
+// SdkAware is the interface that must be supported by any module to become a member of SDK or to be
+// built with SDK
+type SdkAware interface {
+	Module
+	sdkBase() *SdkBase
+	MakeMemberOf(sdk SdkRef)
+	IsInAnySdk() bool
+	ContainingSdk() SdkRef
+	MemberName() string
+	BuildWithSdks(sdks SdkRefs)
+	RequiredSdks() SdkRefs
+}
+
+// SdkRef refers to a version of an SDK
+type SdkRef struct {
+	Name    string
+	Version string
+}
+
+// Unversioned determines if the SdkRef is referencing to the unversioned SDK module
+func (s SdkRef) Unversioned() bool {
+	return s.Version == ""
+}
+
+// String returns string representation of this SdkRef for debugging purpose
+func (s SdkRef) String() string {
+	if s.Name == "" {
+		return "(No Sdk)"
+	}
+	if s.Unversioned() {
+		return s.Name
+	}
+	return s.Name + string(SdkVersionSeparator) + s.Version
+}
+
+// SdkVersionSeparator is a character used to separate an sdk name and its version
+const SdkVersionSeparator = '@'
+
+// ParseSdkRef parses a `name@version` style string into a corresponding SdkRef struct
+func ParseSdkRef(ctx BaseModuleContext, str string, property string) SdkRef {
+	tokens := strings.Split(str, string(SdkVersionSeparator))
+	if len(tokens) < 1 || len(tokens) > 2 {
+		ctx.PropertyErrorf(property, "%q does not follow name#version syntax", str)
+		return SdkRef{Name: "invalid sdk name", Version: "invalid sdk version"}
+	}
+
+	name := tokens[0]
+
+	var version string
+	if len(tokens) == 2 {
+		version = tokens[1]
+	}
+
+	return SdkRef{Name: name, Version: version}
+}
+
+type SdkRefs []SdkRef
+
+// Contains tells if the given SdkRef is in this list of SdkRef's
+func (refs SdkRefs) Contains(s SdkRef) bool {
+	for _, r := range refs {
+		if r == s {
+			return true
+		}
+	}
+	return false
+}
+
+type sdkProperties struct {
+	// The SDK that this module is a member of. nil if it is not a member of any SDK
+	ContainingSdk *SdkRef `blueprint:"mutated"`
+
+	// The list of SDK names and versions that are used to build this module
+	RequiredSdks SdkRefs `blueprint:"mutated"`
+
+	// Name of the module that this sdk member is representing
+	Sdk_member_name *string
+}
+
+// SdkBase is a struct that is expected to be included in module types to implement the SdkAware
+// interface. InitSdkAwareModule should be called to initialize this struct.
+type SdkBase struct {
+	properties sdkProperties
+	module     SdkAware
+}
+
+func (s *SdkBase) sdkBase() *SdkBase {
+	return s
+}
+
+// MakeMemberOf sets this module to be a member of a specific SDK
+func (s *SdkBase) MakeMemberOf(sdk SdkRef) {
+	s.properties.ContainingSdk = &sdk
+}
+
+// IsInAnySdk returns true if this module is a member of any SDK
+func (s *SdkBase) IsInAnySdk() bool {
+	return s.properties.ContainingSdk != nil
+}
+
+// ContainingSdk returns the SDK that this module is a member of
+func (s *SdkBase) ContainingSdk() SdkRef {
+	if s.properties.ContainingSdk != nil {
+		return *s.properties.ContainingSdk
+	}
+	return SdkRef{Name: "", Version: ""}
+}
+
+// MemberName returns the name of the module that this SDK member is overriding
+func (s *SdkBase) MemberName() string {
+	return proptools.String(s.properties.Sdk_member_name)
+}
+
+// BuildWithSdks is used to mark that this module has to be built with the given SDK(s).
+func (s *SdkBase) BuildWithSdks(sdks SdkRefs) {
+	s.properties.RequiredSdks = sdks
+}
+
+// RequiredSdks returns the SDK(s) that this module has to be built with
+func (s *SdkBase) RequiredSdks() SdkRefs {
+	return s.properties.RequiredSdks
+}
+
+func (s *SdkBase) BuildSnapshot(sdkModuleContext ModuleContext, builder SnapshotBuilder) {
+	sdkModuleContext.ModuleErrorf("module type " + sdkModuleContext.OtherModuleType(s.module) + " cannot be used in an sdk")
+}
+
+// InitSdkAwareModule initializes the SdkBase struct. This must be called by all modules including
+// SdkBase.
+func InitSdkAwareModule(m SdkAware) {
+	base := m.sdkBase()
+	base.module = m
+	m.AddProperties(&base.properties)
+}
+
+// Provide support for generating the build rules which will build the snapshot.
+type SnapshotBuilder interface {
+	// Copy src to the dest (which is a snapshot relative path) and add the dest
+	// to the zip
+	CopyToSnapshot(src Path, dest string)
+
+	// Unzip the supplied zip into the snapshot relative directory destDir.
+	UnzipToSnapshot(zipPath Path, destDir string)
+
+	// Add a new prebuilt module to the snapshot. The returned module
+	// must be populated with the module type specific properties. The following
+	// properties will be automatically populated.
+	//
+	// * name
+	// * sdk_member_name
+	// * prefer
+	//
+	// This will result in two Soong modules being generated in the Android. One
+	// that is versioned, coupled to the snapshot version and marked as
+	// prefer=true. And one that is not versioned, not marked as prefer=true and
+	// will only be used if the equivalently named non-prebuilt module is not
+	// present.
+	AddPrebuiltModule(member SdkMember, moduleType string) BpModule
+}
+
+// A set of properties for use in a .bp file.
+type BpPropertySet interface {
+	// Add a property, the value can be one of the following types:
+	// * string
+	// * array of the above
+	// * bool
+	// * BpPropertySet
+	//
+	// It is an error is multiples properties with the same name are added.
+	AddProperty(name string, value interface{})
+
+	// Add a property set with the specified name and return so that additional
+	// properties can be added.
+	AddPropertySet(name string) BpPropertySet
+}
+
+// A .bp module definition.
+type BpModule interface {
+	BpPropertySet
+}
+
+// An individual member of the SDK, includes all of the variants that the SDK
+// requires.
+type SdkMember interface {
+	// The name of the member.
+	Name() string
+
+	// All the variants required by the SDK.
+	Variants() []SdkAware
+}
+
+// Interface that must be implemented for every type that can be a member of an
+// sdk.
+//
+// The basic implementation should look something like this, where ModuleType is
+// the name of the module type being supported.
+//
+//    type moduleTypeSdkMemberType struct {
+//        android.SdkMemberTypeBase
+//    }
+//
+//    func init() {
+//        android.RegisterSdkMemberType(&moduleTypeSdkMemberType{
+//            SdkMemberTypeBase: android.SdkMemberTypeBase{
+//                PropertyName: "module_types",
+//            },
+//        }
+//    }
+//
+//    ...methods...
+//
+type SdkMemberType interface {
+	// The name of the member type property on an sdk module.
+	SdkPropertyName() string
+
+	// True if the member type supports the sdk/sdk_snapshot, false otherwise.
+	UsableWithSdkAndSdkSnapshot() bool
+
+	// Add dependencies from the SDK module to all the variants the member
+	// contributes to the SDK. The exact set of variants required is determined
+	// by the SDK and its properties. The dependencies must be added with the
+	// supplied tag.
+	//
+	// The BottomUpMutatorContext provided is for the SDK module.
+	AddDependencies(mctx BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string)
+
+	// Return true if the supplied module is an instance of this member type.
+	//
+	// This is used to check the type of each variant before added to the
+	// SdkMember. Returning false will cause an error to be logged expaining that
+	// the module is not allowed in whichever sdk property it was added.
+	IsInstance(module Module) bool
+
+	// Build the snapshot for the SDK member
+	//
+	// The ModuleContext provided is for the SDK module, so information for
+	// variants in the supplied member can be accessed using the Other... methods.
+	//
+	// The SdkMember is guaranteed to contain variants for which the
+	// IsInstance(Module) method returned true.
+	BuildSnapshot(sdkModuleContext ModuleContext, builder SnapshotBuilder, member SdkMember)
+}
+
+// Base type for SdkMemberType implementations.
+type SdkMemberTypeBase struct {
+	PropertyName string
+	SupportsSdk  bool
+}
+
+func (b *SdkMemberTypeBase) SdkPropertyName() string {
+	return b.PropertyName
+}
+
+func (b *SdkMemberTypeBase) UsableWithSdkAndSdkSnapshot() bool {
+	return b.SupportsSdk
+}
+
+// Encapsulates the information about registered SdkMemberTypes.
+type SdkMemberTypesRegistry struct {
+	// The list of types sorted by property name.
+	list []SdkMemberType
+
+	// The key that uniquely identifies this registry instance.
+	key OnceKey
+}
+
+func (r *SdkMemberTypesRegistry) copyAndAppend(memberType SdkMemberType) *SdkMemberTypesRegistry {
+	oldList := r.list
+
+	// Copy the slice just in case this is being read while being modified, e.g. when testing.
+	list := make([]SdkMemberType, 0, len(oldList)+1)
+	list = append(list, oldList...)
+	list = append(list, memberType)
+
+	// Sort the member types by their property name to ensure that registry order has no effect
+	// on behavior.
+	sort.Slice(list, func(i1, i2 int) bool {
+		t1 := list[i1]
+		t2 := list[i2]
+
+		return t1.SdkPropertyName() < t2.SdkPropertyName()
+	})
+
+	// Generate a key that identifies the slice of SdkMemberTypes by joining the property names
+	// from all the SdkMemberType .
+	var properties []string
+	for _, t := range list {
+		properties = append(properties, t.SdkPropertyName())
+	}
+	key := NewOnceKey(strings.Join(properties, "|"))
+
+	// Create a new registry so the pointer uniquely identifies the set of registered types.
+	return &SdkMemberTypesRegistry{
+		list: list,
+		key:  key,
+	}
+}
+
+func (r *SdkMemberTypesRegistry) RegisteredTypes() []SdkMemberType {
+	return r.list
+}
+
+func (r *SdkMemberTypesRegistry) UniqueOnceKey() OnceKey {
+	// Use the pointer to the registry as the unique key.
+	return NewCustomOnceKey(r)
+}
+
+// The set of registered SdkMemberTypes, one for sdk module and one for module_exports.
+var ModuleExportsMemberTypes = &SdkMemberTypesRegistry{}
+var SdkMemberTypes = &SdkMemberTypesRegistry{}
+
+// Register an SdkMemberType object to allow them to be used in the sdk and sdk_snapshot module
+// types.
+func RegisterSdkMemberType(memberType SdkMemberType) {
+	// All member types are usable with module_exports.
+	ModuleExportsMemberTypes = ModuleExportsMemberTypes.copyAndAppend(memberType)
+
+	// Only those that explicitly indicate it are usable with sdk.
+	if memberType.UsableWithSdkAndSdkSnapshot() {
+		SdkMemberTypes = SdkMemberTypes.copyAndAppend(memberType)
+	}
+}
diff --git a/android/sh_binary.go b/android/sh_binary.go
index fb7446d..7d9dc67 100644
--- a/android/sh_binary.go
+++ b/android/sh_binary.go
@@ -16,6 +16,7 @@
 
 import (
 	"fmt"
+	"path/filepath"
 	"strings"
 )
 
@@ -29,6 +30,7 @@
 	RegisterModuleType("sh_binary", ShBinaryFactory)
 	RegisterModuleType("sh_binary_host", ShBinaryHostFactory)
 	RegisterModuleType("sh_test", ShTestFactory)
+	RegisterModuleType("sh_test_host", ShTestHostFactory)
 }
 
 type shBinaryProperties struct {
@@ -47,6 +49,9 @@
 
 	// 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 {
@@ -70,8 +75,11 @@
 
 	sourceFilePath Path
 	outputFilePath OutputPath
+	installedFile  InstallPath
 }
 
+var _ HostToolProvider = (*ShBinary)(nil)
+
 type ShTest struct {
 	ShBinary
 
@@ -80,16 +88,16 @@
 	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) SourceFilePath(ctx ModuleContext) Path {
-	return PathForModuleSrc(ctx, String(s.properties.Src))
-}
-
 func (s *ShBinary) OutputFile() OutputPath {
 	return s.outputFilePath
 }
@@ -102,7 +110,11 @@
 	return s.properties.Installable == nil || Bool(s.properties.Installable)
 }
 
-func (s *ShBinary) GenerateAndroidBuildActions(ctx ModuleContext) {
+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)
@@ -127,50 +139,78 @@
 	})
 }
 
-func (s *ShBinary) AndroidMkEntries() AndroidMkEntries {
-	return AndroidMkEntries{
+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",
-		AddCustomEntries: func(name, prefix, moduleDir string, entries *AndroidMkEntries) {
-			s.customAndroidMkEntries(entries)
+		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)
+	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) AndroidMkEntries() AndroidMkEntries {
-	return AndroidMkEntries{
+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",
-		AddCustomEntries: func(name, prefix, moduleDir string, entries *AndroidMkEntries) {
-			s.customAndroidMkEntries(entries)
+		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))
+				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)
 				}
-				path = strings.TrimSuffix(path, rel)
-				entries.AddStrings("LOCAL_TEST_DATA", path+":"+rel)
-			}
+			},
 		},
-	}
+	}}
 }
 
 func InitShBinaryModule(s *ShBinary) {
@@ -181,6 +221,9 @@
 // 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
@@ -195,6 +238,7 @@
 	return module
 }
 
+// sh_test defines a shell script based test module.
 func ShTestFactory() Module {
 	module := &ShTest{}
 	InitShBinaryModule(&module.ShBinary)
@@ -203,3 +247,13 @@
 	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
index c99e18c..137e773 100644
--- a/android/sh_binary_test.go
+++ b/android/sh_binary_test.go
@@ -6,18 +6,18 @@
 )
 
 func testShBinary(t *testing.T, bp string) (*TestContext, Config) {
-	config := TestArchConfig(buildDir, nil)
-
-	ctx := NewTestArchContext()
-	ctx.RegisterModuleType("sh_test", ModuleFactoryAdaptor(ShTestFactory))
-	ctx.Register()
-	mockFiles := map[string][]byte{
-		"Android.bp":         []byte(bp),
+	fs := map[string][]byte{
 		"test.sh":            nil,
 		"testdata/data1":     nil,
 		"testdata/sub/data2": nil,
 	}
-	ctx.MockFileSystem(mockFiles)
+
+	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)
@@ -41,10 +41,30 @@
 
 	mod := ctx.ModuleForTests("foo", "android_arm64_armv8-a").Module().(*ShTest)
 
-	entries := AndroidMkEntriesForTest(t, config, "", mod)
+	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/singleton.go b/android/singleton.go
index a59d54a..91268ad 100644
--- a/android/singleton.go
+++ b/android/singleton.go
@@ -16,7 +16,6 @@
 
 import (
 	"github.com/google/blueprint"
-	"github.com/google/blueprint/pathtools"
 )
 
 // SingletonContext
@@ -52,6 +51,10 @@
 	VisitAllModulesBlueprint(visit func(blueprint.Module))
 	VisitAllModules(visit func(Module))
 	VisitAllModulesIf(pred func(Module) bool, visit func(Module))
+
+	VisitDirectDeps(module Module, visit func(Module))
+	VisitDirectDepsIf(module Module, pred func(Module) bool, visit func(Module))
+
 	// Deprecated: use WalkDeps instead to support multiple dependency tags on the same module
 	VisitDepsDepthFirst(module Module, visit func(Module))
 	// Deprecated: use WalkDeps instead to support multiple dependency tags on the same module
@@ -70,8 +73,6 @@
 	// builder whenever a file matching the pattern as added or removed, without rerunning if a
 	// file that does not match the pattern is added to a searched directory.
 	GlobWithDeps(pattern string, excludes []string) ([]string, error)
-
-	Fs() pathtools.FileSystem
 }
 
 type singletonAdaptor struct {
@@ -127,6 +128,11 @@
 }
 
 func (s *singletonContextAdaptor) Rule(pctx PackageContext, name string, params blueprint.RuleParams, argNames ...string) blueprint.Rule {
+	if (s.Config().UseGoma() || s.Config().UseRBE()) && params.Pool == nil {
+		// When USE_GOMA=true or USE_RBE=true are set and the rule is not supported by goma/RBE, restrict
+		// jobs to the local parallelism value
+		params.Pool = localPool
+	}
 	rule := s.SingletonContext.Rule(pctx.PackageContext, name, params, argNames...)
 	if s.Config().captureBuild {
 		s.ruleParams[rule] = params
@@ -187,6 +193,14 @@
 	s.SingletonContext.VisitAllModulesIf(predAdaptor(pred), visitAdaptor(visit))
 }
 
+func (s *singletonContextAdaptor) VisitDirectDeps(module Module, visit func(Module)) {
+	s.SingletonContext.VisitDirectDeps(module, visitAdaptor(visit))
+}
+
+func (s *singletonContextAdaptor) VisitDirectDepsIf(module Module, pred func(Module) bool, visit func(Module)) {
+	s.SingletonContext.VisitDirectDepsIf(module, predAdaptor(pred), visitAdaptor(visit))
+}
+
 func (s *singletonContextAdaptor) VisitDepsDepthFirst(module Module, visit func(Module)) {
 	s.SingletonContext.VisitDepsDepthFirst(module, visitAdaptor(visit))
 }
diff --git a/android/testing.go b/android/testing.go
index 44bee4b..6663728 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -37,8 +37,6 @@
 
 	ctx.SetNameInterface(nameResolver)
 
-	ctx.preArch = append(ctx.preArch, registerLoadHookMutator)
-
 	ctx.postDeps = append(ctx.postDeps, registerPathDepsMutator)
 
 	return ctx
@@ -54,6 +52,7 @@
 	*Context
 	preArch, preDeps, postDeps []RegisterMutatorFunc
 	NameResolver               *NameResolver
+	config                     Config
 }
 
 func (ctx *TestContext) PreArchMutators(f RegisterMutatorFunc) {
@@ -68,10 +67,36 @@
 	ctx.postDeps = append(ctx.postDeps, f)
 }
 
-func (ctx *TestContext) Register() {
+func (ctx *TestContext) Register(config Config) {
+	ctx.SetFs(config.fs)
+	if config.mockBpList != "" {
+		ctx.SetModuleListFile(config.mockBpList)
+	}
 	registerMutators(ctx.Context.Context, ctx.preArch, ctx.preDeps, ctx.postDeps)
 
-	ctx.RegisterSingletonType("env", SingletonFactoryAdaptor(EnvSingleton))
+	ctx.RegisterSingletonType("env", EnvSingleton)
+
+	ctx.config = config
+}
+
+func (ctx *TestContext) ParseFileList(rootDir string, filePaths []string) (deps []string, errs []error) {
+	// This function adapts the old style ParseFileList calls that are spread throughout the tests
+	// to the new style that takes a config.
+	return ctx.Context.ParseFileList(rootDir, filePaths, ctx.config)
+}
+
+func (ctx *TestContext) ParseBlueprintsFiles(rootDir string) (deps []string, errs []error) {
+	// This function adapts the old style ParseBlueprintsFiles calls that are spread throughout the
+	// tests to the new style that takes a config.
+	return ctx.Context.ParseBlueprintsFiles(rootDir, ctx.config)
+}
+
+func (ctx *TestContext) RegisterModuleType(name string, factory ModuleFactory) {
+	ctx.Context.RegisterModuleType(name, ModuleFactoryAdaptor(factory))
+}
+
+func (ctx *TestContext) RegisterSingletonType(name string, factory SingletonFactory) {
+	ctx.Context.RegisterSingletonType(name, SingletonFactoryAdaptor(factory))
 }
 
 func (ctx *TestContext) ModuleForTests(name, variant string) TestingModule {
@@ -124,25 +149,6 @@
 		"\nall singletons: %v", name, allSingletonNames))
 }
 
-// MockFileSystem causes the Context to replace all reads with accesses to the provided map of
-// filenames to contents stored as a byte slice.
-func (ctx *TestContext) MockFileSystem(files map[string][]byte) {
-	// no module list file specified; find every file named Blueprints or Android.bp
-	pathsToParse := []string{}
-	for candidate := range files {
-		base := filepath.Base(candidate)
-		if base == "Blueprints" || base == "Android.bp" {
-			pathsToParse = append(pathsToParse, candidate)
-		}
-	}
-	if len(pathsToParse) < 1 {
-		panic(fmt.Sprintf("No Blueprint or Android.bp files found in mock filesystem: %v\n", files))
-	}
-	files[blueprint.MockModuleListFile] = []byte(strings.Join(pathsToParse, "\n"))
-
-	ctx.Context.MockFileSystem(files)
-}
-
 type testBuildProvider interface {
 	BuildParamsForTests() []BuildParams
 	RuleParamsForTests() map[blueprint.Rule]blueprint.RuleParams
@@ -372,13 +378,80 @@
 	}
 }
 
-func AndroidMkEntriesForTest(t *testing.T, config Config, bpPath string, mod blueprint.Module) AndroidMkEntries {
+func CheckErrorsAgainstExpectations(t *testing.T, errs []error, expectedErrorPatterns []string) {
+	t.Helper()
+
+	if expectedErrorPatterns == nil {
+		FailIfErrored(t, errs)
+	} else {
+		for _, expectedError := range expectedErrorPatterns {
+			FailIfNoMatchingErrors(t, expectedError, errs)
+		}
+		if len(errs) > len(expectedErrorPatterns) {
+			t.Errorf("additional errors found, expected %d, found %d",
+				len(expectedErrorPatterns), len(errs))
+			for i, expectedError := range expectedErrorPatterns {
+				t.Errorf("expectedErrors[%d] = %s", i, expectedError)
+			}
+			for i, err := range errs {
+				t.Errorf("errs[%d] = %s", i, err)
+			}
+		}
+	}
+
+}
+
+func AndroidMkEntriesForTest(t *testing.T, config Config, bpPath string, mod blueprint.Module) []AndroidMkEntries {
 	var p AndroidMkEntriesProvider
 	var ok bool
 	if p, ok = mod.(AndroidMkEntriesProvider); !ok {
-		t.Errorf("module does not implmement AndroidMkEntriesProvider: " + mod.Name())
+		t.Errorf("module does not implement AndroidMkEntriesProvider: " + mod.Name())
 	}
-	entries := p.AndroidMkEntries()
-	entries.fillInEntries(config, bpPath, mod)
-	return entries
+
+	entriesList := p.AndroidMkEntries()
+	for i, _ := range entriesList {
+		entriesList[i].fillInEntries(config, bpPath, mod)
+	}
+	return entriesList
+}
+
+func AndroidMkDataForTest(t *testing.T, config Config, bpPath string, mod blueprint.Module) AndroidMkData {
+	var p AndroidMkDataProvider
+	var ok bool
+	if p, ok = mod.(AndroidMkDataProvider); !ok {
+		t.Errorf("module does not implement AndroidMkDataProvider: " + mod.Name())
+	}
+	data := p.AndroidMk()
+	data.fillInData(config, bpPath, mod)
+	return data
+}
+
+// Normalize the path for testing.
+//
+// If the path is relative to the build directory then return the relative path
+// to avoid tests having to deal with the dynamically generated build directory.
+//
+// Otherwise, return the supplied path as it is almost certainly a source path
+// that is relative to the root of the source tree.
+//
+// The build and source paths should be distinguishable based on their contents.
+func NormalizePathForTesting(path Path) string {
+	p := path.String()
+	if w, ok := path.(WritablePath); ok {
+		rel, err := filepath.Rel(w.buildDir(), p)
+		if err != nil {
+			panic(err)
+		}
+		return rel
+	}
+	return p
+}
+
+func NormalizePathsForTesting(paths Paths) []string {
+	var result []string
+	for _, path := range paths {
+		relative := NormalizePathForTesting(path)
+		result = append(result, relative)
+	}
+	return result
 }
diff --git a/android/util.go b/android/util.go
index 3b8bc78..81f481d 100644
--- a/android/util.go
+++ b/android/util.go
@@ -16,6 +16,7 @@
 
 import (
 	"fmt"
+	"path/filepath"
 	"reflect"
 	"regexp"
 	"runtime"
@@ -92,6 +93,20 @@
 	return s
 }
 
+func SortedStringMapValues(m interface{}) []string {
+	v := reflect.ValueOf(m)
+	if v.Kind() != reflect.Map {
+		panic(fmt.Sprintf("%#v is not a map", m))
+	}
+	keys := v.MapKeys()
+	s := make([]string, 0, len(keys))
+	for _, key := range keys {
+		s = append(s, v.MapIndex(key).String())
+	}
+	sort.Strings(s)
+	return s
+}
+
 func IndexList(s string, list []string) int {
 	for i, l := range list {
 		if l == s {
@@ -115,6 +130,17 @@
 	return false
 }
 
+// IndexListPred returns the index of the element which in the given `list` satisfying the predicate, or -1 if there is no such element.
+func IndexListPred(pred func(s string) bool, list []string) int {
+	for i, l := range list {
+		if pred(l) {
+			return i
+		}
+	}
+
+	return -1
+}
+
 func FilterList(list []string, filter []string) (remainder []string, filtered []string) {
 	for _, l := range list {
 		if InList(l, filter) {
@@ -188,6 +214,13 @@
 	return list[totalSkip:]
 }
 
+// SortedUniqueStrings returns what the name says
+func SortedUniqueStrings(list []string) []string {
+	unique := FirstUniqueStrings(list)
+	sort.Strings(unique)
+	return unique
+}
+
 // checkCalledFromInit panics if a Go package's init function is not on the
 // call stack.
 func checkCalledFromInit() {
@@ -274,3 +307,73 @@
 	}
 	return strings.HasPrefix(str, pat[:i]) && strings.HasSuffix(str, pat[i+1:])
 }
+
+var shlibVersionPattern = regexp.MustCompile("(?:\\.\\d+(?:svn)?)+")
+
+// splitFileExt splits a file name into root, suffix and ext. root stands for the file name without
+// the file extension and the version number (e.g. "libexample"). suffix stands for the
+// concatenation of the file extension and the version number (e.g. ".so.1.0"). ext stands for the
+// file extension after the version numbers are trimmed (e.g. ".so").
+func SplitFileExt(name string) (string, string, string) {
+	// Extract and trim the shared lib version number if the file name ends with dot digits.
+	suffix := ""
+	matches := shlibVersionPattern.FindAllStringIndex(name, -1)
+	if len(matches) > 0 {
+		lastMatch := matches[len(matches)-1]
+		if lastMatch[1] == len(name) {
+			suffix = name[lastMatch[0]:lastMatch[1]]
+			name = name[0:lastMatch[0]]
+		}
+	}
+
+	// Extract the file name root and the file extension.
+	ext := filepath.Ext(name)
+	root := strings.TrimSuffix(name, ext)
+	suffix = ext + suffix
+
+	return root, suffix, ext
+}
+
+// ShardPaths takes a Paths, and returns a slice of Paths where each one has at most shardSize paths.
+func ShardPaths(paths Paths, shardSize int) []Paths {
+	if len(paths) == 0 {
+		return nil
+	}
+	ret := make([]Paths, 0, (len(paths)+shardSize-1)/shardSize)
+	for len(paths) > shardSize {
+		ret = append(ret, paths[0:shardSize])
+		paths = paths[shardSize:]
+	}
+	if len(paths) > 0 {
+		ret = append(ret, paths)
+	}
+	return ret
+}
+
+// ShardStrings takes a slice of strings, and returns a slice of slices of strings where each one has at most shardSize
+// elements.
+func ShardStrings(s []string, shardSize int) [][]string {
+	if len(s) == 0 {
+		return nil
+	}
+	ret := make([][]string, 0, (len(s)+shardSize-1)/shardSize)
+	for len(s) > shardSize {
+		ret = append(ret, s[0:shardSize])
+		s = s[shardSize:]
+	}
+	if len(s) > 0 {
+		ret = append(ret, s)
+	}
+	return ret
+}
+
+func CheckDuplicate(values []string) (duplicate string, found bool) {
+	seen := make(map[string]string)
+	for _, v := range values {
+		if duplicate, found = seen[v]; found {
+			return
+		}
+		seen[v] = v
+	}
+	return
+}
diff --git a/android/util_test.go b/android/util_test.go
index 2e5eb07..90fefee 100644
--- a/android/util_test.go
+++ b/android/util_test.go
@@ -404,3 +404,167 @@
 	// b = ["foo" "bar"]
 	// c = ["foo" "baz"]
 }
+
+func TestSplitFileExt(t *testing.T) {
+	t.Run("soname with version", func(t *testing.T) {
+		root, suffix, ext := SplitFileExt("libtest.so.1.0.30")
+		expected := "libtest"
+		if root != expected {
+			t.Errorf("root should be %q but got %q", expected, root)
+		}
+		expected = ".so.1.0.30"
+		if suffix != expected {
+			t.Errorf("suffix should be %q but got %q", expected, suffix)
+		}
+		expected = ".so"
+		if ext != expected {
+			t.Errorf("ext should be %q but got %q", expected, ext)
+		}
+	})
+
+	t.Run("soname with svn version", func(t *testing.T) {
+		root, suffix, ext := SplitFileExt("libtest.so.1svn")
+		expected := "libtest"
+		if root != expected {
+			t.Errorf("root should be %q but got %q", expected, root)
+		}
+		expected = ".so.1svn"
+		if suffix != expected {
+			t.Errorf("suffix should be %q but got %q", expected, suffix)
+		}
+		expected = ".so"
+		if ext != expected {
+			t.Errorf("ext should be %q but got %q", expected, ext)
+		}
+	})
+
+	t.Run("version numbers in the middle should be ignored", func(t *testing.T) {
+		root, suffix, ext := SplitFileExt("libtest.1.0.30.so")
+		expected := "libtest.1.0.30"
+		if root != expected {
+			t.Errorf("root should be %q but got %q", expected, root)
+		}
+		expected = ".so"
+		if suffix != expected {
+			t.Errorf("suffix should be %q but got %q", expected, suffix)
+		}
+		expected = ".so"
+		if ext != expected {
+			t.Errorf("ext should be %q but got %q", expected, ext)
+		}
+	})
+
+	t.Run("no known file extension", func(t *testing.T) {
+		root, suffix, ext := SplitFileExt("test.exe")
+		expected := "test"
+		if root != expected {
+			t.Errorf("root should be %q but got %q", expected, root)
+		}
+		expected = ".exe"
+		if suffix != expected {
+			t.Errorf("suffix should be %q but got %q", expected, suffix)
+		}
+		if ext != expected {
+			t.Errorf("ext should be %q but got %q", expected, ext)
+		}
+	})
+}
+
+func Test_Shard(t *testing.T) {
+	type args struct {
+		strings   []string
+		shardSize int
+	}
+	tests := []struct {
+		name string
+		args args
+		want [][]string
+	}{
+		{
+			name: "empty",
+			args: args{
+				strings:   nil,
+				shardSize: 1,
+			},
+			want: [][]string(nil),
+		},
+		{
+			name: "single shard",
+			args: args{
+				strings:   []string{"a", "b"},
+				shardSize: 2,
+			},
+			want: [][]string{{"a", "b"}},
+		},
+		{
+			name: "single short shard",
+			args: args{
+				strings:   []string{"a", "b"},
+				shardSize: 3,
+			},
+			want: [][]string{{"a", "b"}},
+		},
+		{
+			name: "shard per input",
+			args: args{
+				strings:   []string{"a", "b", "c"},
+				shardSize: 1,
+			},
+			want: [][]string{{"a"}, {"b"}, {"c"}},
+		},
+		{
+			name: "balanced shards",
+			args: args{
+				strings:   []string{"a", "b", "c", "d"},
+				shardSize: 2,
+			},
+			want: [][]string{{"a", "b"}, {"c", "d"}},
+		},
+		{
+			name: "unbalanced shards",
+			args: args{
+				strings:   []string{"a", "b", "c"},
+				shardSize: 2,
+			},
+			want: [][]string{{"a", "b"}, {"c"}},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			t.Run("strings", func(t *testing.T) {
+				if got := ShardStrings(tt.args.strings, tt.args.shardSize); !reflect.DeepEqual(got, tt.want) {
+					t.Errorf("ShardStrings(%v, %v) = %v, want %v",
+						tt.args.strings, tt.args.shardSize, got, tt.want)
+				}
+			})
+
+			t.Run("paths", func(t *testing.T) {
+				stringsToPaths := func(strings []string) Paths {
+					if strings == nil {
+						return nil
+					}
+					paths := make(Paths, len(strings))
+					for i, s := range strings {
+						paths[i] = PathForTesting(s)
+					}
+					return paths
+				}
+
+				paths := stringsToPaths(tt.args.strings)
+
+				var want []Paths
+				if sWant := tt.want; sWant != nil {
+					want = make([]Paths, len(sWant))
+					for i, w := range sWant {
+						want[i] = stringsToPaths(w)
+					}
+				}
+
+				if got := ShardPaths(paths, tt.args.shardSize); !reflect.DeepEqual(got, want) {
+					t.Errorf("ShardPaths(%v, %v) = %v, want %v",
+						paths, tt.args.shardSize, got, want)
+				}
+			})
+		})
+	}
+}
diff --git a/android/variable.go b/android/variable.go
index b4f31c6..2bf84dd 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -59,17 +59,6 @@
 			Cflags []string
 		}
 
-		// Product_is_iot is true for Android Things devices.
-		Product_is_iot struct {
-			Cflags       []string
-			Enabled      bool
-			Exclude_srcs []string
-			Init_rc      []string
-			Shared_libs  []string
-			Srcs         []string
-			Static_libs  []string
-		}
-
 		// treble_linker_namespaces is true when the system/vendor linker namespace separation is
 		// enabled.
 		Treble_linker_namespaces struct {
@@ -126,10 +115,18 @@
 			Static_libs  []string
 			Srcs         []string
 		}
+
+		Flatten_apex struct {
+			Enabled *bool
+		}
+
+		Experimental_mte struct {
+			Cflags []string `android:"arch_variant"`
+		} `android:"arch_variant"`
 	} `android:"arch_variant"`
 }
 
-var zeroProductVariables variableProperties
+var zeroProductVariables interface{} = variableProperties{}
 
 type productVariables struct {
 	// Suffix to add to generated Makefiles
@@ -165,15 +162,17 @@
 	DeviceSecondaryCpuVariant  *string  `json:",omitempty"`
 	DeviceSecondaryAbi         []string `json:",omitempty"`
 
-	NativeBridgeArch        *string  `json:",omitempty"`
-	NativeBridgeArchVariant *string  `json:",omitempty"`
-	NativeBridgeCpuVariant  *string  `json:",omitempty"`
-	NativeBridgeAbi         []string `json:",omitempty"`
+	NativeBridgeArch         *string  `json:",omitempty"`
+	NativeBridgeArchVariant  *string  `json:",omitempty"`
+	NativeBridgeCpuVariant   *string  `json:",omitempty"`
+	NativeBridgeAbi          []string `json:",omitempty"`
+	NativeBridgeRelativePath *string  `json:",omitempty"`
 
-	NativeBridgeSecondaryArch        *string  `json:",omitempty"`
-	NativeBridgeSecondaryArchVariant *string  `json:",omitempty"`
-	NativeBridgeSecondaryCpuVariant  *string  `json:",omitempty"`
-	NativeBridgeSecondaryAbi         []string `json:",omitempty"`
+	NativeBridgeSecondaryArch         *string  `json:",omitempty"`
+	NativeBridgeSecondaryArchVariant  *string  `json:",omitempty"`
+	NativeBridgeSecondaryCpuVariant   *string  `json:",omitempty"`
+	NativeBridgeSecondaryAbi          []string `json:",omitempty"`
+	NativeBridgeSecondaryRelativePath *string  `json:",omitempty"`
 
 	HostArch          *string `json:",omitempty"`
 	HostSecondaryArch *string `json:",omitempty"`
@@ -204,6 +203,7 @@
 	HostStaticBinaries               *bool `json:",omitempty"`
 	Binder32bit                      *bool `json:",omitempty"`
 	UseGoma                          *bool `json:",omitempty"`
+	UseRBE                           *bool `json:",omitempty"`
 	Debuggable                       *bool `json:",omitempty"`
 	Eng                              *bool `json:",omitempty"`
 	Treble_linker_namespaces         *bool `json:",omitempty"`
@@ -232,10 +232,12 @@
 	EnableXOM       *bool    `json:",omitempty"`
 	XOMExcludePaths []string `json:",omitempty"`
 
-	VendorPath          *string `json:",omitempty"`
-	OdmPath             *string `json:",omitempty"`
-	ProductPath         *string `json:",omitempty"`
-	ProductServicesPath *string `json:",omitempty"`
+	Experimental_mte *bool `json:",omitempty"`
+
+	VendorPath    *string `json:",omitempty"`
+	OdmPath       *string `json:",omitempty"`
+	ProductPath   *string `json:",omitempty"`
+	SystemExtPath *string `json:",omitempty"`
 
 	ClangTidy  *bool   `json:",omitempty"`
 	TidyChecks *string `json:",omitempty"`
@@ -259,8 +261,6 @@
 
 	Override_rs_driver *string `json:",omitempty"`
 
-	Product_is_iot *bool `json:",omitempty"`
-
 	Fuchsia *bool `json:",omitempty"`
 
 	DeviceKernelHeaders []string `json:",omitempty"`
@@ -271,7 +271,8 @@
 
 	PgoAdditionalProfileDirs []string `json:",omitempty"`
 
-	VndkUseCoreVariant *bool `json:",omitempty"`
+	VndkUseCoreVariant         *bool `json:",omitempty"`
+	VndkSnapshotBuildArtifacts *bool `json:",omitempty"`
 
 	BoardVendorSepolicyDirs      []string `json:",omitempty"`
 	BoardOdmSepolicyDirs         []string `json:",omitempty"`
@@ -286,7 +287,8 @@
 	Ndk_abis               *bool `json:",omitempty"`
 	Exclude_draft_ndk_apis *bool `json:",omitempty"`
 
-	FlattenApex *bool `json:",omitempty"`
+	Flatten_apex *bool `json:",omitempty"`
+	Aml_abis     *bool `json:",omitempty"`
 
 	DexpreoptGlobalConfig *string `json:",omitempty"`
 
@@ -305,9 +307,15 @@
 	ProductPrivateSepolicyDirs []string `json:",omitempty"`
 	ProductCompatibleProperty  *bool    `json:",omitempty"`
 
+	ProductVndkVersion *string `json:",omitempty"`
+
 	TargetFSConfigGen []string `json:",omitempty"`
 
 	MissingUsesLibraries []string `json:",omitempty"`
+
+	EnforceProductPartitionInterface *bool `json:",omitempty"`
+
+	InstallExtraFlattenedApexes *bool `json:",omitempty"`
 }
 
 func boolPtr(v bool) *bool {
@@ -371,8 +379,13 @@
 
 	// TODO: depend on config variable, create variants, propagate variants up tree
 	a := module.base()
-	variableValues := reflect.ValueOf(&a.variableProperties.Product_variables).Elem()
-	zeroValues := reflect.ValueOf(zeroProductVariables.Product_variables)
+
+	if a.variableProperties == nil {
+		return
+	}
+
+	variableValues := reflect.ValueOf(a.variableProperties).Elem().FieldByName("Product_variables")
+	zeroValues := reflect.ValueOf(zeroProductVariables).FieldByName("Product_variables")
 
 	for i := 0; i < variableValues.NumField(); i++ {
 		variableValue := variableValues.Field(i)
@@ -501,3 +514,106 @@
 
 	return nil
 }
+
+var variablePropTypeMap OncePer
+
+// sliceToTypeArray takes a slice of property structs and returns a reflection created array containing the
+// reflect.Types of each property struct.  The result can be used as a key in a map.
+func sliceToTypeArray(s []interface{}) interface{} {
+	// Create an array using reflection whose length is the length of the input slice
+	ret := reflect.New(reflect.ArrayOf(len(s), reflect.TypeOf(reflect.TypeOf(0)))).Elem()
+	for i, e := range s {
+		ret.Index(i).Set(reflect.ValueOf(reflect.TypeOf(e)))
+	}
+	return ret.Interface()
+}
+
+// createVariableProperties takes the list of property structs for a module and returns a property struct that
+// contains the product variable properties that exist in the property structs, or nil if there are none.  It
+// caches the result.
+func createVariableProperties(moduleTypeProps []interface{}, productVariables interface{}) interface{} {
+	// Convert the moduleTypeProps to an array of reflect.Types that can be used as a key in the OncePer.
+	key := sliceToTypeArray(moduleTypeProps)
+
+	// Use the variablePropTypeMap OncePer to cache the result for each set of property struct types.
+	typ, _ := variablePropTypeMap.Once(NewCustomOnceKey(key), func() interface{} {
+		// Compute the filtered property struct type.
+		return createVariablePropertiesType(moduleTypeProps, productVariables)
+	}).(reflect.Type)
+
+	if typ == nil {
+		return nil
+	}
+
+	// Create a new pointer to a filtered property struct.
+	return reflect.New(typ).Interface()
+}
+
+// createVariablePropertiesType creates a new type that contains only the product variable properties that exist in
+// a list of property structs.
+func createVariablePropertiesType(moduleTypeProps []interface{}, productVariables interface{}) reflect.Type {
+	typ, _ := proptools.FilterPropertyStruct(reflect.TypeOf(productVariables),
+		func(field reflect.StructField, prefix string) (bool, reflect.StructField) {
+			// Filter function, returns true if the field should be in the resulting struct
+			if prefix == "" {
+				// Keep the top level Product_variables field
+				return true, field
+			}
+			_, rest := splitPrefix(prefix)
+			if rest == "" {
+				// Keep the 2nd level field (i.e. Product_variables.Eng)
+				return true, field
+			}
+
+			// Strip off the first 2 levels of the prefix
+			_, prefix = splitPrefix(rest)
+
+			for _, p := range moduleTypeProps {
+				if fieldExistsByNameRecursive(reflect.TypeOf(p).Elem(), prefix, field.Name) {
+					// Keep any fields that exist in one of the property structs
+					return true, field
+				}
+			}
+
+			return false, field
+		})
+	return typ
+}
+
+func splitPrefix(prefix string) (first, rest string) {
+	index := strings.IndexByte(prefix, '.')
+	if index == -1 {
+		return prefix, ""
+	}
+	return prefix[:index], prefix[index+1:]
+}
+
+func fieldExistsByNameRecursive(t reflect.Type, prefix, name string) bool {
+	if t.Kind() != reflect.Struct {
+		panic(fmt.Errorf("fieldExistsByNameRecursive can only be called on a reflect.Struct"))
+	}
+
+	if prefix != "" {
+		split := strings.SplitN(prefix, ".", 2)
+		firstPrefix := split[0]
+		rest := ""
+		if len(split) > 1 {
+			rest = split[1]
+		}
+		f, exists := t.FieldByName(firstPrefix)
+		if !exists {
+			return false
+		}
+		ft := f.Type
+		if ft.Kind() == reflect.Ptr {
+			ft = ft.Elem()
+		}
+		if ft.Kind() != reflect.Struct {
+			panic(fmt.Errorf("field %q in %q is not a struct", firstPrefix, t))
+		}
+		return fieldExistsByNameRecursive(ft, rest, name)
+	} else {
+		_, exists := t.FieldByName(name)
+		return exists
+	}
+}
diff --git a/android/variable_test.go b/android/variable_test.go
index ce9ba54..451d43d 100644
--- a/android/variable_test.go
+++ b/android/variable_test.go
@@ -16,7 +16,10 @@
 
 import (
 	"reflect"
+	"strconv"
 	"testing"
+
+	"github.com/google/blueprint/proptools"
 )
 
 type printfIntoPropertyTestCase struct {
@@ -122,3 +125,104 @@
 		}
 	}
 }
+
+type testProductVariableModule struct {
+	ModuleBase
+}
+
+func (m *testProductVariableModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+}
+
+var testProductVariableProperties = struct {
+	Product_variables struct {
+		Eng struct {
+			Srcs   []string
+			Cflags []string
+		}
+	}
+}{}
+
+func testProductVariableModuleFactoryFactory(props interface{}) func() Module {
+	return func() Module {
+		m := &testProductVariableModule{}
+		clonedProps := proptools.CloneProperties(reflect.ValueOf(props)).Interface()
+		m.AddProperties(clonedProps)
+
+		// Set a default variableProperties, this will be used as the input to the property struct filter
+		// for this test module.
+		m.variableProperties = testProductVariableProperties
+		InitAndroidModule(m)
+		return m
+	}
+}
+
+func TestProductVariables(t *testing.T) {
+	ctx := NewTestContext()
+	// A module type that has a srcs property but not a cflags property.
+	ctx.RegisterModuleType("module1", testProductVariableModuleFactoryFactory(struct {
+		Srcs []string
+	}{}))
+	// A module type that has a cflags property but not a srcs property.
+	ctx.RegisterModuleType("module2", testProductVariableModuleFactoryFactory(struct {
+		Cflags []string
+	}{}))
+	// A module type that does not have any properties that match product_variables.
+	ctx.RegisterModuleType("module3", testProductVariableModuleFactoryFactory(struct {
+		Foo []string
+	}{}))
+	ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
+		ctx.BottomUp("variable", variableMutator).Parallel()
+	})
+
+	// Test that a module can use one product variable even if it doesn't have all the properties
+	// supported by that product variable.
+	bp := `
+		module1 {
+			name: "foo",
+			product_variables: {
+				eng: {
+					srcs: ["foo.c"],
+				},
+			},
+		}
+		module2 {
+			name: "bar",
+			product_variables: {
+				eng: {
+					cflags: ["-DBAR"],
+				},
+			},
+		}
+
+		module3 {
+			name: "baz",
+		}
+	`
+	config := TestConfig(buildDir, nil, bp, nil)
+	config.TestProductVariables.Eng = proptools.BoolPtr(true)
+
+	ctx.Register(config)
+
+	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+	FailIfErrored(t, errs)
+	_, errs = ctx.PrepareBuildActions(config)
+	FailIfErrored(t, errs)
+}
+
+func BenchmarkSliceToTypeArray(b *testing.B) {
+	for _, n := range []int{1, 2, 4, 8, 100} {
+		var propStructs []interface{}
+		for i := 0; i < n; i++ {
+			propStructs = append(propStructs, &struct {
+				A *string
+				B string
+			}{})
+
+		}
+		b.Run(strconv.Itoa(n), func(b *testing.B) {
+			for i := 0; i < b.N; i++ {
+				_ = sliceToTypeArray(propStructs)
+			}
+		})
+	}
+}
diff --git a/android/visibility.go b/android/visibility.go
index c7ef1da..c28ec93 100644
--- a/android/visibility.go
+++ b/android/visibility.go
@@ -23,14 +23,21 @@
 
 // Enforces visibility rules between modules.
 //
-// Two stage process:
-// * First stage works bottom up to extract visibility information from the modules, parse it,
+// Multi stage process:
+// * First stage works bottom up, before defaults expansion, to check the syntax of the visibility
+//   rules that have been specified.
+//
+// * Second stage works bottom up to extract the package info for each package and store them in a
+//   map by package name. See package.go for functionality for this.
+//
+// * Third stage works bottom up to extract visibility information from the modules, parse it,
 //   create visibilityRule structures and store them in a map keyed by the module's
 //   qualifiedModuleName instance, i.e. //<pkg>:<name>. The map is stored in the context rather
 //   than a global variable for testing. Each test has its own Config so they do not share a map
-//   and so can be run in parallel.
+//   and so can be run in parallel. If a module has no visibility specified then it uses the
+//   default package visibility if specified.
 //
-// * Second stage works top down and iterates over all the deps for each module. If the dep is in
+// * Fourth stage works top down and iterates over all the deps for each module. If the dep is in
 //   the same package then it is automatically visible. Otherwise, for each dep it first extracts
 //   its visibilityRule from the config map. If one could not be found then it assumes that it is
 //   publicly visible. Otherwise, it calls the visibility rule to check that the module can see
@@ -48,19 +55,6 @@
 
 var visibilityRuleRegexp = regexp.MustCompile(visibilityRulePattern)
 
-// Qualified id for a module
-type qualifiedModuleName struct {
-	// The package (i.e. directory) in which the module is defined, without trailing /
-	pkg string
-
-	// The name of the module.
-	name string
-}
-
-func (q qualifiedModuleName) String() string {
-	return fmt.Sprintf("//%s:%s", q.pkg, q.name)
-}
-
 // A visibility rule is associated with a module and determines which other modules it is visible
 // to, i.e. which other modules can depend on the rule's module.
 type visibilityRule interface {
@@ -71,6 +65,32 @@
 	String() string
 }
 
+// Describes the properties provided by a module that contain visibility rules.
+type visibilityPropertyImpl struct {
+	name            string
+	stringsProperty *[]string
+}
+
+type visibilityProperty interface {
+	getName() string
+	getStrings() []string
+}
+
+func newVisibilityProperty(name string, stringsProperty *[]string) visibilityProperty {
+	return visibilityPropertyImpl{
+		name:            name,
+		stringsProperty: stringsProperty,
+	}
+}
+
+func (p visibilityPropertyImpl) getName() string {
+	return p.name
+}
+
+func (p visibilityPropertyImpl) getStrings() []string {
+	return *p.stringsProperty
+}
+
 // A compositeRule is a visibility rule composed from a list of atomic visibility rules.
 //
 // The list corresponds to the list of strings in the visibility property after defaults expansion.
@@ -96,13 +116,16 @@
 	return false
 }
 
-func (r compositeRule) String() string {
-	s := make([]string, 0, len(r))
-	for _, r := range r {
+func (c compositeRule) String() string {
+	return "[" + strings.Join(c.Strings(), ", ") + "]"
+}
+
+func (c compositeRule) Strings() []string {
+	s := make([]string, 0, len(c))
+	for _, r := range c {
 		s = append(s, r.String())
 	}
-
-	return "[" + strings.Join(s, ", ") + "]"
+	return s
 }
 
 // A packageRule is a visibility rule that matches modules in a specific package (i.e. directory).
@@ -169,57 +192,54 @@
 
 // The rule checker needs to be registered before defaults expansion to correctly check that
 // //visibility:xxx isn't combined with other packages in the same list in any one module.
-func registerVisibilityRuleChecker(ctx RegisterMutatorsContext) {
+func RegisterVisibilityRuleChecker(ctx RegisterMutatorsContext) {
 	ctx.BottomUp("visibilityRuleChecker", visibilityRuleChecker).Parallel()
 }
 
+// Registers the function that gathers the visibility rules for each module.
+//
 // Visibility is not dependent on arch so this must be registered before the arch phase to avoid
 // having to process multiple variants for each module. This goes after defaults expansion to gather
-// the complete visibility lists from flat lists.
-func registerVisibilityRuleGatherer(ctx RegisterMutatorsContext) {
+// the complete visibility lists from flat lists and after the package info is gathered to ensure
+// that default_visibility is available.
+func RegisterVisibilityRuleGatherer(ctx RegisterMutatorsContext) {
 	ctx.BottomUp("visibilityRuleGatherer", visibilityRuleGatherer).Parallel()
 }
 
 // This must be registered after the deps have been resolved.
-func registerVisibilityRuleEnforcer(ctx RegisterMutatorsContext) {
+func RegisterVisibilityRuleEnforcer(ctx RegisterMutatorsContext) {
 	ctx.TopDown("visibilityRuleEnforcer", visibilityRuleEnforcer).Parallel()
 }
 
 // Checks the per-module visibility rule lists before defaults expansion.
 func visibilityRuleChecker(ctx BottomUpMutatorContext) {
 	qualified := createQualifiedModuleName(ctx)
-	if d, ok := ctx.Module().(Defaults); ok {
-		// Defaults modules don't store the payload properties in m.base().
-		for _, props := range d.properties() {
-			if cp, ok := props.(*commonProperties); ok {
-				if visibility := cp.Visibility; visibility != nil {
-					checkRules(ctx, qualified.pkg, visibility)
-				}
+	if m, ok := ctx.Module().(Module); ok {
+		visibilityProperties := m.visibilityProperties()
+		for _, p := range visibilityProperties {
+			if visibility := p.getStrings(); visibility != nil {
+				checkRules(ctx, qualified.pkg, p.getName(), visibility)
 			}
 		}
-	} else if m, ok := ctx.Module().(Module); ok {
-		if visibility := m.base().commonProperties.Visibility; visibility != nil {
-			checkRules(ctx, qualified.pkg, visibility)
-		}
 	}
 }
 
-func checkRules(ctx BottomUpMutatorContext, currentPkg string, visibility []string) {
+func checkRules(ctx BaseModuleContext, currentPkg, property string, visibility []string) {
 	ruleCount := len(visibility)
 	if ruleCount == 0 {
 		// This prohibits an empty list as its meaning is unclear, e.g. it could mean no visibility and
 		// it could mean public visibility. Requiring at least one rule makes the owner's intent
 		// clearer.
-		ctx.PropertyErrorf("visibility", "must contain at least one visibility rule")
+		ctx.PropertyErrorf(property, "must contain at least one visibility rule")
 		return
 	}
 
 	for _, v := range visibility {
-		ok, pkg, name := splitRule(ctx, v, currentPkg)
+		ok, pkg, name := splitRule(v, currentPkg)
 		if !ok {
 			// Visibility rule is invalid so ignore it. Keep going rather than aborting straight away to
 			// ensure all the rules on this module are checked.
-			ctx.PropertyErrorf("visibility",
+			ctx.PropertyErrorf(property,
 				"invalid visibility pattern %q must match"+
 					" //<package>:<module>, //<package> or :<module>",
 				v)
@@ -230,14 +250,14 @@
 			switch name {
 			case "private", "public":
 			case "legacy_public":
-				ctx.PropertyErrorf("visibility", "//visibility:legacy_public must not be used")
+				ctx.PropertyErrorf(property, "//visibility:legacy_public must not be used")
 				continue
 			default:
-				ctx.PropertyErrorf("visibility", "unrecognized visibility rule %q", v)
+				ctx.PropertyErrorf(property, "unrecognized visibility rule %q", v)
 				continue
 			}
 			if ruleCount != 1 {
-				ctx.PropertyErrorf("visibility", "cannot mix %q with any other visibility rules", v)
+				ctx.PropertyErrorf(property, "cannot mix %q with any other visibility rules", v)
 				continue
 			}
 		}
@@ -246,7 +266,7 @@
 		// restrictions on the rules.
 		if !isAncestor("vendor", currentPkg) {
 			if !isAllowedFromOutsideVendor(pkg, name) {
-				ctx.PropertyErrorf("visibility",
+				ctx.PropertyErrorf(property,
 					"%q is not allowed. Packages outside //vendor cannot make themselves visible to specific"+
 						" targets within //vendor, they can only use //vendor:__subpackages__.", v)
 				continue
@@ -265,23 +285,25 @@
 		return
 	}
 
-	qualified := createQualifiedModuleName(ctx)
+	qualifiedModuleId := m.qualifiedModuleId(ctx)
+	currentPkg := qualifiedModuleId.pkg
 
-	visibility := m.base().commonProperties.Visibility
-	if visibility != nil {
-		rule := parseRules(ctx, qualified.pkg, visibility)
+	// Parse the visibility rules that control access to the module and store them by id
+	// for use when enforcing the rules.
+	if visibility := m.visibility(); visibility != nil {
+		rule := parseRules(ctx, currentPkg, m.visibility())
 		if rule != nil {
-			moduleToVisibilityRuleMap(ctx).Store(qualified, rule)
+			moduleToVisibilityRuleMap(ctx).Store(qualifiedModuleId, rule)
 		}
 	}
 }
 
-func parseRules(ctx BottomUpMutatorContext, currentPkg string, visibility []string) compositeRule {
+func parseRules(ctx BaseModuleContext, currentPkg string, visibility []string) compositeRule {
 	rules := make(compositeRule, 0, len(visibility))
 	hasPrivateRule := false
 	hasNonPrivateRule := false
 	for _, v := range visibility {
-		ok, pkg, name := splitRule(ctx, v, currentPkg)
+		ok, pkg, name := splitRule(v, currentPkg)
 		if !ok {
 			continue
 		}
@@ -336,7 +358,7 @@
 	return !isAncestor("vendor", pkg)
 }
 
-func splitRule(ctx BaseModuleContext, ruleExpression string, currentPkg string) (bool, string, string) {
+func splitRule(ruleExpression string, currentPkg string) (bool, string, string) {
 	// Make sure that the rule is of the correct format.
 	matches := visibilityRuleRegexp.FindStringSubmatch(ruleExpression)
 	if ruleExpression == "" || matches == nil {
@@ -365,8 +387,6 @@
 
 	qualified := createQualifiedModuleName(ctx)
 
-	moduleToVisibilityRule := moduleToVisibilityRuleMap(ctx)
-
 	// Visit all the dependencies making sure that this module has access to them all.
 	ctx.VisitDirectDeps(func(dep Module) {
 		depName := ctx.OtherModuleName(dep)
@@ -378,18 +398,61 @@
 			return
 		}
 
-		rule, ok := moduleToVisibilityRule.Load(depQualified)
-		if ok {
-			if !rule.(compositeRule).matches(qualified) {
-				ctx.ModuleErrorf("depends on %s which is not visible to this module", depQualified)
-			}
+		rule := effectiveVisibilityRules(ctx, depQualified)
+		if rule != nil && !rule.matches(qualified) {
+			ctx.ModuleErrorf("depends on %s which is not visible to this module", depQualified)
 		}
 	})
 }
 
+func effectiveVisibilityRules(ctx BaseModuleContext, qualified qualifiedModuleName) compositeRule {
+	moduleToVisibilityRule := moduleToVisibilityRuleMap(ctx)
+	value, ok := moduleToVisibilityRule.Load(qualified)
+	var rule compositeRule
+	if ok {
+		rule = value.(compositeRule)
+	} else {
+		rule = packageDefaultVisibility(ctx, qualified)
+	}
+	return rule
+}
+
 func createQualifiedModuleName(ctx BaseModuleContext) qualifiedModuleName {
 	moduleName := ctx.ModuleName()
 	dir := ctx.ModuleDir()
 	qualified := qualifiedModuleName{dir, moduleName}
 	return qualified
 }
+
+func packageDefaultVisibility(ctx BaseModuleContext, moduleId qualifiedModuleName) compositeRule {
+	moduleToVisibilityRule := moduleToVisibilityRuleMap(ctx)
+	packageQualifiedId := moduleId.getContainingPackageId()
+	for {
+		value, ok := moduleToVisibilityRule.Load(packageQualifiedId)
+		if ok {
+			return value.(compositeRule)
+		}
+
+		if packageQualifiedId.isRootPackage() {
+			return nil
+		}
+
+		packageQualifiedId = packageQualifiedId.getContainingPackageId()
+	}
+}
+
+// Get the effective visibility rules, i.e. the actual rules that affect the visibility of the
+// property irrespective of where they are defined.
+//
+// Includes visibility rules specified by package default_visibility and/or on defaults.
+// Short hand forms, e.g. //:__subpackages__ are replaced with their full form, e.g.
+// //package/containing/rule:__subpackages__.
+func EffectiveVisibilityRules(ctx BaseModuleContext, module Module) []string {
+	moduleName := ctx.OtherModuleName(module)
+	dir := ctx.OtherModuleDir(module)
+	qualified := qualifiedModuleName{dir, moduleName}
+
+	rule := effectiveVisibilityRules(ctx, qualified)
+
+	return rule.Strings()
+}
diff --git a/android/visibility_test.go b/android/visibility_test.go
index 1a51495..fbf2fb7 100644
--- a/android/visibility_test.go
+++ b/android/visibility_test.go
@@ -658,6 +658,201 @@
 				` visible to this module`,
 		},
 	},
+
+	// Defaults module's defaults_visibility tests
+	{
+		name: "defaults_visibility invalid",
+		fs: map[string][]byte{
+			"top/Blueprints": []byte(`
+				mock_defaults {
+					name: "top_defaults",
+					defaults_visibility: ["//visibility:invalid"],
+				}`),
+		},
+		expectedErrors: []string{
+			`defaults_visibility: unrecognized visibility rule "//visibility:invalid"`,
+		},
+	},
+	{
+		name: "defaults_visibility overrides package default",
+		fs: map[string][]byte{
+			"top/Blueprints": []byte(`
+				package {
+					default_visibility: ["//visibility:private"],
+				}
+				mock_defaults {
+					name: "top_defaults",
+					defaults_visibility: ["//visibility:public"],
+				}`),
+			"outsider/Blueprints": []byte(`
+				mock_library {
+					name: "liboutsider",
+					defaults: ["top_defaults"],
+				}`),
+		},
+	},
+
+	// Package default_visibility tests
+	{
+		name: "package default_visibility property is checked",
+		fs: map[string][]byte{
+			"top/Blueprints": []byte(`
+				package {
+					default_visibility: ["//visibility:invalid"],
+				}`),
+		},
+		expectedErrors: []string{`default_visibility: unrecognized visibility rule "//visibility:invalid"`},
+	},
+	{
+		// This test relies on the default visibility being legacy_public.
+		name: "package default_visibility property used when no visibility specified",
+		fs: map[string][]byte{
+			"top/Blueprints": []byte(`
+				package {
+					default_visibility: ["//visibility:private"],
+				}
+
+				mock_library {
+					name: "libexample",
+				}`),
+			"outsider/Blueprints": []byte(`
+				mock_library {
+					name: "liboutsider",
+					deps: ["libexample"],
+				}`),
+		},
+		expectedErrors: []string{
+			`module "liboutsider" variant "android_common": depends on //top:libexample which is not` +
+				` visible to this module`,
+		},
+	},
+	{
+		name: "package default_visibility public does not override visibility private",
+		fs: map[string][]byte{
+			"top/Blueprints": []byte(`
+				package {
+					default_visibility: ["//visibility:public"],
+				}
+
+				mock_library {
+					name: "libexample",
+					visibility: ["//visibility:private"],
+				}`),
+			"outsider/Blueprints": []byte(`
+				mock_library {
+					name: "liboutsider",
+					deps: ["libexample"],
+				}`),
+		},
+		expectedErrors: []string{
+			`module "liboutsider" variant "android_common": depends on //top:libexample which is not` +
+				` visible to this module`,
+		},
+	},
+	{
+		name: "package default_visibility private does not override visibility public",
+		fs: map[string][]byte{
+			"top/Blueprints": []byte(`
+				package {
+					default_visibility: ["//visibility:private"],
+				}
+
+				mock_library {
+					name: "libexample",
+					visibility: ["//visibility:public"],
+				}`),
+			"outsider/Blueprints": []byte(`
+				mock_library {
+					name: "liboutsider",
+					deps: ["libexample"],
+				}`),
+		},
+	},
+	{
+		name: "package default_visibility :__subpackages__",
+		fs: map[string][]byte{
+			"top/Blueprints": []byte(`
+				package {
+					default_visibility: [":__subpackages__"],
+				}
+
+				mock_library {
+					name: "libexample",
+				}`),
+			"top/nested/Blueprints": []byte(`
+				mock_library {
+					name: "libnested",
+					deps: ["libexample"],
+				}`),
+			"outsider/Blueprints": []byte(`
+				mock_library {
+					name: "liboutsider",
+					deps: ["libexample"],
+				}`),
+		},
+		expectedErrors: []string{
+			`module "liboutsider" variant "android_common": depends on //top:libexample which is not` +
+				` visible to this module`,
+		},
+	},
+	{
+		name: "package default_visibility inherited to subpackages",
+		fs: map[string][]byte{
+			"top/Blueprints": []byte(`
+				package {
+					default_visibility: ["//outsider"],
+				}
+
+				mock_library {
+					name: "libexample",
+          visibility: [":__subpackages__"],
+				}`),
+			"top/nested/Blueprints": []byte(`
+				mock_library {
+					name: "libnested",
+					deps: ["libexample"],
+				}`),
+			"outsider/Blueprints": []byte(`
+				mock_library {
+					name: "liboutsider",
+					deps: ["libexample", "libnested"],
+				}`),
+		},
+		expectedErrors: []string{
+			`module "liboutsider" variant "android_common": depends on //top:libexample which is not` +
+				` visible to this module`,
+		},
+	},
+	{
+		name: "package default_visibility inherited to subpackages",
+		fs: map[string][]byte{
+			"top/Blueprints": []byte(`
+				package {
+					default_visibility: ["//visibility:private"],
+				}`),
+			"top/nested/Blueprints": []byte(`
+				package {
+					default_visibility: ["//outsider"],
+				}
+
+				mock_library {
+					name: "libnested",
+				}`),
+			"top/other/Blueprints": []byte(`
+				mock_library {
+					name: "libother",
+				}`),
+			"outsider/Blueprints": []byte(`
+				mock_library {
+					name: "liboutsider",
+					deps: ["libother", "libnested"],
+				}`),
+		},
+		expectedErrors: []string{
+			`module "liboutsider" variant "android_common": depends on //top/other:libother which is` +
+				` not visible to this module`,
+		},
+	},
 }
 
 func TestVisibility(t *testing.T) {
@@ -665,23 +860,7 @@
 		t.Run(test.name, func(t *testing.T) {
 			_, errs := testVisibility(buildDir, test.fs)
 
-			expectedErrors := test.expectedErrors
-			if expectedErrors == nil {
-				FailIfErrored(t, errs)
-			} else {
-				for _, expectedError := range expectedErrors {
-					FailIfNoMatchingErrors(t, expectedError, errs)
-				}
-				if len(errs) > len(expectedErrors) {
-					t.Errorf("additional errors found, expected %d, found %d", len(expectedErrors), len(errs))
-					for i, expectedError := range expectedErrors {
-						t.Errorf("expectedErrors[%d] = %s", i, expectedError)
-					}
-					for i, err := range errs {
-						t.Errorf("errs[%d] = %s", i, err)
-					}
-				}
-			}
+			CheckErrorsAgainstExpectations(t, errs, test.expectedErrors)
 		})
 	}
 }
@@ -689,18 +868,18 @@
 func testVisibility(buildDir string, fs map[string][]byte) (*TestContext, []error) {
 
 	// Create a new config per test as visibility information is stored in the config.
-	config := TestArchConfig(buildDir, nil)
+	config := TestArchConfig(buildDir, nil, "", fs)
 
 	ctx := NewTestArchContext()
-	ctx.RegisterModuleType("mock_library", ModuleFactoryAdaptor(newMockLibraryModule))
-	ctx.RegisterModuleType("mock_defaults", ModuleFactoryAdaptor(defaultsFactory))
-	ctx.PreArchMutators(registerVisibilityRuleChecker)
+	ctx.RegisterModuleType("package", PackageFactory)
+	ctx.RegisterModuleType("mock_library", newMockLibraryModule)
+	ctx.RegisterModuleType("mock_defaults", defaultsFactory)
+	ctx.PreArchMutators(RegisterPackageRenamer)
+	ctx.PreArchMutators(RegisterVisibilityRuleChecker)
 	ctx.PreArchMutators(RegisterDefaultsPreArchMutators)
-	ctx.PreArchMutators(registerVisibilityRuleGatherer)
-	ctx.PostDepsMutators(registerVisibilityRuleEnforcer)
-	ctx.Register()
-
-	ctx.MockFileSystem(fs)
+	ctx.PreArchMutators(RegisterVisibilityRuleGatherer)
+	ctx.PostDepsMutators(RegisterVisibilityRuleEnforcer)
+	ctx.Register(config)
 
 	_, errs := ctx.ParseBlueprintsFiles(".")
 	if len(errs) > 0 {
diff --git a/android/vts_config.go b/android/vts_config.go
index c44b3a3..86f6e72 100644
--- a/android/vts_config.go
+++ b/android/vts_config.go
@@ -41,16 +41,17 @@
 func (me *VtsConfig) AndroidMk() AndroidMkData {
 	androidMkData := AndroidMkData{
 		Class:      "FAKE",
-		Include:    "$(BUILD_SYSTEM)/android_vts_host_config.mk",
+		Include:    "$(BUILD_SYSTEM)/suite_host_config.mk",
 		OutputFile: OptionalPathForPath(me.OutputFilePath),
 	}
-	if me.properties.Test_config != nil {
-		androidMkData.Extra = []AndroidMkExtraFunc{
-			func(w io.Writer, outputFile Path) {
+	androidMkData.Extra = []AndroidMkExtraFunc{
+		func(w io.Writer, outputFile Path) {
+			if me.properties.Test_config != nil {
 				fmt.Fprintf(w, "LOCAL_TEST_CONFIG := %s\n",
 					*me.properties.Test_config)
-			},
-		}
+			}
+			fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE := vts")
+		},
 	}
 	return androidMkData
 }
diff --git a/android/vts_config_test.go b/android/vts_config_test.go
index 142b2f5..254fa92 100644
--- a/android/vts_config_test.go
+++ b/android/vts_config_test.go
@@ -19,15 +19,11 @@
 )
 
 func testVtsConfig(test *testing.T, bpFileContents string) *TestContext {
-	config := TestArchConfig(buildDir, nil)
+	config := TestArchConfig(buildDir, nil, bpFileContents, nil)
 
 	ctx := NewTestArchContext()
-	ctx.RegisterModuleType("vts_config", ModuleFactoryAdaptor(VtsConfigFactory))
-	ctx.Register()
-	mockFiles := map[string][]byte{
-		"Android.bp": []byte(bpFileContents),
-	}
-	ctx.MockFileSystem(mockFiles)
+	ctx.RegisterModuleType("vts_config", VtsConfigFactory)
+	ctx.Register(config)
 	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
 	FailIfErrored(test, errs)
 	_, errs = ctx.PrepareBuildActions(config)
diff --git a/androidmk/Android.bp b/androidmk/Android.bp
index 79fe530..70fc1f7 100644
--- a/androidmk/Android.bp
+++ b/androidmk/Android.bp
@@ -19,18 +19,28 @@
 blueprint_go_binary {
     name: "androidmk",
     srcs: [
-        "cmd/androidmk/android.go",
-        "cmd/androidmk/androidmk.go",
-        "cmd/androidmk/values.go",
+        "cmd/androidmk.go",
+    ],
+    deps: [
+        "androidmk-lib",
+    ],
+}
+
+bootstrap_go_package {
+    name: "androidmk-lib",
+    pkgPath: "android/soong/androidmk/androidmk",
+    srcs: [
+        "androidmk/android.go",
+        "androidmk/androidmk.go",
+        "androidmk/values.go",
     ],
     testSrcs: [
-        "cmd/androidmk/androidmk_test.go",
+        "androidmk/androidmk_test.go",
     ],
     deps: [
         "androidmk-parser",
         "blueprint-parser",
         "bpfix-lib",
-        "soong-android",
     ],
 }
 
diff --git a/androidmk/cmd/androidmk/android.go b/androidmk/androidmk/android.go
similarity index 97%
rename from androidmk/cmd/androidmk/android.go
rename to androidmk/androidmk/android.go
index af81e43..8860984 100644
--- a/androidmk/cmd/androidmk/android.go
+++ b/androidmk/androidmk/android.go
@@ -12,12 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package main
+package androidmk
 
 import (
-	"android/soong/android"
 	mkparser "android/soong/androidmk/parser"
 	"fmt"
+	"sort"
 	"strings"
 
 	bpparser "github.com/google/blueprint/parser"
@@ -173,6 +173,8 @@
 			// Jacoco filters:
 			"LOCAL_JACK_COVERAGE_INCLUDE_FILTER": "jacoco.include_filter",
 			"LOCAL_JACK_COVERAGE_EXCLUDE_FILTER": "jacoco.exclude_filter",
+
+			"LOCAL_FULL_LIBS_MANIFEST_FILES": "additional_manifests",
 		})
 
 	addStandardProperties(bpparser.BoolType,
@@ -192,10 +194,11 @@
 			"LOCAL_VENDOR_MODULE":              "vendor",
 			"LOCAL_ODM_MODULE":                 "device_specific",
 			"LOCAL_PRODUCT_MODULE":             "product_specific",
-			"LOCAL_PRODUCT_SERVICES_MODULE":    "product_services_specific",
+			"LOCAL_SYSTEM_EXT_MODULE":          "system_ext_specific",
 			"LOCAL_EXPORT_PACKAGE_RESOURCES":   "export_package_resources",
 			"LOCAL_PRIVILEGED_MODULE":          "privileged",
 			"LOCAL_AAPT_INCLUDE_ALL_RESOURCES": "aapt_include_all_resources",
+			"LOCAL_DONT_MERGE_MANIFESTS":       "dont_merge_manifests",
 			"LOCAL_USE_EMBEDDED_NATIVE_LIBS":   "use_embedded_native_libs",
 			"LOCAL_USE_EMBEDDED_DEX":           "use_embedded_dex",
 
@@ -347,7 +350,13 @@
 		return err
 	}
 
-	for _, nameClassification := range android.SortedStringKeys(namesByClassification) {
+	var classifications []string
+	for classification := range namesByClassification {
+		classifications = append(classifications, classification)
+	}
+	sort.Strings(classifications)
+
+	for _, nameClassification := range classifications {
 		name := namesByClassification[nameClassification]
 		if component, ok := lists[nameClassification]; ok && !emptyList(component) {
 			err = setVariable(ctx.file, ctx.append, ctx.prefix, name, component, true)
@@ -602,8 +611,8 @@
 		return fmt.Errorf("Cannot handle appending to LOCAL_MODULE_PATH")
 	}
 	// Analyze value in order to set the correct values for the 'device_specific',
-	// 'product_specific', 'product_services_specific' 'vendor'/'soc_specific',
-	// 'product_services_specific' attribute. Two cases are allowed:
+	// 'product_specific', 'system_ext_specific' 'vendor'/'soc_specific',
+	// 'system_ext_specific' attribute. Two cases are allowed:
 	//   $(VAR)/<literal-value>
 	//   $(PRODUCT_OUT)/$(TARGET_COPY_OUT_VENDOR)/<literal-value>
 	// The last case is equivalent to $(TARGET_OUT_VENDOR)/<literal-value>
diff --git a/androidmk/cmd/androidmk/androidmk.go b/androidmk/androidmk/androidmk.go
similarity index 93%
rename from androidmk/cmd/androidmk/androidmk.go
rename to androidmk/androidmk/androidmk.go
index d2a84d1..9d0c3ac 100644
--- a/androidmk/cmd/androidmk/androidmk.go
+++ b/androidmk/androidmk/androidmk.go
@@ -12,14 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package main
+package androidmk
 
 import (
 	"bytes"
-	"flag"
 	"fmt"
-	"io/ioutil"
-	"os"
 	"strings"
 	"text/scanner"
 
@@ -30,13 +27,6 @@
 	bpparser "github.com/google/blueprint/parser"
 )
 
-var usage = func() {
-	fmt.Fprintf(os.Stderr, "usage: androidmk [flags] <inputFile>\n"+
-		"\nandroidmk parses <inputFile> as an Android.mk file and attempts to output an analogous Android.bp file (to standard out)\n")
-	flag.PrintDefaults()
-	os.Exit(1)
-}
-
 // TODO: non-expanded variables with expressions
 
 type bpFile struct {
@@ -118,31 +108,7 @@
 	eq   bool
 }
 
-func main() {
-	flag.Usage = usage
-	flag.Parse()
-	if len(flag.Args()) != 1 {
-		usage()
-	}
-	filePathToRead := flag.Arg(0)
-	b, err := ioutil.ReadFile(filePathToRead)
-	if err != nil {
-		fmt.Println(err.Error())
-		return
-	}
-
-	output, errs := convertFile(os.Args[1], bytes.NewBuffer(b))
-	if len(errs) > 0 {
-		for _, err := range errs {
-			fmt.Fprintln(os.Stderr, "ERROR: ", err)
-		}
-		os.Exit(1)
-	}
-
-	fmt.Print(output)
-}
-
-func convertFile(filename string, buffer *bytes.Buffer) (string, []error) {
+func ConvertFile(filename string, buffer *bytes.Buffer) (string, []error) {
 	p := mkparser.NewParser(filename, buffer)
 
 	nodes, errs := p.Parse()
diff --git a/androidmk/cmd/androidmk/androidmk_test.go b/androidmk/androidmk/androidmk_test.go
similarity index 98%
rename from androidmk/cmd/androidmk/androidmk_test.go
rename to androidmk/androidmk/androidmk_test.go
index 4d5180e..7e1a72c 100644
--- a/androidmk/cmd/androidmk/androidmk_test.go
+++ b/androidmk/androidmk/androidmk_test.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package main
+package androidmk
 
 import (
 	"bytes"
@@ -957,37 +957,37 @@
 `,
 	},
 	{
-		desc: "prebuilt_etc_TARGET_OUT_PRODUCT_SERVICES/etc",
+		desc: "prebuilt_etc_TARGET_OUT_SYSTEM_EXT/etc",
 		in: `
 include $(CLEAR_VARS)
 LOCAL_MODULE := etc.test1
 LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT_SERVICES)/etc/foo/bar
+LOCAL_MODULE_PATH := $(TARGET_OUT_SYSTEM_EXT)/etc/foo/bar
 include $(BUILD_PREBUILT)
 `,
 		expected: `
 prebuilt_etc {
 	name: "etc.test1",
 	sub_dir: "foo/bar",
-	product_services_specific: true,
+	system_ext_specific: true,
 
 }
 `,
 	},
 	{
-		desc: "prebuilt_etc_TARGET_OUT_PRODUCT_SERVICES_ETC",
+		desc: "prebuilt_etc_TARGET_OUT_SYSTEM_EXT_ETC",
 		in: `
 include $(CLEAR_VARS)
 LOCAL_MODULE := etc.test1
 LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT_SERVICES_ETC)/foo/bar
+LOCAL_MODULE_PATH := $(TARGET_OUT_SYSTEM_EXT_ETC)/foo/bar
 include $(BUILD_PREBUILT)
 `,
 		expected: `
 prebuilt_etc {
 	name: "etc.test1",
 	sub_dir: "foo/bar",
-	product_services_specific: true,
+	system_ext_specific: true,
 
 
 }
@@ -1350,7 +1350,7 @@
 			t.Error(err)
 		}
 
-		got, errs := convertFile(fmt.Sprintf("<testcase %d>", i), bytes.NewBufferString(test.in))
+		got, errs := ConvertFile(fmt.Sprintf("<testcase %d>", i), bytes.NewBufferString(test.in))
 		if len(errs) > 0 {
 			t.Errorf("Unexpected errors: %q", errs)
 			continue
diff --git a/androidmk/cmd/androidmk/values.go b/androidmk/androidmk/values.go
similarity index 99%
rename from androidmk/cmd/androidmk/values.go
rename to androidmk/androidmk/values.go
index 90f2e74..6b18a65 100644
--- a/androidmk/cmd/androidmk/values.go
+++ b/androidmk/androidmk/values.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package main
+package androidmk
 
 import (
 	"fmt"
diff --git a/androidmk/cmd/androidmk.go b/androidmk/cmd/androidmk.go
new file mode 100644
index 0000000..00488eb
--- /dev/null
+++ b/androidmk/cmd/androidmk.go
@@ -0,0 +1,56 @@
+// Copyright 2017 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 main
+
+import (
+	"bytes"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"os"
+
+	"android/soong/androidmk/androidmk"
+)
+
+var usage = func() {
+	fmt.Fprintf(os.Stderr, "usage: androidmk [flags] <inputFile>\n"+
+		"\nandroidmk parses <inputFile> as an Android.mk file and attempts to output an analogous Android.bp file (to standard out)\n")
+	flag.PrintDefaults()
+	os.Exit(1)
+}
+
+func main() {
+	flag.Usage = usage
+	flag.Parse()
+	if len(flag.Args()) != 1 {
+		usage()
+	}
+	filePathToRead := flag.Arg(0)
+	b, err := ioutil.ReadFile(filePathToRead)
+	if err != nil {
+		fmt.Println(err.Error())
+		return
+	}
+
+	output, errs := androidmk.ConvertFile(os.Args[1], bytes.NewBuffer(b))
+	if len(errs) > 0 {
+		for _, err := range errs {
+			fmt.Fprintln(os.Stderr, "ERROR: ", err)
+		}
+		os.Exit(1)
+	}
+
+	fmt.Print(output)
+}
diff --git a/apex/TEST_MAPPING b/apex/TEST_MAPPING
new file mode 100644
index 0000000..d0223d1
--- /dev/null
+++ b/apex/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "imports": [
+    {
+      "path": "system/apex/apexd"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/apex/androidmk.go b/apex/androidmk.go
new file mode 100644
index 0000000..b2fe8da
--- /dev/null
+++ b/apex/androidmk.go
@@ -0,0 +1,250 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package apex
+
+import (
+	"fmt"
+	"io"
+	"path/filepath"
+	"strings"
+
+	"android/soong/android"
+	"android/soong/cc"
+
+	"github.com/google/blueprint/proptools"
+)
+
+func (a *apexBundle) AndroidMk() android.AndroidMkData {
+	if a.properties.HideFromMake {
+		return android.AndroidMkData{
+			Disabled: true,
+		}
+	}
+	writers := []android.AndroidMkData{}
+	writers = append(writers, a.androidMkForType())
+	return android.AndroidMkData{
+		Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
+			for _, data := range writers {
+				data.Custom(w, name, prefix, moduleDir, data)
+			}
+		}}
+}
+
+func (a *apexBundle) androidMkForFiles(w io.Writer, apexName, moduleDir string) []string {
+	moduleNames := []string{}
+	apexType := a.properties.ApexType
+	// To avoid creating duplicate build rules, run this function only when primaryApexType is true
+	// to install symbol files in $(PRODUCT_OUT}/apex.
+	// And if apexType is flattened, run this function to install files in $(PRODUCT_OUT}/system/apex.
+	if !a.primaryApexType && apexType != flattenedApex {
+		return moduleNames
+	}
+
+	for _, fi := range a.filesInfo {
+		if cc, ok := fi.module.(*cc.Module); ok && cc.Properties.HideFromMake {
+			continue
+		}
+
+		if !android.InList(fi.moduleName, moduleNames) {
+			moduleNames = append(moduleNames, fi.moduleName)
+		}
+
+		fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
+		if fi.moduleDir != "" {
+			fmt.Fprintln(w, "LOCAL_PATH :=", fi.moduleDir)
+		} else {
+			fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
+		}
+		fmt.Fprintln(w, "LOCAL_MODULE :=", fi.moduleName)
+		// /apex/<apex_name>/{lib|framework|...}
+		pathWhenActivated := filepath.Join("$(PRODUCT_OUT)", "apex", apexName, fi.installDir)
+		if apexType == flattenedApex {
+			// /system/apex/<name>/{lib|framework|...}
+			fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", filepath.Join(a.installDir.ToMakePath().String(),
+				apexName, fi.installDir))
+			if a.primaryApexType {
+				fmt.Fprintln(w, "LOCAL_SOONG_SYMBOL_PATH :=", pathWhenActivated)
+			}
+			if len(fi.symlinks) > 0 {
+				fmt.Fprintln(w, "LOCAL_MODULE_SYMLINKS :=", strings.Join(fi.symlinks, " "))
+			}
+
+			if fi.module != nil && fi.module.NoticeFile().Valid() {
+				fmt.Fprintln(w, "LOCAL_NOTICE_FILE :=", fi.module.NoticeFile().Path().String())
+			}
+		} else {
+			fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", pathWhenActivated)
+		}
+		fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", fi.builtFile.String())
+		fmt.Fprintln(w, "LOCAL_MODULE_CLASS :=", fi.class.NameInMake())
+		if fi.module != nil {
+			archStr := fi.module.Target().Arch.ArchType.String()
+			host := false
+			switch fi.module.Target().Os.Class {
+			case android.Host:
+				if fi.module.Target().Arch.ArchType != android.Common {
+					fmt.Fprintln(w, "LOCAL_MODULE_HOST_ARCH :=", archStr)
+				}
+				host = true
+			case android.HostCross:
+				if fi.module.Target().Arch.ArchType != android.Common {
+					fmt.Fprintln(w, "LOCAL_MODULE_HOST_CROSS_ARCH :=", archStr)
+				}
+				host = true
+			case android.Device:
+				if fi.module.Target().Arch.ArchType != android.Common {
+					fmt.Fprintln(w, "LOCAL_MODULE_TARGET_ARCH :=", archStr)
+				}
+			}
+			if host {
+				makeOs := fi.module.Target().Os.String()
+				if fi.module.Target().Os == android.Linux || fi.module.Target().Os == android.LinuxBionic {
+					makeOs = "linux"
+				}
+				fmt.Fprintln(w, "LOCAL_MODULE_HOST_OS :=", makeOs)
+				fmt.Fprintln(w, "LOCAL_IS_HOST_MODULE := true")
+			}
+		}
+		if fi.jacocoReportClassesFile != nil {
+			fmt.Fprintln(w, "LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR :=", fi.jacocoReportClassesFile.String())
+		}
+		if fi.class == javaSharedLib {
+			javaModule := fi.module.(javaLibrary)
+			// 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_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 {
+			// 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, "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())
+				}
+				cc.AndroidMkWriteAdditionalDependenciesForSourceAbiDiff(w)
+				if cc.CoverageOutputFile().Valid() {
+					fmt.Fprintln(w, "LOCAL_PREBUILT_COVERAGE_ARCHIVE :=", cc.CoverageOutputFile().String())
+				}
+			}
+			fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_cc_prebuilt.mk")
+		} else {
+			fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", fi.builtFile.Base())
+			if a.primaryApexType && fi.builtFile == a.manifestPbOut {
+				// Make apex_manifest.pb module for this APEX to override all other
+				// modules in the APEXes being overridden by this APEX
+				var patterns []string
+				for _, o := range a.overridableProperties.Overrides {
+					patterns = append(patterns, "%."+o+a.suffix)
+				}
+				fmt.Fprintln(w, "LOCAL_OVERRIDES_MODULES :=", strings.Join(patterns, " "))
+
+				if len(a.compatSymlinks) > 0 {
+					// For flattened apexes, compat symlinks are attached to apex_manifest.json which is guaranteed for every apex
+					fmt.Fprintln(w, "LOCAL_POST_INSTALL_CMD :=", strings.Join(a.compatSymlinks, " && "))
+				}
+			}
+			fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
+		}
+	}
+	return moduleNames
+}
+
+func (a *apexBundle) writeRequiredModules(w io.Writer) {
+	var required []string
+	var targetRequired []string
+	var hostRequired []string
+	for _, fi := range a.filesInfo {
+		required = append(required, fi.requiredModuleNames...)
+		targetRequired = append(targetRequired, fi.targetRequiredModuleNames...)
+		hostRequired = append(hostRequired, fi.hostRequiredModuleNames...)
+	}
+
+	if len(required) > 0 {
+		fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES +=", strings.Join(required, " "))
+	}
+	if len(targetRequired) > 0 {
+		fmt.Fprintln(w, "LOCAL_TARGET_REQUIRED_MODULES +=", strings.Join(targetRequired, " "))
+	}
+	if len(hostRequired) > 0 {
+		fmt.Fprintln(w, "LOCAL_HOST_REQUIRED_MODULES +=", strings.Join(hostRequired, " "))
+	}
+}
+
+func (a *apexBundle) androidMkForType() android.AndroidMkData {
+	return android.AndroidMkData{
+		Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
+			moduleNames := []string{}
+			apexType := a.properties.ApexType
+			if a.installable() {
+				apexName := proptools.StringDefault(a.properties.Apex_name, name)
+				moduleNames = a.androidMkForFiles(w, apexName, moduleDir)
+			}
+
+			if apexType == flattenedApex {
+				// Only image APEXes can be flattened.
+				fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
+				fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
+				fmt.Fprintln(w, "LOCAL_MODULE :=", name+a.suffix)
+				if len(moduleNames) > 0 {
+					fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES :=", strings.Join(moduleNames, " "))
+				}
+				a.writeRequiredModules(w)
+				fmt.Fprintln(w, "include $(BUILD_PHONY_PACKAGE)")
+
+			} else {
+				fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
+				fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
+				fmt.Fprintln(w, "LOCAL_MODULE :=", name+a.suffix)
+				fmt.Fprintln(w, "LOCAL_MODULE_CLASS := ETC") // do we need a new class?
+				fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", a.outputFile.String())
+				fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", a.installDir.ToMakePath().String())
+				fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", name+apexType.suffix())
+				fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE :=", !a.installable())
+				fmt.Fprintln(w, "LOCAL_OVERRIDES_MODULES :=", strings.Join(a.overridableProperties.Overrides, " "))
+				if len(moduleNames) > 0 {
+					fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES +=", strings.Join(moduleNames, " "))
+				}
+				if len(a.requiredDeps) > 0 {
+					fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES +=", strings.Join(a.requiredDeps, " "))
+				}
+				a.writeRequiredModules(w)
+				var postInstallCommands []string
+				if a.prebuiltFileToDelete != "" {
+					postInstallCommands = append(postInstallCommands, "rm -rf "+
+						filepath.Join(a.installDir.ToMakePath().String(), a.prebuiltFileToDelete))
+				}
+				// For unflattened apexes, compat symlinks are attached to apex package itself as LOCAL_POST_INSTALL_CMD
+				postInstallCommands = append(postInstallCommands, a.compatSymlinks...)
+				if len(postInstallCommands) > 0 {
+					fmt.Fprintln(w, "LOCAL_POST_INSTALL_CMD :=", strings.Join(postInstallCommands, " && "))
+				}
+				fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
+
+				if apexType == imageApex {
+					fmt.Fprintln(w, "ALL_MODULES.$(LOCAL_MODULE).BUNDLE :=", a.bundleModuleFile.String())
+				}
+			}
+		}}
+}
diff --git a/apex/apex.go b/apex/apex.go
index 84e5497..7d6b7f0 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -16,11 +16,11 @@
 
 import (
 	"fmt"
-	"io"
+	"path"
 	"path/filepath"
-	"runtime"
 	"sort"
 	"strings"
+	"sync"
 
 	"android/soong/android"
 	"android/soong/cc"
@@ -32,78 +32,16 @@
 	"github.com/google/blueprint/proptools"
 )
 
-var (
-	pctx = android.NewPackageContext("android/apex")
+const (
+	imageApexSuffix = ".apex"
+	zipApexSuffix   = ".zipapex"
+	flattenedSuffix = ".flattened"
 
-	// Create a canned fs config file where all files and directories are
-	// 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 '/apex_manifest.json 1000 1000 0644' >> ${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")
-
-	// TODO(b/113233103): make sure that file_contexts is sane, i.e., validate
-	// against the binary policy using sefcontext_compiler -p <policy>.
-
-	// TODO(b/114327326): automate the generation of file_contexts
-	apexRule = pctx.StaticRule("apexRule", blueprint.RuleParams{
-		Command: `rm -rf ${image_dir} && mkdir -p ${image_dir} && ` +
-			`(${copy_commands}) && ` +
-			`APEXER_TOOL_PATH=${tool_path} ` +
-			`${apexer} --force --manifest ${manifest} ` +
-			`--file_contexts ${file_contexts} ` +
-			`--canned_fs_config ${canned_fs_config} ` +
-			`--payload_type image ` +
-			`--key ${key} ${opt_flags} ${image_dir} ${out} `,
-		CommandDeps: []string{"${apexer}", "${avbtool}", "${e2fsdroid}", "${merge_zips}",
-			"${mke2fs}", "${resize2fs}", "${sefcontext_compile}",
-			"${soong_zip}", "${zipalign}", "${aapt2}", "prebuilts/sdk/current/public/android.jar"},
-		Description: "APEX ${image_dir} => ${out}",
-	}, "tool_path", "image_dir", "copy_commands", "manifest", "file_contexts", "canned_fs_config", "key", "opt_flags")
-
-	zipApexRule = pctx.StaticRule("zipApexRule", blueprint.RuleParams{
-		Command: `rm -rf ${image_dir} && mkdir -p ${image_dir} && ` +
-			`(${copy_commands}) && ` +
-			`APEXER_TOOL_PATH=${tool_path} ` +
-			`${apexer} --force --manifest ${manifest} ` +
-			`--payload_type zip ` +
-			`${image_dir} ${out} `,
-		CommandDeps: []string{"${apexer}", "${merge_zips}", "${soong_zip}", "${zipalign}", "${aapt2}"},
-		Description: "ZipAPEX ${image_dir} => ${out}",
-	}, "tool_path", "image_dir", "copy_commands", "manifest")
-
-	apexProtoConvertRule = pctx.AndroidStaticRule("apexProtoConvertRule",
-		blueprint.RuleParams{
-			Command:     `${aapt2} convert --output-format proto $in -o $out`,
-			CommandDeps: []string{"${aapt2}"},
-		})
-
-	apexBundleRule = pctx.StaticRule("apexBundleRule", blueprint.RuleParams{
-		Command: `${zip2zip} -i $in -o $out ` +
-			`apex_payload.img:apex/${abi}.img ` +
-			`apex_manifest.json:root/apex_manifest.json ` +
-			`AndroidManifest.xml:manifest/AndroidManifest.xml`,
-		CommandDeps: []string{"${zip2zip}"},
-		Description: "app bundle",
-	}, "abi")
-
-	apexMergeNoticeRule = pctx.StaticRule("apexMergeNoticeRule", blueprint.RuleParams{
-		Command:     `${mergenotice} --output $out $inputs`,
-		CommandDeps: []string{"${mergenotice}"},
-		Description: "merge notice files into $out",
-	}, "inputs")
+	imageApexType     = "image"
+	zipApexType       = "zip"
+	flattenedApexType = "flattened"
 )
 
-var imageApexSuffix = ".apex"
-var zipApexSuffix = ".zipapex"
-
-var imageApexType = "image"
-var zipApexType = "zip"
-
 type dependencyTag struct {
 	blueprint.BaseDependencyTag
 	name string
@@ -114,52 +52,46 @@
 	executableTag  = dependencyTag{name: "executable"}
 	javaLibTag     = dependencyTag{name: "javaLib"}
 	prebuiltTag    = dependencyTag{name: "prebuilt"}
+	testTag        = dependencyTag{name: "test"}
 	keyTag         = dependencyTag{name: "key"}
 	certificateTag = dependencyTag{name: "certificate"}
+	usesTag        = dependencyTag{name: "uses"}
+	androidAppTag  = dependencyTag{name: "androidApp"}
 )
 
 func init() {
-	pctx.Import("android/soong/android")
-	pctx.Import("android/soong/java")
-	pctx.HostBinToolVariable("apexer", "apexer")
-	// ART minimal builds (using the master-art manifest) do not have the "frameworks/base"
-	// projects, and hence cannot built 'aapt2'. Use the SDK prebuilt instead.
-	hostBinToolVariableWithPrebuilt := func(name, prebuiltDir, tool string) {
-		pctx.VariableFunc(name, func(ctx android.PackageVarContext) string {
-			if !ctx.Config().FrameworksBaseDirExists(ctx) {
-				return filepath.Join(prebuiltDir, runtime.GOOS, "bin", tool)
-			} else {
-				return pctx.HostBinToolPath(ctx, tool).String()
-			}
-		})
-	}
-	hostBinToolVariableWithPrebuilt("aapt2", "prebuilts/sdk/tools", "aapt2")
-	pctx.HostBinToolVariable("avbtool", "avbtool")
-	pctx.HostBinToolVariable("e2fsdroid", "e2fsdroid")
-	pctx.HostBinToolVariable("merge_zips", "merge_zips")
-	pctx.HostBinToolVariable("mke2fs", "mke2fs")
-	pctx.HostBinToolVariable("resize2fs", "resize2fs")
-	pctx.HostBinToolVariable("sefcontext_compile", "sefcontext_compile")
-	pctx.HostBinToolVariable("soong_zip", "soong_zip")
-	pctx.HostBinToolVariable("zip2zip", "zip2zip")
-	pctx.HostBinToolVariable("zipalign", "zipalign")
-
-	pctx.SourcePathVariable("mergenotice", "build/soong/scripts/mergenotice.py")
-
-	android.RegisterModuleType("apex", apexBundleFactory)
+	android.RegisterModuleType("apex", BundleFactory)
 	android.RegisterModuleType("apex_test", testApexBundleFactory)
+	android.RegisterModuleType("apex_vndk", vndkApexBundleFactory)
 	android.RegisterModuleType("apex_defaults", defaultsFactory)
 	android.RegisterModuleType("prebuilt_apex", PrebuiltFactory)
+	android.RegisterModuleType("override_apex", overrideApexFactory)
 
-	android.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.TopDown("apex_deps", apexDepsMutator)
-		ctx.BottomUp("apex", apexMutator)
+	android.PreDepsMutators(RegisterPreDepsMutators)
+	android.PostDepsMutators(RegisterPostDepsMutators)
+
+	android.RegisterMakeVarsProvider(pctx, func(ctx android.MakeVarsContext) {
+		apexFileContextsInfos := apexFileContextsInfos(ctx.Config())
+		sort.Strings(*apexFileContextsInfos)
+		ctx.Strict("APEX_FILE_CONTEXTS_INFOS", strings.Join(*apexFileContextsInfos, " "))
 	})
 }
 
+func RegisterPreDepsMutators(ctx android.RegisterMutatorsContext) {
+	ctx.TopDown("apex_vndk", apexVndkMutator).Parallel()
+	ctx.BottomUp("apex_vndk_deps", apexVndkDepsMutator).Parallel()
+}
+
+func RegisterPostDepsMutators(ctx android.RegisterMutatorsContext) {
+	ctx.BottomUp("apex_deps", apexDepsMutator)
+	ctx.BottomUp("apex", apexMutator).Parallel()
+	ctx.BottomUp("apex_flattened", apexFlattenedMutator).Parallel()
+	ctx.BottomUp("apex_uses", apexUsesMutator).Parallel()
+}
+
 // Mark the direct and transitive dependencies of apex bundles so that they
 // can be built for the apex bundles.
-func apexDepsMutator(mctx android.TopDownMutatorContext) {
+func apexDepsMutator(mctx android.BottomUpMutatorContext) {
 	if a, ok := mctx.Module().(*apexBundle); ok {
 		apexBundleName := mctx.ModuleName()
 		mctx.WalkDeps(func(child, parent android.Module) bool {
@@ -173,7 +105,8 @@
 				android.UpdateApexDependency(apexBundleName, depName, directDep)
 			}
 
-			if am, ok := child.(android.ApexModule); ok && am.CanHaveApexVariants() {
+			if am, ok := child.(android.ApexModule); ok && am.CanHaveApexVariants() &&
+				(directDep || am.DepIsInSameApex(mctx, child)) {
 				am.BuildForApex(apexBundleName)
 				return true
 			} else {
@@ -192,15 +125,115 @@
 		// apex variant.
 		apexBundleName := mctx.ModuleName()
 		mctx.CreateVariations(apexBundleName)
+	} else if o, ok := mctx.Module().(*OverrideApex); ok {
+		apexBundleName := o.GetOverriddenModuleName()
+		if apexBundleName == "" {
+			mctx.ModuleErrorf("base property is not set")
+			return
+		}
+		mctx.CreateVariations(apexBundleName)
 	}
+
+}
+
+var (
+	apexFileContextsInfosKey   = android.NewOnceKey("apexFileContextsInfosKey")
+	apexFileContextsInfosMutex sync.Mutex
+)
+
+func apexFileContextsInfos(config android.Config) *[]string {
+	return config.Once(apexFileContextsInfosKey, func() interface{} {
+		return &[]string{}
+	}).(*[]string)
+}
+
+func addFlattenedFileContextsInfos(ctx android.BaseModuleContext, fileContextsInfo string) {
+	apexFileContextsInfosMutex.Lock()
+	defer apexFileContextsInfosMutex.Unlock()
+	apexFileContextsInfos := apexFileContextsInfos(ctx.Config())
+	*apexFileContextsInfos = append(*apexFileContextsInfos, fileContextsInfo)
+}
+
+func apexFlattenedMutator(mctx android.BottomUpMutatorContext) {
+	if ab, ok := mctx.Module().(*apexBundle); ok {
+		var variants []string
+		switch proptools.StringDefault(ab.properties.Payload_type, "image") {
+		case "image":
+			variants = append(variants, imageApexType, flattenedApexType)
+		case "zip":
+			variants = append(variants, zipApexType)
+		case "both":
+			variants = append(variants, imageApexType, zipApexType, flattenedApexType)
+		default:
+			mctx.PropertyErrorf("type", "%q is not one of \"image\", \"zip\", or \"both\".", *ab.properties.Payload_type)
+			return
+		}
+
+		modules := mctx.CreateLocalVariations(variants...)
+
+		for i, v := range variants {
+			switch v {
+			case imageApexType:
+				modules[i].(*apexBundle).properties.ApexType = imageApex
+			case zipApexType:
+				modules[i].(*apexBundle).properties.ApexType = zipApex
+			case flattenedApexType:
+				modules[i].(*apexBundle).properties.ApexType = flattenedApex
+				if !mctx.Config().FlattenApex() && ab.Platform() {
+					modules[i].(*apexBundle).MakeAsSystemExt()
+				}
+			}
+		}
+	} else if _, ok := mctx.Module().(*OverrideApex); ok {
+		mctx.CreateVariations(imageApexType, flattenedApexType)
+	}
+}
+
+func apexUsesMutator(mctx android.BottomUpMutatorContext) {
+	if ab, ok := mctx.Module().(*apexBundle); ok {
+		mctx.AddFarVariationDependencies(nil, usesTag, ab.properties.Uses...)
+	}
+}
+
+var (
+	useVendorWhitelistKey = android.NewOnceKey("useVendorWhitelist")
+)
+
+// useVendorWhitelist returns the list of APEXes which are allowed to use_vendor.
+// When use_vendor is used, native modules are built with __ANDROID_VNDK__ and __ANDROID_APEX__,
+// which may cause compatibility issues. (e.g. libbinder)
+// Even though libbinder restricts its availability via 'apex_available' property and relies on
+// yet another macro __ANDROID_APEX_<NAME>__, we restrict usage of "use_vendor:" from other APEX modules
+// to avoid similar problems.
+func useVendorWhitelist(config android.Config) []string {
+	return config.Once(useVendorWhitelistKey, func() interface{} {
+		return []string{
+			// swcodec uses "vendor" variants for smaller size
+			"com.android.media.swcodec",
+			"test_com.android.media.swcodec",
+		}
+	}).([]string)
+}
+
+// setUseVendorWhitelistForTest overrides useVendorWhitelist and must be
+// called before the first call to useVendorWhitelist()
+func setUseVendorWhitelistForTest(config android.Config, whitelist []string) {
+	config.Once(useVendorWhitelistKey, func() interface{} {
+		return whitelist
+	})
 }
 
 type apexNativeDependencies struct {
 	// List of native libraries
 	Native_shared_libs []string
+
 	// List of native executables
 	Binaries []string
+
+	// List of native tests
+	Tests []string
 }
+
 type apexMultilibProperties struct {
 	// Native dependencies whose compile_multilib is "first"
 	First apexNativeDependencies
@@ -227,20 +260,20 @@
 	// If unspecified, a default one is automatically generated.
 	AndroidManifest *string `android:"path"`
 
-	// Canonical name of the APEX bundle in the manifest file.
-	// If unspecified, defaults to the value of name
+	// Canonical name of the APEX bundle. Used to determine the path to the activated APEX on
+	// device (/apex/<apex_name>).
+	// If unspecified, defaults to the value of name.
 	Apex_name *string
 
 	// Determines the file contexts file for setting security context to each file in this APEX bundle.
-	// Specifically, when this is set to <value>, /system/sepolicy/apex/<value>_file_contexts file is
-	// used.
-	// Default: <name_of_this_module>
-	File_contexts *string
+	// For platform APEXes, this should points to a file under /system/sepolicy
+	// Default: /system/sepolicy/apex/<module_name>_file_contexts.
+	File_contexts *string `android:"path"`
 
 	// List of native shared libs that are embedded inside this APEX bundle
 	Native_shared_libs []string
 
-	// List of native executables that are embedded inside this APEX bundle
+	// List of executables that are embedded inside this APEX bundle
 	Binaries []string
 
 	// List of java libraries that are embedded inside this APEX bundle
@@ -249,6 +282,9 @@
 	// List of prebuilt files that are embedded inside this APEX bundle
 	Prebuilts []string
 
+	// List of tests that are embedded inside this APEX bundle
+	Tests []string
+
 	// Name of the apex_key module that provides the private key to sign APEX
 	Key *string
 
@@ -274,6 +310,39 @@
 
 	// List of sanitizer names that this APEX is enabled for
 	SanitizerNames []string `blueprint:"mutated"`
+
+	PreventInstall bool `blueprint:"mutated"`
+
+	HideFromMake bool `blueprint:"mutated"`
+
+	// Indicates this APEX provides C++ shared libaries to other APEXes. Default: false.
+	Provide_cpp_shared_libs *bool
+
+	// List of providing APEXes' names so that this APEX can depend on provided shared libraries.
+	Uses []string
+
+	// A txt file containing list of files that are whitelisted to be included in this APEX.
+	Whitelisted_files *string
+
+	// package format of this apex variant; could be non-flattened, flattened, or zip.
+	// imageApex, zipApex or flattened
+	ApexType apexPackaging `blueprint:"mutated"`
+
+	// List of SDKs that are used to build this APEX. A reference to an SDK should be either
+	// `name#version` or `name` which is an alias for `name#current`. If left empty, `platform#current`
+	// is implied. This value affects all modules included in this APEX. In other words, they are
+	// also built with the SDKs specified here.
+	Uses_sdks []string
+
+	// Whenever apex_payload.img of the APEX should include dm-verity hashtree.
+	// Should be only used in tests#.
+	Test_only_no_hashtree *bool
+
+	// Whether this APEX should support Android10. Default is false. If this is set true, then apex_manifest.json is bundled as well
+	// because Android10 requires legacy apex_manifest.json instead of apex_manifest.pb
+	Legacy_android10_support *bool
+
+	IsCoverageVariant bool `blueprint:"mutated"`
 }
 
 type apexTargetBundleProperties struct {
@@ -282,14 +351,17 @@
 		Android struct {
 			Multilib apexMultilibProperties
 		}
+
 		// Multilib properties only for host.
 		Host struct {
 			Multilib apexMultilibProperties
 		}
+
 		// Multilib properties only for host linux_bionic.
 		Linux_bionic struct {
 			Multilib apexMultilibProperties
 		}
+
 		// Multilib properties only for host linux_glibc.
 		Linux_glibc struct {
 			Multilib apexMultilibProperties
@@ -297,6 +369,49 @@
 	}
 }
 
+type overridableProperties struct {
+	// List of APKs to package inside APEX
+	Apps []string
+
+	// Names of modules to be overridden. Listed modules can only be other binaries
+	// (in Make or Soong).
+	// This does not completely prevent installation of the overridden binaries, but if both
+	// binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will be removed
+	// from PRODUCT_PACKAGES.
+	Overrides []string
+}
+
+type apexPackaging int
+
+const (
+	imageApex apexPackaging = iota
+	zipApex
+	flattenedApex
+)
+
+// The suffix for the output "file", not the module
+func (a apexPackaging) suffix() string {
+	switch a {
+	case imageApex:
+		return imageApexSuffix
+	case zipApex:
+		return zipApexSuffix
+	default:
+		panic(fmt.Errorf("unknown APEX type %d", a))
+	}
+}
+
+func (a apexPackaging) name() string {
+	switch a {
+	case imageApex:
+		return imageApexType
+	case zipApex:
+		return zipApexType
+	default:
+		panic(fmt.Errorf("unknown APEX type %d", a))
+	}
+}
+
 type apexFileClass int
 
 const (
@@ -307,58 +422,10 @@
 	pyBinary
 	goBinary
 	javaSharedLib
+	nativeTest
+	app
 )
 
-type apexPackaging int
-
-const (
-	imageApex apexPackaging = iota
-	zipApex
-	both
-)
-
-func (a apexPackaging) image() bool {
-	switch a {
-	case imageApex, both:
-		return true
-	}
-	return false
-}
-
-func (a apexPackaging) zip() bool {
-	switch a {
-	case zipApex, both:
-		return true
-	}
-	return false
-}
-
-func (a apexPackaging) suffix() string {
-	switch a {
-	case imageApex:
-		return imageApexSuffix
-	case zipApex:
-		return zipApexSuffix
-	case both:
-		panic(fmt.Errorf("must be either zip or image"))
-	default:
-		panic(fmt.Errorf("unkonwn APEX type %d", a))
-	}
-}
-
-func (a apexPackaging) name() string {
-	switch a {
-	case imageApex:
-		return imageApexType
-	case zipApex:
-		return zipApexType
-	case both:
-		panic(fmt.Errorf("must be either zip or image"))
-	default:
-		panic(fmt.Errorf("unkonwn APEX type %d", a))
-	}
-}
-
 func (class apexFileClass) NameInMake() string {
 	switch class {
 	case etc:
@@ -369,32 +436,80 @@
 		return "EXECUTABLES"
 	case javaSharedLib:
 		return "JAVA_LIBRARIES"
+	case nativeTest:
+		return "NATIVE_TESTS"
+	case app:
+		// 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
+		// by the Make build system. For example, it is installed to
+		// /system/apex/<apexname>/app/<Appname>/<apexname>.<Appname>/ instead of
+		// /system/apex/<apexname>/app/<Appname> because the build system automatically
+		// appends module name (which is <apexname>.<Appname> to the path.
+		return "ETC"
 	default:
-		panic(fmt.Errorf("unkonwn class %d", class))
+		panic(fmt.Errorf("unknown class %d", class))
 	}
 }
 
+// apexFile represents a file in an APEX bundle
 type apexFile struct {
 	builtFile  android.Path
 	moduleName string
 	installDir string
 	class      apexFileClass
 	module     android.Module
-	symlinks   []string
+	// list of symlinks that will be created in installDir that point to this apexFile
+	symlinks      []string
+	transitiveDep bool
+	moduleDir     string
+
+	requiredModuleNames       []string
+	targetRequiredModuleNames []string
+	hostRequiredModuleNames   []string
+
+	jacocoReportClassesFile android.Path // only for javalibs and apps
+}
+
+func newApexFile(ctx android.BaseModuleContext, builtFile android.Path, moduleName string, installDir string, class apexFileClass, module android.Module) apexFile {
+	ret := apexFile{
+		builtFile:  builtFile,
+		moduleName: moduleName,
+		installDir: installDir,
+		class:      class,
+		module:     module,
+	}
+	if module != nil {
+		ret.moduleDir = ctx.OtherModuleDir(module)
+		ret.requiredModuleNames = module.RequiredModuleNames()
+		ret.targetRequiredModuleNames = module.TargetRequiredModuleNames()
+		ret.hostRequiredModuleNames = module.HostRequiredModuleNames()
+	}
+	return ret
+}
+
+func (af *apexFile) Ok() bool {
+	return af.builtFile != nil && af.builtFile.String() != ""
 }
 
 type apexBundle struct {
 	android.ModuleBase
 	android.DefaultableModuleBase
+	android.OverridableModuleBase
+	android.SdkBase
 
-	properties       apexBundleProperties
-	targetProperties apexTargetBundleProperties
+	properties            apexBundleProperties
+	targetProperties      apexTargetBundleProperties
+	overridableProperties overridableProperties
 
-	apexTypes apexPackaging
+	// specific to apex_vndk modules
+	vndkProperties apexVndkProperties
 
 	bundleModuleFile android.WritablePath
-	outputFiles      map[apexPackaging]android.WritablePath
-	installDir       android.OutputPath
+	outputFile       android.WritablePath
+	installDir       android.InstallPath
+
+	prebuiltFileToDelete string
 
 	public_key_file  android.Path
 	private_key_file android.Path
@@ -402,36 +517,59 @@
 	container_certificate_file android.Path
 	container_private_key_file android.Path
 
-	mergedNoticeFile android.WritablePath
+	fileContexts android.Path
 
 	// list of files to be included in this apex
 	filesInfo []apexFile
 
-	// list of module names that this APEX is depending on
+	// list of module names that should be installed along with this APEX
+	requiredDeps []string
+
+	// list of module names that this APEX is depending on (to be shown via *-deps-info target)
 	externalDeps []string
+	// list of module names that this APEX is including (to be shown via *-deps-info target)
+	internalDeps []string
 
-	flattened bool
+	testApex        bool
+	vndkApex        bool
+	artApex         bool
+	primaryApexType bool
 
-	testApex bool
+	manifestJsonOut android.WritablePath
+	manifestPbOut   android.WritablePath
+
+	// list of commands to create symlinks for backward compatibility.
+	// these commands will be attached as LOCAL_POST_INSTALL_CMD to
+	// apex package itself(for unflattened build) or apex_manifest(for flattened build)
+	// so that compat symlinks are always installed regardless of TARGET_FLATTEN_APEX setting.
+	compatSymlinks []string
+
+	// Suffix of module name in Android.mk
+	// ".flattened", ".apex", ".zipapex", or ""
+	suffix string
 }
 
 func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext,
-	native_shared_libs []string, binaries []string, arch string, imageVariation string) {
+	native_shared_libs []string, binaries []string, tests []string,
+	target android.Target, imageVariation string) {
 	// Use *FarVariation* to be able to depend on modules having
 	// conflicting variations with this module. This is required since
 	// arch variant of an APEX bundle is 'common' but it is 'arm' or 'arm64'
 	// for native shared libs.
-	ctx.AddFarVariationDependencies([]blueprint.Variation{
-		{Mutator: "arch", Variation: arch},
+	ctx.AddFarVariationDependencies(append(target.Variations(), []blueprint.Variation{
 		{Mutator: "image", Variation: imageVariation},
 		{Mutator: "link", Variation: "shared"},
 		{Mutator: "version", Variation: ""}, // "" is the non-stub variant
-	}, sharedLibTag, native_shared_libs...)
+	}...), sharedLibTag, native_shared_libs...)
 
-	ctx.AddFarVariationDependencies([]blueprint.Variation{
-		{Mutator: "arch", Variation: arch},
+	ctx.AddFarVariationDependencies(append(target.Variations(),
+		blueprint.Variation{Mutator: "image", Variation: imageVariation}),
+		executableTag, binaries...)
+
+	ctx.AddFarVariationDependencies(append(target.Variations(), []blueprint.Variation{
 		{Mutator: "image", Variation: imageVariation},
-	}, executableTag, binaries...)
+		{Mutator: "test_per_src", Variation: ""}, // "" is the all-tests variant
+	}...), testTag, tests...)
 }
 
 func (a *apexBundle) combineProperties(ctx android.BottomUpMutatorContext) {
@@ -448,6 +586,9 @@
 }
 
 func (a *apexBundle) DepsMutator(ctx android.BottomUpMutatorContext) {
+	if proptools.Bool(a.properties.Use_vendor) && !android.InList(a.Name(), useVendorWhitelist(ctx.Config())) {
+		ctx.PropertyErrorf("use_vendor", "not allowed to set use_vendor: true")
+	}
 
 	targets := ctx.MultiTargets()
 	config := ctx.DeviceConfig()
@@ -463,37 +604,41 @@
 	for i, target := range targets {
 		// When multilib.* is omitted for native_shared_libs, it implies
 		// multilib.both.
-		ctx.AddFarVariationDependencies([]blueprint.Variation{
-			{Mutator: "arch", Variation: target.String()},
+		ctx.AddFarVariationDependencies(append(target.Variations(), []blueprint.Variation{
 			{Mutator: "image", Variation: a.getImageVariation(config)},
 			{Mutator: "link", Variation: "shared"},
-		}, sharedLibTag, a.properties.Native_shared_libs...)
+		}...), sharedLibTag, a.properties.Native_shared_libs...)
+
+		// When multilib.* is omitted for tests, it implies
+		// multilib.both.
+		ctx.AddFarVariationDependencies(append(target.Variations(), []blueprint.Variation{
+			{Mutator: "image", Variation: a.getImageVariation(config)},
+			{Mutator: "test_per_src", Variation: ""}, // "" is the all-tests variant
+		}...), testTag, a.properties.Tests...)
 
 		// Add native modules targetting both ABIs
 		addDependenciesForNativeModules(ctx,
 			a.properties.Multilib.Both.Native_shared_libs,
-			a.properties.Multilib.Both.Binaries, target.String(),
+			a.properties.Multilib.Both.Binaries,
+			a.properties.Multilib.Both.Tests,
+			target,
 			a.getImageVariation(config))
 
 		isPrimaryAbi := i == 0
 		if isPrimaryAbi {
 			// When multilib.* is omitted for binaries, it implies
 			// multilib.first.
-			ctx.AddFarVariationDependencies([]blueprint.Variation{
-				{Mutator: "arch", Variation: target.String()},
-				{Mutator: "image", Variation: a.getImageVariation(config)},
-			}, executableTag, a.properties.Binaries...)
+			ctx.AddFarVariationDependencies(append(target.Variations(),
+				blueprint.Variation{Mutator: "image", Variation: a.getImageVariation(config)}),
+				executableTag, a.properties.Binaries...)
 
 			// Add native modules targetting the first ABI
 			addDependenciesForNativeModules(ctx,
 				a.properties.Multilib.First.Native_shared_libs,
-				a.properties.Multilib.First.Binaries, target.String(),
+				a.properties.Multilib.First.Binaries,
+				a.properties.Multilib.First.Tests,
+				target,
 				a.getImageVariation(config))
-
-			// When multilib.* is omitted for prebuilts, it implies multilib.first.
-			ctx.AddFarVariationDependencies([]blueprint.Variation{
-				{Mutator: "arch", Variation: target.String()},
-			}, prebuiltTag, a.properties.Prebuilts...)
 		}
 
 		switch target.Arch.ArchType.Multilib {
@@ -501,24 +646,32 @@
 			// Add native modules targetting 32-bit ABI
 			addDependenciesForNativeModules(ctx,
 				a.properties.Multilib.Lib32.Native_shared_libs,
-				a.properties.Multilib.Lib32.Binaries, target.String(),
+				a.properties.Multilib.Lib32.Binaries,
+				a.properties.Multilib.Lib32.Tests,
+				target,
 				a.getImageVariation(config))
 
 			addDependenciesForNativeModules(ctx,
 				a.properties.Multilib.Prefer32.Native_shared_libs,
-				a.properties.Multilib.Prefer32.Binaries, target.String(),
+				a.properties.Multilib.Prefer32.Binaries,
+				a.properties.Multilib.Prefer32.Tests,
+				target,
 				a.getImageVariation(config))
 		case "lib64":
 			// Add native modules targetting 64-bit ABI
 			addDependenciesForNativeModules(ctx,
 				a.properties.Multilib.Lib64.Native_shared_libs,
-				a.properties.Multilib.Lib64.Binaries, target.String(),
+				a.properties.Multilib.Lib64.Binaries,
+				a.properties.Multilib.Lib64.Tests,
+				target,
 				a.getImageVariation(config))
 
 			if !has32BitTarget {
 				addDependenciesForNativeModules(ctx,
 					a.properties.Multilib.Prefer32.Native_shared_libs,
-					a.properties.Multilib.Prefer32.Binaries, target.String(),
+					a.properties.Multilib.Prefer32.Binaries,
+					a.properties.Multilib.Prefer32.Tests,
+					target,
 					a.getImageVariation(config))
 			}
 
@@ -527,7 +680,7 @@
 					if sanitizer == "hwaddress" {
 						addDependenciesForNativeModules(ctx,
 							[]string{"libclang_rt.hwasan-aarch64-android"},
-							nil, target.String(), a.getImageVariation(config))
+							nil, nil, target, a.getImageVariation(config))
 						break
 					}
 				}
@@ -536,9 +689,30 @@
 
 	}
 
+	// For prebuilt_etc, use the first variant (64 on 64/32bit device,
+	// 32 on 32bit device) regardless of the TARGET_PREFER_* setting.
+	// b/144532908
+	archForPrebuiltEtc := config.Arches()[0]
+	for _, arch := range config.Arches() {
+		// Prefer 64-bit arch if there is any
+		if arch.ArchType.Multilib == "lib64" {
+			archForPrebuiltEtc = arch
+			break
+		}
+	}
 	ctx.AddFarVariationDependencies([]blueprint.Variation{
-		{Mutator: "arch", Variation: "android_common"},
-	}, javaLibTag, a.properties.Java_libs...)
+		{Mutator: "os", Variation: ctx.Os().String()},
+		{Mutator: "arch", Variation: archForPrebuiltEtc.String()},
+	}, prebuiltTag, a.properties.Prebuilts...)
+
+	ctx.AddFarVariationDependencies(ctx.Config().AndroidCommonTarget.Variations(),
+		javaLibTag, a.properties.Java_libs...)
+
+	// With EMMA_INSTRUMENT_FRAMEWORK=true the ART boot image includes jacoco library.
+	if a.artApex && ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") {
+		ctx.AddFarVariationDependencies(ctx.Config().AndroidCommonTarget.Variations(),
+			javaLibTag, "jacocoagent")
+	}
 
 	if String(a.properties.Key) == "" {
 		ctx.ModuleErrorf("key is missing")
@@ -550,10 +724,36 @@
 	if cert != "" {
 		ctx.AddDependency(ctx.Module(), certificateTag, cert)
 	}
+
+	// TODO(jiyong): ensure that all apexes are with non-empty uses_sdks
+	if len(a.properties.Uses_sdks) > 0 {
+		sdkRefs := []android.SdkRef{}
+		for _, str := range a.properties.Uses_sdks {
+			parsed := android.ParseSdkRef(ctx, str, "uses_sdks")
+			sdkRefs = append(sdkRefs, parsed)
+		}
+		a.BuildWithSdks(sdkRefs)
+	}
+}
+
+func (a *apexBundle) OverridablePropertiesDepsMutator(ctx android.BottomUpMutatorContext) {
+	ctx.AddFarVariationDependencies(ctx.Config().AndroidCommonTarget.Variations(),
+		androidAppTag, a.overridableProperties.Apps...)
+}
+
+func (a *apexBundle) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
+	// direct deps of an APEX bundle are all part of the APEX bundle
+	return true
 }
 
 func (a *apexBundle) getCertString(ctx android.BaseModuleContext) string {
-	certificate, overridden := ctx.DeviceConfig().OverrideCertificateFor(ctx.ModuleName())
+	moduleName := ctx.ModuleName()
+	// VNDK APEXes share the same certificate. To avoid adding a new VNDK version to the OVERRIDE_* list,
+	// we check with the pseudo module name to see if its certificate is overridden.
+	if a.vndkApex {
+		moduleName = vndkApexName
+	}
+	certificate, overridden := ctx.DeviceConfig().OverrideCertificateFor(moduleName)
 	if overridden {
 		return ":" + certificate
 	}
@@ -563,25 +763,28 @@
 func (a *apexBundle) OutputFiles(tag string) (android.Paths, error) {
 	switch tag {
 	case "":
-		if file, ok := a.outputFiles[imageApex]; ok {
-			return android.Paths{file}, nil
-		} else {
-			return nil, nil
-		}
+		return android.Paths{a.outputFile}, nil
 	default:
 		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
 	}
 }
 
 func (a *apexBundle) installable() bool {
-	return a.properties.Installable == nil || proptools.Bool(a.properties.Installable)
+	return !a.properties.PreventInstall && (a.properties.Installable == nil || proptools.Bool(a.properties.Installable))
+}
+
+func (a *apexBundle) testOnlyShouldSkipHashtreeGeneration() bool {
+	return proptools.Bool(a.properties.Test_only_no_hashtree)
 }
 
 func (a *apexBundle) getImageVariation(config android.DeviceConfig) string {
+	if a.vndkApex {
+		return cc.VendorVariationPrefix + a.vndkVersion(config)
+	}
 	if config.VndkVersion() != "" && proptools.Bool(a.properties.Use_vendor) {
-		return "vendor"
+		return cc.VendorVariationPrefix + config.PlatformVndkVersion()
 	} else {
-		return "core"
+		return android.CoreVariation
 	}
 }
 
@@ -609,200 +812,396 @@
 	return android.InList(sanitizerName, globalSanitizerNames)
 }
 
-func getCopyManifestForNativeLibrary(cc *cc.Module, handleSpecialLibs bool) (fileToCopy android.Path, dirInApex string) {
+func (a *apexBundle) IsNativeCoverageNeeded(ctx android.BaseModuleContext) bool {
+	return ctx.Device() && ctx.DeviceConfig().NativeCoverageEnabled()
+}
+
+func (a *apexBundle) PreventInstall() {
+	a.properties.PreventInstall = true
+}
+
+func (a *apexBundle) HideFromMake() {
+	a.properties.HideFromMake = true
+}
+
+func (a *apexBundle) MarkAsCoverageVariant(coverage bool) {
+	a.properties.IsCoverageVariant = coverage
+}
+
+// TODO(jiyong) move apexFileFor* close to the apexFile type definition
+func apexFileForNativeLibrary(ctx android.BaseModuleContext, ccMod *cc.Module, handleSpecialLibs bool) apexFile {
 	// Decide the APEX-local directory by the multilib of the library
 	// In the future, we may query this to the module.
-	switch cc.Arch().ArchType.Multilib {
+	var dirInApex string
+	switch ccMod.Arch().ArchType.Multilib {
 	case "lib32":
 		dirInApex = "lib"
 	case "lib64":
 		dirInApex = "lib64"
 	}
-	dirInApex = filepath.Join(dirInApex, cc.RelativeInstallPath())
-	if !cc.Arch().Native {
-		dirInApex = filepath.Join(dirInApex, cc.Arch().ArchType.String())
+	dirInApex = filepath.Join(dirInApex, ccMod.RelativeInstallPath())
+	if ccMod.Target().NativeBridge == android.NativeBridgeEnabled {
+		dirInApex = filepath.Join(dirInApex, ccMod.Target().NativeBridgeRelativePath)
 	}
-	if handleSpecialLibs {
-		switch cc.Name() {
-		case "libc", "libm", "libdl":
-			// Special case for bionic libs. This is to prevent the bionic libs
-			// from being included in the search path /apex/com.android.apex/lib.
-			// This exclusion is required because bionic libs in the runtime APEX
-			// are available via the legacy paths /system/lib/libc.so, etc. By the
-			// init process, the bionic libs in the APEX are bind-mounted to the
-			// legacy paths and thus will be loaded into the default linker namespace.
-			// If the bionic libs are directly in /apex/com.android.apex/lib then
-			// the same libs will be again loaded to the runtime linker namespace,
-			// which will result double loading of bionic libs that isn't supported.
-			dirInApex = filepath.Join(dirInApex, "bionic")
-		}
+	if handleSpecialLibs && cc.InstallToBootstrap(ccMod.BaseModuleName(), ctx.Config()) {
+		// Special case for Bionic libs and other libs installed with them. This is
+		// to prevent those libs from being included in the search path
+		// /apex/com.android.runtime/${LIB}. This exclusion is required because
+		// those libs in the Runtime APEX are available via the legacy paths in
+		// /system/lib/. By the init process, the libs in the APEX are bind-mounted
+		// to the legacy paths and thus will be loaded into the default linker
+		// namespace (aka "platform" namespace). If the libs are directly in
+		// /apex/com.android.runtime/${LIB} then the same libs will be loaded again
+		// into the runtime linker namespace, which will result in double loading of
+		// them, which isn't supported.
+		dirInApex = filepath.Join(dirInApex, "bionic")
 	}
 
-	fileToCopy = cc.OutputFile().Path()
-	return
+	fileToCopy := ccMod.OutputFile().Path()
+	return newApexFile(ctx, fileToCopy, ccMod.Name(), dirInApex, nativeSharedLib, ccMod)
 }
 
-func getCopyManifestForExecutable(cc *cc.Module) (fileToCopy android.Path, dirInApex string) {
-	dirInApex = filepath.Join("bin", cc.RelativeInstallPath())
-	fileToCopy = cc.OutputFile().Path()
-	return
+func apexFileForExecutable(ctx android.BaseModuleContext, cc *cc.Module) apexFile {
+	dirInApex := filepath.Join("bin", cc.RelativeInstallPath())
+	if cc.Target().NativeBridge == android.NativeBridgeEnabled {
+		dirInApex = filepath.Join(dirInApex, cc.Target().NativeBridgeRelativePath)
+	}
+	fileToCopy := cc.OutputFile().Path()
+	af := newApexFile(ctx, fileToCopy, cc.Name(), dirInApex, nativeExecutable, cc)
+	af.symlinks = cc.Symlinks()
+	return af
 }
 
-func getCopyManifestForPyBinary(py *python.Module) (fileToCopy android.Path, dirInApex string) {
-	dirInApex = "bin"
-	fileToCopy = py.HostToolPath().Path()
-	return
+func apexFileForPyBinary(ctx android.BaseModuleContext, py *python.Module) apexFile {
+	dirInApex := "bin"
+	fileToCopy := py.HostToolPath().Path()
+	return newApexFile(ctx, fileToCopy, py.Name(), dirInApex, pyBinary, py)
 }
-func getCopyManifestForGoBinary(ctx android.ModuleContext, gb bootstrap.GoBinaryTool) (fileToCopy android.Path, dirInApex string) {
-	dirInApex = "bin"
+func apexFileForGoBinary(ctx android.BaseModuleContext, depName string, gb bootstrap.GoBinaryTool) apexFile {
+	dirInApex := "bin"
 	s, err := filepath.Rel(android.PathForOutput(ctx).String(), gb.InstallPath())
 	if err != nil {
 		ctx.ModuleErrorf("Unable to use compiled binary at %s", gb.InstallPath())
-		return
+		return apexFile{}
 	}
-	fileToCopy = android.PathForOutput(ctx, s)
-	return
+	fileToCopy := android.PathForOutput(ctx, s)
+	// NB: Since go binaries are static we don't need the module for anything here, which is
+	// good since the go tool is a blueprint.Module not an android.Module like we would
+	// normally use.
+	return newApexFile(ctx, fileToCopy, depName, dirInApex, goBinary, nil)
 }
 
-func getCopyManifestForShBinary(sh *android.ShBinary) (fileToCopy android.Path, dirInApex string) {
-	dirInApex = filepath.Join("bin", sh.SubDir())
-	fileToCopy = sh.OutputFile()
-	return
+func apexFileForShBinary(ctx android.BaseModuleContext, sh *android.ShBinary) apexFile {
+	dirInApex := filepath.Join("bin", sh.SubDir())
+	fileToCopy := sh.OutputFile()
+	af := newApexFile(ctx, fileToCopy, sh.Name(), dirInApex, shBinary, sh)
+	af.symlinks = sh.Symlinks()
+	return af
 }
 
-func getCopyManifestForJavaLibrary(java *java.Library) (fileToCopy android.Path, dirInApex string) {
-	dirInApex = "javalib"
-	fileToCopy = java.DexJarFile()
-	return
+// TODO(b/146586360): replace javaLibrary(in apex/apex.go) with java.Dependency
+type javaLibrary interface {
+	android.Module
+	java.Dependency
 }
 
-func getCopyManifestForPrebuiltEtc(prebuilt *android.PrebuiltEtc) (fileToCopy android.Path, dirInApex string) {
-	dirInApex = filepath.Join("etc", prebuilt.SubDir())
-	fileToCopy = prebuilt.OutputFile()
-	return
+func apexFileForJavaLibrary(ctx android.BaseModuleContext, lib javaLibrary) apexFile {
+	dirInApex := "javalib"
+	fileToCopy := lib.DexJar()
+	af := newApexFile(ctx, fileToCopy, lib.Name(), dirInApex, javaSharedLib, lib)
+	af.jacocoReportClassesFile = lib.JacocoReportClassesFile()
+	return af
+}
+
+func apexFileForPrebuiltEtc(ctx android.BaseModuleContext, prebuilt android.PrebuiltEtcModule, depName string) apexFile {
+	dirInApex := filepath.Join("etc", prebuilt.SubDir())
+	fileToCopy := prebuilt.OutputFile()
+	return newApexFile(ctx, fileToCopy, depName, dirInApex, etc, prebuilt)
+}
+
+func apexFileForAndroidApp(ctx android.BaseModuleContext, aapp interface {
+	android.Module
+	Privileged() bool
+	OutputFile() android.Path
+	JacocoReportClassesFile() android.Path
+}, pkgName string) apexFile {
+	appDir := "app"
+	if aapp.Privileged() {
+		appDir = "priv-app"
+	}
+	dirInApex := filepath.Join(appDir, pkgName)
+	fileToCopy := aapp.OutputFile()
+	af := newApexFile(ctx, fileToCopy, aapp.Name(), dirInApex, app, aapp)
+	af.jacocoReportClassesFile = aapp.JacocoReportClassesFile()
+	return af
+}
+
+// Context "decorator", overriding the InstallBypassMake method to always reply `true`.
+type flattenedApexContext struct {
+	android.ModuleContext
+}
+
+func (c *flattenedApexContext) InstallBypassMake() bool {
+	return true
 }
 
 func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	filesInfo := []apexFile{}
+	buildFlattenedAsDefault := ctx.Config().FlattenApex() && !ctx.Config().UnbundledBuild()
+	switch a.properties.ApexType {
+	case imageApex:
+		if buildFlattenedAsDefault {
+			a.suffix = imageApexSuffix
+		} else {
+			a.suffix = ""
+			a.primaryApexType = true
 
-	if a.properties.Payload_type == nil || *a.properties.Payload_type == "image" {
-		a.apexTypes = imageApex
-	} else if *a.properties.Payload_type == "zip" {
-		a.apexTypes = zipApex
-	} else if *a.properties.Payload_type == "both" {
-		a.apexTypes = both
-	} else {
-		ctx.PropertyErrorf("type", "%q is not one of \"image\", \"zip\", or \"both\".", *a.properties.Payload_type)
+			if ctx.Config().InstallExtraFlattenedApexes() {
+				a.requiredDeps = append(a.requiredDeps, a.Name()+flattenedSuffix)
+			}
+		}
+	case zipApex:
+		if proptools.String(a.properties.Payload_type) == "zip" {
+			a.suffix = ""
+			a.primaryApexType = true
+		} else {
+			a.suffix = zipApexSuffix
+		}
+	case flattenedApex:
+		if buildFlattenedAsDefault {
+			a.suffix = ""
+			a.primaryApexType = true
+		} else {
+			a.suffix = flattenedSuffix
+		}
+	}
+
+	if len(a.properties.Tests) > 0 && !a.testApex {
+		ctx.PropertyErrorf("tests", "property not allowed in apex module type")
 		return
 	}
 
 	handleSpecialLibs := !android.Bool(a.properties.Ignore_system_library_special_case)
 
+	// native lib dependencies
+	var provideNativeLibs []string
+	var requireNativeLibs []string
+
+	// Check if "uses" requirements are met with dependent apexBundles
+	var providedNativeSharedLibs []string
+	useVendor := proptools.Bool(a.properties.Use_vendor)
+	ctx.VisitDirectDepsBlueprint(func(m blueprint.Module) {
+		if ctx.OtherModuleDependencyTag(m) != usesTag {
+			return
+		}
+		otherName := ctx.OtherModuleName(m)
+		other, ok := m.(*apexBundle)
+		if !ok {
+			ctx.PropertyErrorf("uses", "%q is not a provider", otherName)
+			return
+		}
+		if proptools.Bool(other.properties.Use_vendor) != useVendor {
+			ctx.PropertyErrorf("use_vendor", "%q has different value of use_vendor", otherName)
+			return
+		}
+		if !proptools.Bool(other.properties.Provide_cpp_shared_libs) {
+			ctx.PropertyErrorf("uses", "%q does not provide native_shared_libs", otherName)
+			return
+		}
+		providedNativeSharedLibs = append(providedNativeSharedLibs, other.properties.Native_shared_libs...)
+	})
+
+	var filesInfo []apexFile
 	ctx.WalkDepsBlueprint(func(child, parent blueprint.Module) bool {
-		if _, ok := parent.(*apexBundle); ok {
-			// direct dependencies
-			depTag := ctx.OtherModuleDependencyTag(child)
-			depName := ctx.OtherModuleName(child)
+		depTag := ctx.OtherModuleDependencyTag(child)
+		depName := ctx.OtherModuleName(child)
+		if _, isDirectDep := parent.(*apexBundle); isDirectDep {
+			if depTag != keyTag && depTag != certificateTag {
+				a.internalDeps = append(a.internalDeps, depName)
+			}
 			switch depTag {
 			case sharedLibTag:
 				if cc, ok := child.(*cc.Module); ok {
-					fileToCopy, dirInApex := getCopyManifestForNativeLibrary(cc, handleSpecialLibs)
-					filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, nativeSharedLib, cc, nil})
-					return true
+					if cc.HasStubsVariants() {
+						provideNativeLibs = append(provideNativeLibs, cc.OutputFile().Path().Base())
+					}
+					filesInfo = append(filesInfo, apexFileForNativeLibrary(ctx, cc, handleSpecialLibs))
+					return true // track transitive dependencies
 				} else {
 					ctx.PropertyErrorf("native_shared_libs", "%q is not a cc_library or cc_library_shared module", depName)
 				}
 			case executableTag:
 				if cc, ok := child.(*cc.Module); ok {
-					if !cc.Arch().Native {
-						// There is only one 'bin' directory so we shouldn't bother copying in
-						// native-bridge'd binaries and only use main ones.
-						return true
-					}
-					fileToCopy, dirInApex := getCopyManifestForExecutable(cc)
-					filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, nativeExecutable, cc, cc.Symlinks()})
-					return true
+					filesInfo = append(filesInfo, apexFileForExecutable(ctx, cc))
+					return true // track transitive dependencies
 				} else if sh, ok := child.(*android.ShBinary); ok {
-					fileToCopy, dirInApex := getCopyManifestForShBinary(sh)
-					filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, shBinary, sh, nil})
+					filesInfo = append(filesInfo, apexFileForShBinary(ctx, sh))
 				} else if py, ok := child.(*python.Module); ok && py.HostToolPath().Valid() {
-					fileToCopy, dirInApex := getCopyManifestForPyBinary(py)
-					filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, pyBinary, py, nil})
+					filesInfo = append(filesInfo, apexFileForPyBinary(ctx, py))
 				} else if gb, ok := child.(bootstrap.GoBinaryTool); ok && a.Host() {
-					fileToCopy, dirInApex := getCopyManifestForGoBinary(ctx, gb)
-					// NB: Since go binaries are static we don't need the module for anything here, which is
-					// good since the go tool is a blueprint.Module not an android.Module like we would
-					// normally use.
-					filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, goBinary, nil, nil})
+					filesInfo = append(filesInfo, apexFileForGoBinary(ctx, depName, gb))
 				} else {
 					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 java, ok := child.(*java.Library); ok {
-					fileToCopy, dirInApex := getCopyManifestForJavaLibrary(java)
-					if fileToCopy == nil {
+				if javaLib, ok := child.(*java.Library); ok {
+					af := apexFileForJavaLibrary(ctx, javaLib)
+					if !af.Ok() {
 						ctx.PropertyErrorf("java_libs", "%q is not configured to be compiled into dex", depName)
 					} else {
-						filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, javaSharedLib, java, nil})
+						filesInfo = append(filesInfo, af)
+						return true // track transitive dependencies
 					}
-					return true
+				} else if sdkLib, ok := child.(*java.SdkLibrary); ok {
+					af := apexFileForJavaLibrary(ctx, sdkLib)
+					if !af.Ok() {
+						ctx.PropertyErrorf("java_libs", "%q is not configured to be compiled into dex", depName)
+						return false
+					}
+					filesInfo = append(filesInfo, af)
+
+					pf, _ := sdkLib.OutputFiles(".xml")
+					if len(pf) != 1 {
+						ctx.PropertyErrorf("java_libs", "%q failed to generate permission XML", depName)
+						return false
+					}
+					filesInfo = append(filesInfo, newApexFile(ctx, pf[0], pf[0].Base(), "etc/permissions", etc, nil))
+					return true // track transitive dependencies
 				} else {
-					ctx.PropertyErrorf("java_libs", "%q is not a java_library module", depName)
+					ctx.PropertyErrorf("java_libs", "%q of type %q is not supported", depName, ctx.OtherModuleType(child))
+				}
+			case androidAppTag:
+				pkgName := ctx.DeviceConfig().OverridePackageNameFor(depName)
+				if ap, ok := child.(*java.AndroidApp); ok {
+					filesInfo = append(filesInfo, apexFileForAndroidApp(ctx, ap, pkgName))
+					return true // track transitive dependencies
+				} else if ap, ok := child.(*java.AndroidAppImport); ok {
+					filesInfo = append(filesInfo, apexFileForAndroidApp(ctx, ap, pkgName))
+				} else if ap, ok := child.(*java.AndroidTestHelperApp); ok {
+					filesInfo = append(filesInfo, apexFileForAndroidApp(ctx, ap, pkgName))
+				} else {
+					ctx.PropertyErrorf("apps", "%q is not an android_app module", depName)
 				}
 			case prebuiltTag:
-				if prebuilt, ok := child.(*android.PrebuiltEtc); ok {
-					fileToCopy, dirInApex := getCopyManifestForPrebuiltEtc(prebuilt)
-					filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, etc, prebuilt, nil})
-					return true
+				if prebuilt, ok := child.(android.PrebuiltEtcModule); ok {
+					filesInfo = append(filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, depName))
 				} else {
 					ctx.PropertyErrorf("prebuilts", "%q is not a prebuilt_etc module", depName)
 				}
+			case testTag:
+				if ccTest, ok := child.(*cc.Module); ok {
+					if ccTest.IsTestPerSrcAllTestsVariation() {
+						// Multiple-output test module (where `test_per_src: true`).
+						//
+						// `ccTest` is the "" ("all tests") variation of a `test_per_src` module.
+						// We do not add this variation to `filesInfo`, as it has no output;
+						// however, we do add the other variations of this module as indirect
+						// dependencies (see below).
+						return true
+					} else {
+						// Single-output test module (where `test_per_src: false`).
+						af := apexFileForExecutable(ctx, ccTest)
+						af.class = nativeTest
+						filesInfo = append(filesInfo, af)
+					}
+				} else {
+					ctx.PropertyErrorf("tests", "%q is not a cc module", depName)
+				}
 			case keyTag:
 				if key, ok := child.(*apexKey); ok {
 					a.private_key_file = key.private_key_file
 					a.public_key_file = key.public_key_file
-					return false
 				} else {
 					ctx.PropertyErrorf("key", "%q is not an apex_key module", depName)
 				}
+				return false
 			case certificateTag:
 				if dep, ok := child.(*java.AndroidAppCertificate); ok {
 					a.container_certificate_file = dep.Certificate.Pem
 					a.container_private_key_file = dep.Certificate.Key
-					return false
 				} else {
 					ctx.ModuleErrorf("certificate dependency %q must be an android_app_certificate module", depName)
 				}
+			case android.PrebuiltDepTag:
+				// If the prebuilt is force disabled, remember to delete the prebuilt file
+				// that might have been installed in the previous builds
+				if prebuilt, ok := child.(*Prebuilt); ok && prebuilt.isForceDisabled() {
+					a.prebuiltFileToDelete = prebuilt.InstallFilename()
+				}
 			}
-		} else {
+		} else if !a.vndkApex {
 			// indirect dependencies
-			if am, ok := child.(android.ApexModule); ok && am.CanHaveApexVariants() && am.IsInstallableToApex() {
-				if cc, ok := child.(*cc.Module); ok {
-					if !a.Host() && (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.
-						// In case no APEX is having the lib, the lib is installed to the system
-						// partition.
-						//
-						// Always include if we are a host-apex however since those won't have any
-						// system libraries.
-						if !android.DirectlyInAnyApex(ctx, cc.Name()) && !android.InList(cc.Name(), a.externalDeps) {
-							a.externalDeps = append(a.externalDeps, cc.Name())
+			if am, ok := child.(android.ApexModule); ok {
+				// We cannot use a switch statement on `depTag` here as the checked
+				// tags used below are private (e.g. `cc.sharedDepTag`).
+				if cc.IsSharedDepTag(depTag) || cc.IsRuntimeDepTag(depTag) {
+					if cc, ok := child.(*cc.Module); ok {
+						if android.InList(cc.Name(), providedNativeSharedLibs) {
+							// If we're using a shared library which is provided from other APEX,
+							// don't include it in this APEX
+							return false
 						}
-						// Don't track further
-						return false
+						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.
+							// In case no APEX is having the lib, the lib is installed to the system
+							// partition.
+							//
+							// Always include if we are a host-apex however since those won't have any
+							// system libraries.
+							if !android.DirectlyInAnyApex(ctx, cc.Name()) && !android.InList(cc.Name(), a.requiredDeps) {
+								a.requiredDeps = append(a.requiredDeps, cc.Name())
+							}
+							a.externalDeps = append(a.externalDeps, depName)
+							requireNativeLibs = append(requireNativeLibs, cc.OutputFile().Path().Base())
+							// Don't track further
+							return false
+						}
+						af := apexFileForNativeLibrary(ctx, cc, handleSpecialLibs)
+						af.transitiveDep = true
+						filesInfo = append(filesInfo, af)
+						a.internalDeps = append(a.internalDeps, depName)
+						a.internalDeps = append(a.internalDeps, cc.AllStaticDeps()...)
+						return true // track transitive dependencies
 					}
-					depName := ctx.OtherModuleName(child)
-					fileToCopy, dirInApex := getCopyManifestForNativeLibrary(cc, handleSpecialLibs)
-					filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, nativeSharedLib, cc, nil})
+				} else if cc.IsTestPerSrcDepTag(depTag) {
+					if cc, ok := child.(*cc.Module); ok {
+						af := apexFileForExecutable(ctx, cc)
+						// Handle modules created as `test_per_src` variations of a single test module:
+						// use the name of the generated test binary (`fileToCopy`) instead of the name
+						// of the original test module (`depName`, shared by all `test_per_src`
+						// variations of that module).
+						af.moduleName = filepath.Base(af.builtFile.String())
+						af.transitiveDep = true
+						filesInfo = append(filesInfo, af)
+						return true // track transitive dependencies
+					}
+				} else if java.IsJniDepTag(depTag) {
+					a.externalDeps = append(a.externalDeps, depName)
 					return true
+				} else if java.IsStaticLibDepTag(depTag) {
+					a.internalDeps = append(a.internalDeps, depName)
+				} else if am.CanHaveApexVariants() && am.IsInstallableToApex() {
+					ctx.ModuleErrorf("unexpected tag %q for indirect dependency %q", depTag, depName)
 				}
 			}
 		}
 		return false
 	})
 
-	a.flattened = ctx.Config().FlattenApex() && !ctx.Config().UnbundledBuild()
+	// Specific to the ART apex: dexpreopt artifacts for libcore Java libraries.
+	// Build rules are generated by the dexpreopt singleton, and here we access build artifacts
+	// via the global boot image config.
+	if a.artApex {
+		for arch, files := range java.DexpreoptedArtApexJars(ctx) {
+			dirInApex := filepath.Join("javalib", arch.String())
+			for _, f := range files {
+				localModule := "javalib_" + arch.String() + "_" + filepath.Base(f.String())
+				af := newApexFile(ctx, f, localModule, dirInApex, etc, nil)
+				filesInfo = append(filesInfo, af)
+			}
+		}
+	}
+
 	if a.private_key_file == nil {
 		ctx.PropertyErrorf("key", "private_key for %q could not be found", String(a.properties.Key))
 		return
@@ -810,11 +1209,12 @@
 
 	// remove duplicates in filesInfo
 	removeDup := func(filesInfo []apexFile) []apexFile {
-		encountered := make(map[android.Path]bool)
+		encountered := make(map[string]bool)
 		result := []apexFile{}
 		for _, f := range filesInfo {
-			if !encountered[f.builtFile] {
-				encountered[f.builtFile] = true
+			dest := filepath.Join(f.installDir, f.builtFile.Base())
+			if !encountered[dest] {
+				encountered[dest] = true
 				result = append(result, f)
 			}
 		}
@@ -827,470 +1227,91 @@
 		return filesInfo[i].builtFile.String() < filesInfo[j].builtFile.String()
 	})
 
+	// check apex_available requirements
+	if !ctx.Host() && !a.testApex {
+		for _, fi := range filesInfo {
+			if am, ok := fi.module.(android.ApexModule); ok {
+				if !am.AvailableFor(ctx.ModuleName()) {
+					ctx.ModuleErrorf("requires %q that is not available for the APEX", fi.module.Name())
+					// don't stop so that we can report other violations in the same run
+				}
+			}
+		}
+	}
+
 	// prepend the name of this APEX to the module names. These names will be the names of
 	// modules that will be defined if the APEX is flattened.
 	for i := range filesInfo {
-		filesInfo[i].moduleName = ctx.ModuleName() + "." + filesInfo[i].moduleName
+		filesInfo[i].moduleName = filesInfo[i].moduleName + "." + a.Name() + a.suffix
 	}
 
 	a.installDir = android.PathForModuleInstall(ctx, "apex")
 	a.filesInfo = filesInfo
 
-	a.buildNoticeFile(ctx)
-
-	if a.apexTypes.zip() {
-		a.buildUnflattenedApex(ctx, zipApex)
-	}
-	if a.apexTypes.image() {
-		// Build rule for unflattened APEX is created even when ctx.Config().FlattenApex()
-		// is true. This is to support referencing APEX via ":<module_name" syntax
-		// in other modules. It is in AndroidMk where the selection of flattened
-		// or unflattened APEX is made.
-		a.buildUnflattenedApex(ctx, imageApex)
-		a.buildFlattenedApex(ctx)
-	}
-}
-
-func (a *apexBundle) buildNoticeFile(ctx android.ModuleContext) {
-	noticeFiles := []android.Path{}
-	noticeFilesString := []string{}
-	for _, f := range a.filesInfo {
-		if f.module != nil {
-			notice := f.module.NoticeFile()
-			if notice.Valid() {
-				noticeFiles = append(noticeFiles, notice.Path())
-				noticeFilesString = append(noticeFilesString, notice.Path().String())
-			}
-		}
-	}
-	// append the notice file specified in the apex module itself
-	if a.NoticeFile().Valid() {
-		noticeFiles = append(noticeFiles, a.NoticeFile().Path())
-		noticeFilesString = append(noticeFilesString, a.NoticeFile().Path().String())
-	}
-
-	if len(noticeFiles) > 0 {
-		a.mergedNoticeFile = android.PathForModuleOut(ctx, "NOTICE")
-		ctx.Build(pctx, android.BuildParams{
-			Rule:   apexMergeNoticeRule,
-			Inputs: noticeFiles,
-			Output: a.mergedNoticeFile,
-			Args: map[string]string{
-				"inputs": strings.Join(noticeFilesString, " "),
-			},
-		})
-	}
-}
-
-func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext, apexType apexPackaging) {
-	cert := String(a.properties.Certificate)
-	if cert != "" && android.SrcIsModule(cert) == "" {
-		defaultDir := ctx.Config().DefaultAppCertificateDir(ctx)
-		a.container_certificate_file = defaultDir.Join(ctx, cert+".x509.pem")
-		a.container_private_key_file = defaultDir.Join(ctx, cert+".pk8")
-	} else if cert == "" {
-		pem, key := ctx.Config().DefaultAppCertificate(ctx)
-		a.container_certificate_file = pem
-		a.container_private_key_file = key
-	}
-
-	manifest := android.PathForModuleSrc(ctx, proptools.StringDefault(a.properties.Manifest, "apex_manifest.json"))
-
-	var abis []string
-	for _, target := range ctx.MultiTargets() {
-		if len(target.Arch.Abi) > 0 {
-			abis = append(abis, target.Arch.Abi[0])
-		}
-	}
-
-	abis = android.FirstUniqueStrings(abis)
-
-	suffix := apexType.suffix()
-	unsignedOutputFile := android.PathForModuleOut(ctx, ctx.ModuleName()+suffix+".unsigned")
-
-	filesToCopy := []android.Path{}
-	for _, f := range a.filesInfo {
-		filesToCopy = append(filesToCopy, f.builtFile)
-	}
-
-	copyCommands := []string{}
-	for i, src := range filesToCopy {
-		dest := filepath.Join(a.filesInfo[i].installDir, src.Base())
-		dest_path := filepath.Join(android.PathForModuleOut(ctx, "image"+suffix).String(), dest)
-		copyCommands = append(copyCommands, "mkdir -p "+filepath.Dir(dest_path))
-		copyCommands = append(copyCommands, "cp "+src.String()+" "+dest_path)
-		for _, sym := range a.filesInfo[i].symlinks {
-			symlinkDest := filepath.Join(filepath.Dir(dest_path), sym)
-			copyCommands = append(copyCommands, "ln -s "+filepath.Base(dest)+" "+symlinkDest)
-		}
-	}
-	implicitInputs := append(android.Paths(nil), filesToCopy...)
-	implicitInputs = append(implicitInputs, manifest)
-
-	outHostBinDir := android.PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "bin").String()
-	prebuiltSdkToolsBinDir := filepath.Join("prebuilts", "sdk", "tools", runtime.GOOS, "bin")
-
-	if apexType.image() {
-		// files and dirs that will be created in APEX
-		var readOnlyPaths []string
-		var executablePaths []string // this also includes dirs
-		for _, f := range a.filesInfo {
-			pathInApex := filepath.Join(f.installDir, f.builtFile.Base())
-			if f.installDir == "bin" {
-				executablePaths = append(executablePaths, pathInApex)
-				for _, s := range f.symlinks {
-					executablePaths = append(executablePaths, filepath.Join("bin", s))
-				}
-			} else {
-				readOnlyPaths = append(readOnlyPaths, pathInApex)
-			}
-			dir := f.installDir
-			for !android.InList(dir, executablePaths) && dir != "" {
-				executablePaths = append(executablePaths, dir)
-				dir, _ = filepath.Split(dir) // move up to the parent
-				if len(dir) > 0 {
-					// remove trailing slash
-					dir = dir[:len(dir)-1]
+	if a.properties.ApexType != zipApex {
+		if a.properties.File_contexts == nil {
+			a.fileContexts = android.PathForSource(ctx, "system/sepolicy/apex", ctx.ModuleName()+"-file_contexts")
+		} else {
+			a.fileContexts = android.PathForModuleSrc(ctx, *a.properties.File_contexts)
+			if a.Platform() {
+				if matched, err := path.Match("system/sepolicy/**/*", a.fileContexts.String()); err != nil || !matched {
+					ctx.PropertyErrorf("file_contexts", "should be under system/sepolicy, but %q", a.fileContexts)
 				}
 			}
 		}
-		sort.Strings(readOnlyPaths)
-		sort.Strings(executablePaths)
-		cannedFsConfig := android.PathForModuleOut(ctx, "canned_fs_config")
-		ctx.Build(pctx, android.BuildParams{
-			Rule:        generateFsConfig,
-			Output:      cannedFsConfig,
-			Description: "generate fs config",
-			Args: map[string]string{
-				"ro_paths":   strings.Join(readOnlyPaths, " "),
-				"exec_paths": strings.Join(executablePaths, " "),
-			},
-		})
-
-		fcName := proptools.StringDefault(a.properties.File_contexts, ctx.ModuleName())
-		fileContextsPath := "system/sepolicy/apex/" + fcName + "-file_contexts"
-		fileContextsOptionalPath := android.ExistentPathForSource(ctx, fileContextsPath)
-		if !fileContextsOptionalPath.Valid() {
-			ctx.ModuleErrorf("Cannot find file_contexts file: %q", fileContextsPath)
+		if !android.ExistentPathForSource(ctx, a.fileContexts.String()).Valid() {
+			ctx.PropertyErrorf("file_contexts", "cannot find file_contexts file: %q", a.fileContexts)
 			return
 		}
-		fileContexts := fileContextsOptionalPath.Path()
+	}
 
-		optFlags := []string{}
+	// prepare apex_manifest.json
+	a.buildManifest(ctx, provideNativeLibs, requireNativeLibs)
 
-		// Additional implicit inputs.
-		implicitInputs = append(implicitInputs, cannedFsConfig, fileContexts, a.private_key_file, a.public_key_file)
-		optFlags = append(optFlags, "--pubkey "+a.public_key_file.String())
-
-		manifestPackageName, overridden := ctx.DeviceConfig().OverrideManifestPackageNameFor(ctx.ModuleName())
-		if overridden {
-			optFlags = append(optFlags, "--override_apk_package_name "+manifestPackageName)
-		}
-
-		if a.properties.AndroidManifest != nil {
-			androidManifestFile := android.PathForModuleSrc(ctx, proptools.String(a.properties.AndroidManifest))
-			implicitInputs = append(implicitInputs, androidManifestFile)
-			optFlags = append(optFlags, "--android_manifest "+androidManifestFile.String())
-		}
-
-		targetSdkVersion := ctx.Config().DefaultAppTargetSdk()
-		if targetSdkVersion == ctx.Config().PlatformSdkCodename() &&
-			ctx.Config().UnbundledBuild() &&
-			!ctx.Config().UnbundledBuildUsePrebuiltSdks() &&
-			ctx.Config().IsEnvTrue("UNBUNDLED_BUILD_TARGET_SDK_WITH_API_FINGERPRINT") {
-			apiFingerprint := java.ApiFingerprintPath(ctx)
-			targetSdkVersion += fmt.Sprintf(".$$(cat %s)", apiFingerprint.String())
-			implicitInputs = append(implicitInputs, apiFingerprint)
-		}
-		optFlags = append(optFlags, "--target_sdk_version "+targetSdkVersion)
-
-		ctx.Build(pctx, android.BuildParams{
-			Rule:        apexRule,
-			Implicits:   implicitInputs,
-			Output:      unsignedOutputFile,
-			Description: "apex (" + apexType.name() + ")",
-			Args: map[string]string{
-				"tool_path":        outHostBinDir + ":" + prebuiltSdkToolsBinDir,
-				"image_dir":        android.PathForModuleOut(ctx, "image"+suffix).String(),
-				"copy_commands":    strings.Join(copyCommands, " && "),
-				"manifest":         manifest.String(),
-				"file_contexts":    fileContexts.String(),
-				"canned_fs_config": cannedFsConfig.String(),
-				"key":              a.private_key_file.String(),
-				"opt_flags":        strings.Join(optFlags, " "),
-			},
-		})
-
-		apexProtoFile := android.PathForModuleOut(ctx, ctx.ModuleName()+".pb"+suffix)
-		bundleModuleFile := android.PathForModuleOut(ctx, ctx.ModuleName()+suffix+"-base.zip")
-		a.bundleModuleFile = bundleModuleFile
-
-		ctx.Build(pctx, android.BuildParams{
-			Rule:        apexProtoConvertRule,
-			Input:       unsignedOutputFile,
-			Output:      apexProtoFile,
-			Description: "apex proto convert",
-		})
-
-		ctx.Build(pctx, android.BuildParams{
-			Rule:        apexBundleRule,
-			Input:       apexProtoFile,
-			Output:      a.bundleModuleFile,
-			Description: "apex bundle module",
-			Args: map[string]string{
-				"abi": strings.Join(abis, "."),
-			},
-		})
+	a.setCertificateAndPrivateKey(ctx)
+	if a.properties.ApexType == flattenedApex {
+		a.buildFlattenedApex(ctx)
 	} else {
-		ctx.Build(pctx, android.BuildParams{
-			Rule:        zipApexRule,
-			Implicits:   implicitInputs,
-			Output:      unsignedOutputFile,
-			Description: "apex (" + apexType.name() + ")",
-			Args: map[string]string{
-				"tool_path":     outHostBinDir + ":" + prebuiltSdkToolsBinDir,
-				"image_dir":     android.PathForModuleOut(ctx, "image"+suffix).String(),
-				"copy_commands": strings.Join(copyCommands, " && "),
-				"manifest":      manifest.String(),
-			},
-		})
+		a.buildUnflattenedApex(ctx)
 	}
 
-	a.outputFiles[apexType] = android.PathForModuleOut(ctx, ctx.ModuleName()+suffix)
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        java.Signapk,
-		Description: "signapk",
-		Output:      a.outputFiles[apexType],
-		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
-		},
-	})
+	a.compatSymlinks = makeCompatSymlinks(a.BaseModuleName(), ctx)
 
-	// Install to $OUT/soong/{target,host}/.../apex
-	if a.installable() && (!ctx.Config().FlattenApex() || apexType.zip()) {
-		ctx.InstallFile(a.installDir, ctx.ModuleName()+suffix, a.outputFiles[apexType])
-	}
+	a.buildApexDependencyInfo(ctx)
 }
 
-func (a *apexBundle) buildFlattenedApex(ctx android.ModuleContext) {
-	if a.installable() {
-		// For flattened APEX, do nothing but make sure that apex_manifest.json and apex_pubkey are also copied along
-		// with other ordinary files.
-		manifest := android.PathForModuleSrc(ctx, proptools.StringDefault(a.properties.Manifest, "apex_manifest.json"))
-
-		// rename to apex_manifest.json
-		copiedManifest := android.PathForModuleOut(ctx, "apex_manifest.json")
-		ctx.Build(pctx, android.BuildParams{
-			Rule:   android.Cp,
-			Input:  manifest,
-			Output: copiedManifest,
-		})
-		a.filesInfo = append(a.filesInfo, apexFile{copiedManifest, ctx.ModuleName() + ".apex_manifest.json", ".", etc, nil, nil})
-
-		// rename to apex_pubkey
-		copiedPubkey := android.PathForModuleOut(ctx, "apex_pubkey")
-		ctx.Build(pctx, android.BuildParams{
-			Rule:   android.Cp,
-			Input:  a.public_key_file,
-			Output: copiedPubkey,
-		})
-		a.filesInfo = append(a.filesInfo, apexFile{copiedPubkey, ctx.ModuleName() + ".apex_pubkey", ".", etc, nil, nil})
-
-		if ctx.Config().FlattenApex() {
-			for _, fi := range a.filesInfo {
-				dir := filepath.Join("apex", ctx.ModuleName(), fi.installDir)
-				target := ctx.InstallFile(android.PathForModuleInstall(ctx, dir), fi.builtFile.Base(), fi.builtFile)
-				for _, sym := range fi.symlinks {
-					ctx.InstallSymlink(android.PathForModuleInstall(ctx, dir), sym, target)
-				}
-			}
-		}
-	}
-}
-
-func (a *apexBundle) AndroidMk() android.AndroidMkData {
-	writers := []android.AndroidMkData{}
-	if a.apexTypes.image() {
-		writers = append(writers, a.androidMkForType(imageApex))
-	}
-	if a.apexTypes.zip() {
-		writers = append(writers, a.androidMkForType(zipApex))
-	}
-	return android.AndroidMkData{
-		Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
-			for _, data := range writers {
-				data.Custom(w, name, prefix, moduleDir, data)
-			}
-		}}
-}
-
-func (a *apexBundle) androidMkForFiles(w io.Writer, name, moduleDir string, apexType apexPackaging) []string {
-	moduleNames := []string{}
-
-	for _, fi := range a.filesInfo {
-		if cc, ok := fi.module.(*cc.Module); ok && cc.Properties.HideFromMake {
-			continue
-		}
-		if !android.InList(fi.moduleName, moduleNames) {
-			moduleNames = append(moduleNames, fi.moduleName)
-		}
-		fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
-		fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
-		fmt.Fprintln(w, "LOCAL_MODULE :=", fi.moduleName)
-		// /apex/<name>/{lib|framework|...}
-		pathWhenActivated := filepath.Join("$(PRODUCT_OUT)", "apex",
-			proptools.StringDefault(a.properties.Apex_name, name), fi.installDir)
-		if a.flattened && apexType.image() {
-			// /system/apex/<name>/{lib|framework|...}
-			fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", filepath.Join("$(OUT_DIR)",
-				a.installDir.RelPathString(), name, fi.installDir))
-			fmt.Fprintln(w, "LOCAL_SOONG_SYMBOL_PATH :=", pathWhenActivated)
-			if len(fi.symlinks) > 0 {
-				fmt.Fprintln(w, "LOCAL_MODULE_SYMLINKS :=", strings.Join(fi.symlinks, " "))
-			}
-
-			if fi.module != nil && fi.module.NoticeFile().Valid() {
-				fmt.Fprintln(w, "LOCAL_NOTICE_FILE :=", fi.module.NoticeFile().Path().String())
-			}
-		} else {
-			fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", pathWhenActivated)
-		}
-		fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", fi.builtFile.String())
-		fmt.Fprintln(w, "LOCAL_MODULE_CLASS :=", fi.class.NameInMake())
-		if fi.module != nil {
-			archStr := fi.module.Target().Arch.ArchType.String()
-			host := false
-			switch fi.module.Target().Os.Class {
-			case android.Host:
-				if archStr != "common" {
-					fmt.Fprintln(w, "LOCAL_MODULE_HOST_ARCH :=", archStr)
-				}
-				host = true
-			case android.HostCross:
-				if archStr != "common" {
-					fmt.Fprintln(w, "LOCAL_MODULE_HOST_CROSS_ARCH :=", archStr)
-				}
-				host = true
-			case android.Device:
-				if archStr != "common" {
-					fmt.Fprintln(w, "LOCAL_MODULE_TARGET_ARCH :=", archStr)
-				}
-			}
-			if host {
-				makeOs := fi.module.Target().Os.String()
-				if fi.module.Target().Os == android.Linux || fi.module.Target().Os == android.LinuxBionic {
-					makeOs = "linux"
-				}
-				fmt.Fprintln(w, "LOCAL_MODULE_HOST_OS :=", makeOs)
-				fmt.Fprintln(w, "LOCAL_IS_HOST_MODULE := true")
-			}
-		}
-		if fi.class == javaSharedLib {
-			javaModule := fi.module.(*java.Library)
-			// 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_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 == nativeSharedLib || fi.class == nativeExecutable {
-			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())
-				}
-				cc.AndroidMkWriteAdditionalDependenciesForSourceAbiDiff(w)
-			}
-			fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_cc_prebuilt.mk")
-		} else {
-			fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", fi.builtFile.Base())
-			fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
-		}
-	}
-	return moduleNames
-}
-
-func (a *apexBundle) androidMkForType(apexType apexPackaging) android.AndroidMkData {
-	return android.AndroidMkData{
-		Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
-			moduleNames := []string{}
-			if a.installable() {
-				moduleNames = a.androidMkForFiles(w, name, moduleDir, apexType)
-			}
-
-			if a.flattened && apexType.image() {
-				// Only image APEXes can be flattened.
-				fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
-				fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
-				fmt.Fprintln(w, "LOCAL_MODULE :=", name)
-				if len(moduleNames) > 0 {
-					fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES :=", strings.Join(moduleNames, " "))
-				}
-				fmt.Fprintln(w, "include $(BUILD_PHONY_PACKAGE)")
-			} else {
-				// zip-apex is the less common type so have the name refer to the image-apex
-				// only and use {name}.zip if you want the zip-apex
-				if apexType == zipApex && a.apexTypes == both {
-					name = name + ".zip"
-				}
-				fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
-				fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
-				fmt.Fprintln(w, "LOCAL_MODULE :=", name)
-				fmt.Fprintln(w, "LOCAL_MODULE_CLASS := ETC") // do we need a new class?
-				fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", a.outputFiles[apexType].String())
-				fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", filepath.Join("$(OUT_DIR)", a.installDir.RelPathString()))
-				fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", name+apexType.suffix())
-				fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE :=", !a.installable())
-				if a.installable() && a.mergedNoticeFile != nil {
-					fmt.Fprintln(w, "LOCAL_NOTICE_FILE :=", a.mergedNoticeFile.String())
-				}
-				if len(moduleNames) > 0 {
-					fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES +=", strings.Join(moduleNames, " "))
-				}
-				if len(a.externalDeps) > 0 {
-					fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES +=", strings.Join(a.externalDeps, " "))
-				}
-				fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
-
-				if apexType == imageApex {
-					fmt.Fprintln(w, "ALL_MODULES.$(LOCAL_MODULE).BUNDLE :=", a.bundleModuleFile.String())
-				}
-			}
-		}}
-}
-
-func testApexBundleFactory() android.Module {
-	return ApexBundleFactory( /*testApex*/ true)
-}
-
-func apexBundleFactory() android.Module {
-	return ApexBundleFactory( /*testApex*/ false)
-}
-
-func ApexBundleFactory(testApex bool) android.Module {
-	module := &apexBundle{
-		outputFiles: map[apexPackaging]android.WritablePath{},
-		testApex:    testApex,
-	}
+func newApexBundle() *apexBundle {
+	module := &apexBundle{}
 	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)
+	android.InitOverridableModule(module, &module.overridableProperties.Overrides)
 	return module
 }
 
+func ApexBundleFactory(testApex bool, artApex bool) android.Module {
+	bundle := newApexBundle()
+	bundle.testApex = testApex
+	bundle.artApex = artApex
+	return bundle
+}
+
+func testApexBundleFactory() android.Module {
+	bundle := newApexBundle()
+	bundle.testApex = true
+	return bundle
+}
+
+func BundleFactory() android.Module {
+	return newApexBundle()
+}
+
 //
 // Defaults
 //
@@ -1310,6 +1331,7 @@
 	module.AddProperties(
 		&apexBundleProperties{},
 		&apexTargetBundleProperties{},
+		&overridableProperties{},
 	)
 
 	android.InitDefaultsModule(module)
@@ -1317,138 +1339,24 @@
 }
 
 //
-// Prebuilt APEX
+// OverrideApex
 //
-type Prebuilt struct {
+type OverrideApex struct {
 	android.ModuleBase
-	prebuilt android.Prebuilt
-
-	properties PrebuiltProperties
-
-	inputApex       android.Path
-	installDir      android.OutputPath
-	installFilename string
-	outputApex      android.WritablePath
+	android.OverrideModuleBase
 }
 
-type PrebuiltProperties struct {
-	// the path to the prebuilt .apex file to import.
-	Source string `blueprint:"mutated"`
-
-	Src  *string
-	Arch struct {
-		Arm struct {
-			Src *string
-		}
-		Arm64 struct {
-			Src *string
-		}
-		X86 struct {
-			Src *string
-		}
-		X86_64 struct {
-			Src *string
-		}
-	}
-
-	Installable *bool
-	// Optional name for the installed apex. If unspecified, name of the
-	// module is used as the file name
-	Filename *string
+func (o *OverrideApex) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	// All the overrides happen in the base module.
 }
 
-func (p *Prebuilt) installable() bool {
-	return p.properties.Installable == nil || proptools.Bool(p.properties.Installable)
-}
+// override_apex is used to create an apex module based on another apex module
+// by overriding some of its properties.
+func overrideApexFactory() android.Module {
+	m := &OverrideApex{}
+	m.AddProperties(&overridableProperties{})
 
-func (p *Prebuilt) DepsMutator(ctx android.BottomUpMutatorContext) {
-	// This is called before prebuilt_select and prebuilt_postdeps mutators
-	// The mutators requires that src to be set correctly for each arch so that
-	// arch variants are disabled when src is not provided for the arch.
-	if len(ctx.MultiTargets()) != 1 {
-		ctx.ModuleErrorf("compile_multilib shouldn't be \"both\" for prebuilt_apex")
-		return
-	}
-	var src string
-	switch ctx.MultiTargets()[0].Arch.ArchType {
-	case android.Arm:
-		src = String(p.properties.Arch.Arm.Src)
-	case android.Arm64:
-		src = String(p.properties.Arch.Arm64.Src)
-	case android.X86:
-		src = String(p.properties.Arch.X86.Src)
-	case android.X86_64:
-		src = String(p.properties.Arch.X86_64.Src)
-	default:
-		ctx.ModuleErrorf("prebuilt_apex does not support %q", ctx.MultiTargets()[0].Arch.String())
-		return
-	}
-	if src == "" {
-		src = String(p.properties.Src)
-	}
-	p.properties.Source = src
-}
-
-func (p *Prebuilt) OutputFiles(tag string) (android.Paths, error) {
-	switch tag {
-	case "":
-		return android.Paths{p.outputApex}, nil
-	default:
-		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-	}
-}
-
-func (p *Prebuilt) InstallFilename() string {
-	return proptools.StringDefault(p.properties.Filename, p.BaseModuleName()+imageApexSuffix)
-}
-
-func (p *Prebuilt) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	// TODO(jungjw): Check the key validity.
-	p.inputApex = p.Prebuilt().SingleSourcePath(ctx)
-	p.installDir = android.PathForModuleInstall(ctx, "apex")
-	p.installFilename = p.InstallFilename()
-	if !strings.HasSuffix(p.installFilename, imageApexSuffix) {
-		ctx.ModuleErrorf("filename should end in %s for prebuilt_apex", imageApexSuffix)
-	}
-	p.outputApex = android.PathForModuleOut(ctx, p.installFilename)
-	ctx.Build(pctx, android.BuildParams{
-		Rule:   android.Cp,
-		Input:  p.inputApex,
-		Output: p.outputApex,
-	})
-	if p.installable() {
-		ctx.InstallFile(p.installDir, p.installFilename, p.inputApex)
-	}
-}
-
-func (p *Prebuilt) Prebuilt() *android.Prebuilt {
-	return &p.prebuilt
-}
-
-func (p *Prebuilt) Name() string {
-	return p.prebuilt.Name(p.ModuleBase.Name())
-}
-
-func (p *Prebuilt) AndroidMk() android.AndroidMkData {
-	return android.AndroidMkData{
-		Class:      "ETC",
-		OutputFile: android.OptionalPathForPath(p.inputApex),
-		Include:    "$(BUILD_PREBUILT)",
-		Extra: []android.AndroidMkExtraFunc{
-			func(w io.Writer, outputFile android.Path) {
-				fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", filepath.Join("$(OUT_DIR)", p.installDir.RelPathString()))
-				fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", p.installFilename)
-				fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE :=", !p.installable())
-			},
-		},
-	}
-}
-
-// prebuilt_apex imports an `.apex` file into the build graph as if it was built with apex.
-func PrebuiltFactory() android.Module {
-	module := &Prebuilt{}
-	module.AddProperties(&module.properties)
-	android.InitSingleSourcePrebuiltModule(module, &module.properties.Source)
-	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
-	return module
+	android.InitAndroidMultiTargetsArchModule(m, android.DeviceSupported, android.MultilibCommon)
+	android.InitOverrideModule(m)
+	return m
 }
diff --git a/apex/apex_test.go b/apex/apex_test.go
index f71abd7..b37674f 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -17,6 +17,9 @@
 import (
 	"io/ioutil"
 	"os"
+	"path"
+	"reflect"
+	"sort"
 	"strings"
 	"testing"
 
@@ -27,48 +30,69 @@
 	"android/soong/java"
 )
 
-func testApex(t *testing.T, bp string) *android.TestContext {
-	config, buildDir := setup(t)
-	defer teardown(buildDir)
+var buildDir string
 
-	ctx := android.NewTestArchContext()
-	ctx.RegisterModuleType("apex", android.ModuleFactoryAdaptor(apexBundleFactory))
-	ctx.RegisterModuleType("apex_test", android.ModuleFactoryAdaptor(testApexBundleFactory))
-	ctx.RegisterModuleType("apex_key", android.ModuleFactoryAdaptor(apexKeyFactory))
-	ctx.RegisterModuleType("apex_defaults", android.ModuleFactoryAdaptor(defaultsFactory))
-	ctx.RegisterModuleType("prebuilt_apex", android.ModuleFactoryAdaptor(PrebuiltFactory))
-	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
+// names returns name list from white space separated string
+func names(s string) (ns []string) {
+	for _, n := range strings.Split(s, " ") {
+		if len(n) > 0 {
+			ns = append(ns, n)
+		}
+	}
+	return
+}
 
-	ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.TopDown("apex_deps", apexDepsMutator)
-		ctx.BottomUp("apex", apexMutator)
-		ctx.TopDown("prebuilt_select", android.PrebuiltSelectModuleMutator).Parallel()
-		ctx.BottomUp("prebuilt_postdeps", android.PrebuiltPostDepsMutator).Parallel()
-	})
+func testApexError(t *testing.T, pattern, bp string, handlers ...testCustomizer) {
+	t.Helper()
+	ctx, config := testApexContext(t, bp, handlers...)
+	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+	if len(errs) > 0 {
+		android.FailIfNoMatchingErrors(t, pattern, errs)
+		return
+	}
+	_, errs = ctx.PrepareBuildActions(config)
+	if len(errs) > 0 {
+		android.FailIfNoMatchingErrors(t, pattern, errs)
+		return
+	}
 
-	ctx.RegisterModuleType("cc_library", android.ModuleFactoryAdaptor(cc.LibraryFactory))
-	ctx.RegisterModuleType("cc_library_shared", android.ModuleFactoryAdaptor(cc.LibrarySharedFactory))
-	ctx.RegisterModuleType("cc_library_headers", android.ModuleFactoryAdaptor(cc.LibraryHeaderFactory))
-	ctx.RegisterModuleType("cc_binary", android.ModuleFactoryAdaptor(cc.BinaryFactory))
-	ctx.RegisterModuleType("cc_object", android.ModuleFactoryAdaptor(cc.ObjectFactory))
-	ctx.RegisterModuleType("llndk_library", android.ModuleFactoryAdaptor(cc.LlndkLibraryFactory))
-	ctx.RegisterModuleType("toolchain_library", android.ModuleFactoryAdaptor(cc.ToolchainLibraryFactory))
-	ctx.RegisterModuleType("prebuilt_etc", android.ModuleFactoryAdaptor(android.PrebuiltEtcFactory))
-	ctx.RegisterModuleType("sh_binary", android.ModuleFactoryAdaptor(android.ShBinaryFactory))
-	ctx.RegisterModuleType("android_app_certificate", android.ModuleFactoryAdaptor(java.AndroidAppCertificateFactory))
-	ctx.RegisterModuleType("filegroup", android.ModuleFactoryAdaptor(android.FileGroupFactory))
-	ctx.PreArchMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.BottomUp("prebuilts", android.PrebuiltMutator).Parallel()
-	})
-	ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.BottomUp("image", cc.ImageMutator).Parallel()
-		ctx.BottomUp("link", cc.LinkageMutator).Parallel()
-		ctx.BottomUp("vndk", cc.VndkMutator).Parallel()
-		ctx.BottomUp("version", cc.VersionMutator).Parallel()
-		ctx.BottomUp("begin", cc.BeginMutator).Parallel()
-	})
+	t.Fatalf("missing expected error %q (0 errors are returned)", pattern)
+}
 
-	ctx.Register()
+func testApex(t *testing.T, bp string, handlers ...testCustomizer) (*android.TestContext, android.Config) {
+	t.Helper()
+	ctx, config := testApexContext(t, bp, handlers...)
+	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+	android.FailIfErrored(t, errs)
+	_, errs = ctx.PrepareBuildActions(config)
+	android.FailIfErrored(t, errs)
+	return ctx, config
+}
+
+type testCustomizer func(fs map[string][]byte, config android.Config)
+
+func withFiles(files map[string][]byte) testCustomizer {
+	return func(fs map[string][]byte, config android.Config) {
+		for k, v := range files {
+			fs[k] = v
+		}
+	}
+}
+
+func withTargets(targets map[android.OsType][]android.Target) testCustomizer {
+	return func(fs map[string][]byte, config android.Config) {
+		for k, v := range targets {
+			config.Targets[k] = v
+		}
+	}
+}
+
+func withBinder32bit(fs map[string][]byte, config android.Config) {
+	config.TestProductVariables.Binder32bit = proptools.BoolPtr(true)
+}
+
+func testApexContext(t *testing.T, bp string, handlers ...testCustomizer) (*android.TestContext, android.Config) {
+	android.ClearApexDependency()
 
 	bp = bp + `
 		toolchain_library {
@@ -83,6 +107,7 @@
 			src: "",
 			vendor_available: true,
 			recovery_available: true,
+			native_bridge_supported: true,
 		}
 
 		toolchain_library {
@@ -97,6 +122,7 @@
 			src: "",
 			vendor_available: true,
 			recovery_available: true,
+			native_bridge_supported: true,
 		}
 
 		toolchain_library {
@@ -104,6 +130,7 @@
 			src: "",
 			vendor_available: true,
 			recovery_available: true,
+			native_bridge_supported: true,
 		}
 
 		toolchain_library {
@@ -111,6 +138,23 @@
 			src: "",
 			vendor_available: true,
 			recovery_available: true,
+			native_bridge_supported: true,
+		}
+
+		toolchain_library {
+			name: "libclang_rt.builtins-x86_64-android",
+			src: "",
+			vendor_available: true,
+			recovery_available: true,
+			native_bridge_supported: true,
+		}
+
+		toolchain_library {
+			name: "libclang_rt.builtins-i686-android",
+			src: "",
+			vendor_available: true,
+			recovery_available: true,
+			native_bridge_supported: true,
 		}
 
 		cc_object {
@@ -118,6 +162,7 @@
 			stl: "none",
 			vendor_available: true,
 			recovery_available: true,
+			native_bridge_supported: true,
 		}
 
 		cc_object {
@@ -125,6 +170,7 @@
 			stl: "none",
 			vendor_available: true,
 			recovery_available: true,
+			native_bridge_supported: true,
 		}
 
 		cc_object {
@@ -140,75 +186,145 @@
 		llndk_library {
 			name: "libc",
 			symbol_file: "",
+			native_bridge_supported: true,
 		}
 
 		llndk_library {
 			name: "libm",
 			symbol_file: "",
+			native_bridge_supported: true,
 		}
 
 		llndk_library {
 			name: "libdl",
 			symbol_file: "",
+			native_bridge_supported: true,
+		}
+
+		filegroup {
+			name: "myapex-file_contexts",
+			srcs: [
+				"system/sepolicy/apex/myapex-file_contexts",
+			],
 		}
 	`
 
-	ctx.MockFileSystem(map[string][]byte{
-		"Android.bp":                                        []byte(bp),
-		"build/make/target/product/security":                nil,
-		"apex_manifest.json":                                nil,
-		"AndroidManifest.xml":                               nil,
-		"system/sepolicy/apex/myapex-file_contexts":         nil,
-		"system/sepolicy/apex/myapex_keytest-file_contexts": nil,
-		"system/sepolicy/apex/otherapex-file_contexts":      nil,
-		"mylib.cpp":                            nil,
-		"myprebuilt":                           nil,
-		"my_include":                           nil,
-		"vendor/foo/devkeys/test.x509.pem":     nil,
-		"vendor/foo/devkeys/test.pk8":          nil,
-		"testkey.x509.pem":                     nil,
-		"testkey.pk8":                          nil,
-		"testkey.override.x509.pem":            nil,
-		"testkey.override.pk8":                 nil,
-		"vendor/foo/devkeys/testkey.avbpubkey": nil,
-		"vendor/foo/devkeys/testkey.pem":       nil,
-		"NOTICE":                               nil,
-		"custom_notice":                        nil,
-		"testkey2.avbpubkey":                   nil,
-		"testkey2.pem":                         nil,
-		"myapex-arm64.apex":                    nil,
-		"myapex-arm.apex":                      nil,
-		"frameworks/base/api/current.txt":      nil,
-	})
-	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
-	android.FailIfErrored(t, errs)
-	_, errs = ctx.PrepareBuildActions(config)
-	android.FailIfErrored(t, errs)
+	bp = bp + java.GatherRequiredDepsForTest()
 
-	return ctx
-}
-
-func setup(t *testing.T) (config android.Config, buildDir string) {
-	buildDir, err := ioutil.TempDir("", "soong_apex_test")
-	if err != nil {
-		t.Fatal(err)
+	fs := map[string][]byte{
+		"a.java":                                              nil,
+		"PrebuiltAppFoo.apk":                                  nil,
+		"PrebuiltAppFooPriv.apk":                              nil,
+		"build/make/target/product/security":                  nil,
+		"apex_manifest.json":                                  nil,
+		"AndroidManifest.xml":                                 nil,
+		"system/sepolicy/apex/myapex-file_contexts":           nil,
+		"system/sepolicy/apex/myapex2-file_contexts":          nil,
+		"system/sepolicy/apex/otherapex-file_contexts":        nil,
+		"system/sepolicy/apex/commonapex-file_contexts":       nil,
+		"system/sepolicy/apex/com.android.vndk-file_contexts": nil,
+		"mylib.cpp":                                  nil,
+		"mylib_common.cpp":                           nil,
+		"mytest.cpp":                                 nil,
+		"mytest1.cpp":                                nil,
+		"mytest2.cpp":                                nil,
+		"mytest3.cpp":                                nil,
+		"myprebuilt":                                 nil,
+		"my_include":                                 nil,
+		"foo/bar/MyClass.java":                       nil,
+		"prebuilt.jar":                               nil,
+		"vendor/foo/devkeys/test.x509.pem":           nil,
+		"vendor/foo/devkeys/test.pk8":                nil,
+		"testkey.x509.pem":                           nil,
+		"testkey.pk8":                                nil,
+		"testkey.override.x509.pem":                  nil,
+		"testkey.override.pk8":                       nil,
+		"vendor/foo/devkeys/testkey.avbpubkey":       nil,
+		"vendor/foo/devkeys/testkey.pem":             nil,
+		"NOTICE":                                     nil,
+		"custom_notice":                              nil,
+		"testkey2.avbpubkey":                         nil,
+		"testkey2.pem":                               nil,
+		"myapex-arm64.apex":                          nil,
+		"myapex-arm.apex":                            nil,
+		"frameworks/base/api/current.txt":            nil,
+		"framework/aidl/a.aidl":                      nil,
+		"build/make/core/proguard.flags":             nil,
+		"build/make/core/proguard_basic_keeps.flags": nil,
+		"dummy.txt":                                  nil,
 	}
 
-	config = android.TestArchConfig(buildDir, nil)
+	for _, handler := range handlers {
+		// The fs now needs to be populated before creating the config, call handlers twice
+		// for now, once to get any fs changes, and later after the config was created to
+		// set product variables or targets.
+		tempConfig := android.TestArchConfig(buildDir, nil, bp, fs)
+		handler(fs, tempConfig)
+	}
+
+	config := android.TestArchConfig(buildDir, nil, bp, fs)
 	config.TestProductVariables.DeviceVndkVersion = proptools.StringPtr("current")
 	config.TestProductVariables.DefaultAppCertificate = proptools.StringPtr("vendor/foo/devkeys/test")
 	config.TestProductVariables.CertificateOverrides = []string{"myapex_keytest:myapex.certificate.override"}
 	config.TestProductVariables.Platform_sdk_codename = proptools.StringPtr("Q")
 	config.TestProductVariables.Platform_sdk_final = proptools.BoolPtr(false)
-	return
+	config.TestProductVariables.Platform_vndk_version = proptools.StringPtr("VER")
+
+	for _, handler := range handlers {
+		// The fs now needs to be populated before creating the config, call handlers twice
+		// for now, earlier to get any fs changes, and now after the config was created to
+		// set product variables or targets.
+		tempFS := map[string][]byte{}
+		handler(tempFS, config)
+	}
+
+	ctx := android.NewTestArchContext()
+	ctx.RegisterModuleType("apex", BundleFactory)
+	ctx.RegisterModuleType("apex_test", testApexBundleFactory)
+	ctx.RegisterModuleType("apex_vndk", vndkApexBundleFactory)
+	ctx.RegisterModuleType("apex_key", ApexKeyFactory)
+	ctx.RegisterModuleType("apex_defaults", defaultsFactory)
+	ctx.RegisterModuleType("prebuilt_apex", PrebuiltFactory)
+	ctx.RegisterModuleType("override_apex", overrideApexFactory)
+
+	cc.RegisterRequiredBuildComponentsForTest(ctx)
+	ctx.RegisterModuleType("cc_binary", cc.BinaryFactory)
+	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("sh_binary", android.ShBinaryFactory)
+	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
+	java.RegisterJavaBuildComponents(ctx)
+	java.RegisterSystemModulesBuildComponents(ctx)
+	java.RegisterAppBuildComponents(ctx)
+	ctx.RegisterModuleType("java_sdk_library", java.SdkLibraryFactory)
+
+	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
+	ctx.PreDepsMutators(RegisterPreDepsMutators)
+	ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators)
+	ctx.PostDepsMutators(RegisterPostDepsMutators)
+
+	ctx.Register(config)
+
+	return ctx, config
 }
 
-func teardown(buildDir string) {
+func setUp() {
+	var err error
+	buildDir, err = ioutil.TempDir("", "soong_apex_test")
+	if err != nil {
+		panic(err)
+	}
+}
+
+func tearDown() {
 	os.RemoveAll(buildDir)
 }
 
 // ensure that 'result' contains 'expected'
 func ensureContains(t *testing.T, result string, expected string) {
+	t.Helper()
 	if !strings.Contains(result, expected) {
 		t.Errorf("%q is not found in %q", expected, result)
 	}
@@ -216,26 +332,36 @@
 
 // ensures that 'result' does not contain 'notExpected'
 func ensureNotContains(t *testing.T, result string, notExpected string) {
+	t.Helper()
 	if strings.Contains(result, notExpected) {
 		t.Errorf("%q is found in %q", notExpected, result)
 	}
 }
 
 func ensureListContains(t *testing.T, result []string, expected string) {
+	t.Helper()
 	if !android.InList(expected, result) {
 		t.Errorf("%q is not found in %v", expected, result)
 	}
 }
 
 func ensureListNotContains(t *testing.T, result []string, notExpected string) {
+	t.Helper()
 	if android.InList(notExpected, result) {
 		t.Errorf("%q is found in %v", notExpected, result)
 	}
 }
 
+func ensureListEmpty(t *testing.T, result []string) {
+	t.Helper()
+	if len(result) > 0 {
+		t.Errorf("%q is expected to be empty", result)
+	}
+}
+
 // Minimal test
 func TestBasicApex(t *testing.T) {
-	ctx := testApex(t, `
+	ctx, _ := testApex(t, `
 		apex_defaults {
 			name: "myapex-defaults",
 			manifest: ":myapex.manifest",
@@ -246,7 +372,8 @@
 				both: {
 					binaries: ["foo",],
 				}
-			}
+			},
+			java_libs: ["myjar"],
 		}
 
 		apex {
@@ -304,12 +431,40 @@
 			stl: "none",
 			notice: "custom_notice",
 		}
+
+		java_library {
+			name: "myjar",
+			srcs: ["foo/bar/MyClass.java"],
+			sdk_version: "none",
+			system_modules: "none",
+			compile_dex: true,
+			static_libs: ["myotherjar"],
+			libs: ["mysharedjar"],
+		}
+
+		java_library {
+			name: "myotherjar",
+			srcs: ["foo/bar/MyClass.java"],
+			sdk_version: "none",
+			system_modules: "none",
+			compile_dex: true,
+		}
+
+		java_library {
+			name: "mysharedjar",
+			srcs: ["foo/bar/MyClass.java"],
+			sdk_version: "none",
+			system_modules: "none",
+			compile_dex: true,
+		}
 	`)
 
-	apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule")
+	apexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexRule")
 
 	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.
+	ensureContains(t, optFlags, "--assets_dir "+buildDir+"/.intermediates/myapex/android_common_myapex_image/NOTICE")
 
 	copyCmds := apexRule.Args["copy_commands"]
 
@@ -317,18 +472,30 @@
 	ensureContains(t, apexRule.Output.String(), "myapex.apex.unsigned")
 
 	// Ensure that apex variant is created for the direct dep
-	ensureListContains(t, ctx.ModuleVariantsForTests("mylib"), "android_arm64_armv8-a_core_shared_myapex")
+	ensureListContains(t, ctx.ModuleVariantsForTests("mylib"), "android_arm64_armv8-a_shared_myapex")
+	ensureListContains(t, ctx.ModuleVariantsForTests("myjar"), "android_common_myapex")
 
 	// Ensure that apex variant is created for the indirect dep
-	ensureListContains(t, ctx.ModuleVariantsForTests("mylib2"), "android_arm64_armv8-a_core_shared_myapex")
+	ensureListContains(t, ctx.ModuleVariantsForTests("mylib2"), "android_arm64_armv8-a_shared_myapex")
+	ensureListContains(t, ctx.ModuleVariantsForTests("myotherjar"), "android_common_myapex")
 
 	// 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")
+	// .. but not for java libs
+	ensureNotContains(t, copyCmds, "image.apex/javalib/myotherjar.jar")
+	ensureNotContains(t, copyCmds, "image.apex/javalib/msharedjar.jar")
 
-	// Ensure that the platform variant ends with _core_shared
-	ensureListContains(t, ctx.ModuleVariantsForTests("mylib"), "android_arm64_armv8-a_core_shared")
-	ensureListContains(t, ctx.ModuleVariantsForTests("mylib2"), "android_arm64_armv8-a_core_shared")
+	// Ensure that the platform variant ends with _shared or _common
+	ensureListContains(t, ctx.ModuleVariantsForTests("mylib"), "android_arm64_armv8-a_shared")
+	ensureListContains(t, ctx.ModuleVariantsForTests("mylib2"), "android_arm64_armv8-a_shared")
+	ensureListContains(t, ctx.ModuleVariantsForTests("myjar"), "android_common")
+	ensureListContains(t, ctx.ModuleVariantsForTests("myotherjar"), "android_common")
+	ensureListContains(t, ctx.ModuleVariantsForTests("mysharedjar"), "android_common")
+
+	// Ensure that dynamic dependency to java libs are not included
+	ensureListNotContains(t, ctx.ModuleVariantsForTests("mysharedjar"), "android_common_myapex")
 
 	// Ensure that all symlinks are present.
 	found_foo_link_64 := false
@@ -347,17 +514,100 @@
 		t.Errorf("Could not find all expected symlinks! foo: %t, foo_link_64: %t. Command was %s", found_foo, found_foo_link_64, copyCmds)
 	}
 
-	apexMergeNoticeRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexMergeNoticeRule")
-	noticeInputs := strings.Split(apexMergeNoticeRule.Args["inputs"], " ")
-	if len(noticeInputs) != 3 {
-		t.Errorf("number of input notice files: expected = 3, actual = %d", len(noticeInputs))
+	mergeNoticesRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("mergeNoticesRule")
+	noticeInputs := mergeNoticesRule.Inputs.Strings()
+	if len(noticeInputs) != 2 {
+		t.Errorf("number of input notice files: expected = 2, actual = %q", len(noticeInputs))
 	}
 	ensureListContains(t, noticeInputs, "NOTICE")
 	ensureListContains(t, noticeInputs, "custom_notice")
+
+	depsInfo := strings.Split(ctx.ModuleForTests("myapex", "android_common_myapex_image").Output("myapex-deps-info.txt").Args["content"], "\\n")
+	ensureListContains(t, depsInfo, "internal myjar")
+	ensureListContains(t, depsInfo, "internal mylib")
+	ensureListContains(t, depsInfo, "internal mylib2")
+	ensureListContains(t, depsInfo, "internal myotherjar")
+}
+
+func TestDefaults(t *testing.T) {
+	ctx, _ := testApex(t, `
+		apex_defaults {
+			name: "myapex-defaults",
+			key: "myapex.key",
+			prebuilts: ["myetc"],
+			native_shared_libs: ["mylib"],
+			java_libs: ["myjar"],
+			apps: ["AppFoo"],
+		}
+
+		prebuilt_etc {
+			name: "myetc",
+			src: "myprebuilt",
+		}
+
+		apex {
+			name: "myapex",
+			defaults: ["myapex-defaults"],
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "mylib",
+			system_shared_libs: [],
+			stl: "none",
+		}
+
+		java_library {
+			name: "myjar",
+			srcs: ["foo/bar/MyClass.java"],
+			sdk_version: "none",
+			system_modules: "none",
+			compile_dex: true,
+		}
+
+		android_app {
+			name: "AppFoo",
+			srcs: ["foo/bar/MyClass.java"],
+			sdk_version: "none",
+			system_modules: "none",
+		}
+	`)
+	ensureExactContents(t, ctx, "myapex", []string{
+		"etc/myetc",
+		"javalib/myjar.jar",
+		"lib64/mylib.so",
+		"app/AppFoo/AppFoo.apk",
+	})
+}
+
+func TestApexManifest(t *testing.T) {
+	ctx, _ := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+	`)
+
+	module := ctx.ModuleForTests("myapex", "android_common_myapex_image")
+	args := module.Rule("apexRule").Args
+	if manifest := args["manifest"]; manifest != module.Output("apex_manifest.pb").Output.String() {
+		t.Error("manifest should be apex_manifest.pb, but " + manifest)
+	}
 }
 
 func TestBasicZipApex(t *testing.T) {
-	ctx := testApex(t, `
+	ctx, _ := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
@@ -387,17 +637,17 @@
 		}
 	`)
 
-	zipApexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("zipApexRule")
+	zipApexRule := ctx.ModuleForTests("myapex", "android_common_myapex_zip").Rule("zipApexRule")
 	copyCmds := zipApexRule.Args["copy_commands"]
 
 	// Ensure that main rule creates an output
 	ensureContains(t, zipApexRule.Output.String(), "myapex.zipapex.unsigned")
 
 	// Ensure that APEX variant is created for the direct dep
-	ensureListContains(t, ctx.ModuleVariantsForTests("mylib"), "android_arm64_armv8-a_core_shared_myapex")
+	ensureListContains(t, ctx.ModuleVariantsForTests("mylib"), "android_arm64_armv8-a_shared_myapex")
 
 	// Ensure that APEX variant is created for the indirect dep
-	ensureListContains(t, ctx.ModuleVariantsForTests("mylib2"), "android_arm64_armv8-a_core_shared_myapex")
+	ensureListContains(t, ctx.ModuleVariantsForTests("mylib2"), "android_arm64_armv8-a_shared_myapex")
 
 	// Ensure that both direct and indirect deps are copied into apex
 	ensureContains(t, copyCmds, "image.zipapex/lib64/mylib.so")
@@ -405,7 +655,7 @@
 }
 
 func TestApexWithStubs(t *testing.T) {
-	ctx := testApex(t, `
+	ctx, _ := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
@@ -456,7 +706,7 @@
 		}
 	`)
 
-	apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule")
+	apexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexRule")
 	copyCmds := apexRule.Args["copy_commands"]
 
 	// Ensure that direct non-stubs dep is always included
@@ -468,36 +718,42 @@
 	// Ensure that direct stubs dep is included
 	ensureContains(t, copyCmds, "image.apex/lib64/mylib3.so")
 
-	mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_core_shared_myapex").Rule("ld").Args["libFlags"]
+	mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_myapex").Rule("ld").Args["libFlags"]
 
 	// Ensure that mylib is linking with the latest version of stubs for mylib2
-	ensureContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_core_shared_3_myapex/mylib2.so")
+	ensureContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared_3/mylib2.so")
 	// ... and not linking to the non-stub (impl) variant of mylib2
-	ensureNotContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_core_shared_myapex/mylib2.so")
+	ensureNotContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared/mylib2.so")
 
 	// Ensure that mylib is linking with the non-stub (impl) of mylib3 (because mylib3 is in the same apex)
-	ensureContains(t, mylibLdFlags, "mylib3/android_arm64_armv8-a_core_shared_myapex/mylib3.so")
+	ensureContains(t, mylibLdFlags, "mylib3/android_arm64_armv8-a_shared_myapex/mylib3.so")
 	// .. and not linking to the stubs variant of mylib3
-	ensureNotContains(t, mylibLdFlags, "mylib3/android_arm64_armv8-a_core_shared_12_myapex/mylib3.so")
+	ensureNotContains(t, mylibLdFlags, "mylib3/android_arm64_armv8-a_shared_12_myapex/mylib3.so")
 
 	// Ensure that stubs libs are built without -include flags
-	mylib2Cflags := ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_core_static_myapex").Rule("cc").Args["cFlags"]
+	mylib2Cflags := ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_static_myapex").Rule("cc").Args["cFlags"]
 	ensureNotContains(t, mylib2Cflags, "-include ")
 
 	// Ensure that genstub is invoked with --apex
-	ensureContains(t, "--apex", ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_core_static_3_myapex").Rule("genStubSrc").Args["flags"])
+	ensureContains(t, "--apex", ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_static_3").Rule("genStubSrc").Args["flags"])
+
+	ensureExactContents(t, ctx, "myapex", []string{
+		"lib64/mylib.so",
+		"lib64/mylib3.so",
+		"lib64/mylib4.so",
+	})
 }
 
 func TestApexWithExplicitStubsDependency(t *testing.T) {
-	ctx := testApex(t, `
+	ctx, _ := testApex(t, `
 		apex {
-			name: "myapex",
-			key: "myapex.key",
+			name: "myapex2",
+			key: "myapex2.key",
 			native_shared_libs: ["mylib"],
 		}
 
 		apex_key {
-			name: "myapex.key",
+			name: "myapex2.key",
 			public_key: "testkey.avbpubkey",
 			private_key: "testkey.pem",
 		}
@@ -530,7 +786,7 @@
 
 	`)
 
-	apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule")
+	apexRule := ctx.ModuleForTests("myapex2", "android_common_myapex2_image").Rule("apexRule")
 	copyCmds := apexRule.Args["copy_commands"]
 
 	// Ensure that direct non-stubs dep is always included
@@ -542,21 +798,147 @@
 	// Ensure that dependency of stubs is not included
 	ensureNotContains(t, copyCmds, "image.apex/lib64/libbar.so")
 
-	mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_core_shared_myapex").Rule("ld").Args["libFlags"]
+	mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_myapex2").Rule("ld").Args["libFlags"]
 
 	// Ensure that mylib is linking with version 10 of libfoo
-	ensureContains(t, mylibLdFlags, "libfoo/android_arm64_armv8-a_core_shared_10_myapex/libfoo.so")
+	ensureContains(t, mylibLdFlags, "libfoo/android_arm64_armv8-a_shared_10/libfoo.so")
 	// ... and not linking to the non-stub (impl) variant of libfoo
-	ensureNotContains(t, mylibLdFlags, "libfoo/android_arm64_armv8-a_core_shared_myapex/libfoo.so")
+	ensureNotContains(t, mylibLdFlags, "libfoo/android_arm64_armv8-a_shared/libfoo.so")
 
-	libFooStubsLdFlags := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_core_shared_10_myapex").Rule("ld").Args["libFlags"]
+	libFooStubsLdFlags := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared_10").Rule("ld").Args["libFlags"]
 
 	// Ensure that libfoo stubs is not linking to libbar (since it is a stubs)
 	ensureNotContains(t, libFooStubsLdFlags, "libbar.so")
+
+	depsInfo := strings.Split(ctx.ModuleForTests("myapex2", "android_common_myapex2_image").Output("myapex2-deps-info.txt").Args["content"], "\\n")
+	ensureListContains(t, depsInfo, "internal mylib")
+	ensureListContains(t, depsInfo, "external libfoo")
+	ensureListNotContains(t, depsInfo, "internal libfoo")
+	ensureListNotContains(t, depsInfo, "external mylib")
+}
+
+func TestApexWithRuntimeLibsDependency(t *testing.T) {
+	/*
+		myapex
+		  |
+		  v   (runtime_libs)
+		mylib ------+------> libfoo [provides stub]
+			    |
+			    `------> libbar
+	*/
+	ctx, _ := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			native_shared_libs: ["mylib"],
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "mylib",
+			srcs: ["mylib.cpp"],
+			runtime_libs: ["libfoo", "libbar"],
+			system_shared_libs: [],
+			stl: "none",
+		}
+
+		cc_library {
+			name: "libfoo",
+			srcs: ["mylib.cpp"],
+			system_shared_libs: [],
+			stl: "none",
+			stubs: {
+				versions: ["10", "20", "30"],
+			},
+		}
+
+		cc_library {
+			name: "libbar",
+			srcs: ["mylib.cpp"],
+			system_shared_libs: [],
+			stl: "none",
+		}
+
+	`)
+
+	apexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexRule")
+	copyCmds := apexRule.Args["copy_commands"]
+
+	// Ensure that direct non-stubs dep is always included
+	ensureContains(t, copyCmds, "image.apex/lib64/mylib.so")
+
+	// Ensure that indirect stubs dep is not included
+	ensureNotContains(t, copyCmds, "image.apex/lib64/libfoo.so")
+
+	// Ensure that runtime_libs dep in included
+	ensureContains(t, copyCmds, "image.apex/lib64/libbar.so")
+
+	apexManifestRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexManifestRule")
+	ensureListEmpty(t, names(apexManifestRule.Args["provideNativeLibs"]))
+	ensureListContains(t, names(apexManifestRule.Args["requireNativeLibs"]), "libfoo.so")
+
+}
+
+func TestApexDependencyToLLNDK(t *testing.T) {
+	ctx, _ := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			use_vendor: true,
+			native_shared_libs: ["mylib"],
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "mylib",
+			srcs: ["mylib.cpp"],
+			vendor_available: true,
+			shared_libs: ["libbar"],
+			system_shared_libs: [],
+			stl: "none",
+		}
+
+		cc_library {
+			name: "libbar",
+			srcs: ["mylib.cpp"],
+			system_shared_libs: [],
+			stl: "none",
+		}
+
+		llndk_library {
+			name: "libbar",
+			symbol_file: "",
+		}
+	`, func(fs map[string][]byte, config android.Config) {
+		setUseVendorWhitelistForTest(config, []string{"myapex"})
+	})
+
+	apexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexRule")
+	copyCmds := apexRule.Args["copy_commands"]
+
+	// Ensure that LLNDK dep is not included
+	ensureNotContains(t, copyCmds, "image.apex/lib64/libbar.so")
+
+	apexManifestRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexManifestRule")
+	ensureListEmpty(t, names(apexManifestRule.Args["provideNativeLibs"]))
+
+	// Ensure that LLNDK dep is required
+	ensureListContains(t, names(apexManifestRule.Args["requireNativeLibs"]), "libbar.so")
+
 }
 
 func TestApexWithSystemLibsStubs(t *testing.T) {
-	ctx := testApex(t, `
+	ctx, _ := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
@@ -624,7 +1006,7 @@
 		}
 	`)
 
-	apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule")
+	apexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexRule")
 	copyCmds := apexRule.Args["copy_commands"]
 
 	// Ensure that mylib, libm, libdl are included.
@@ -635,49 +1017,49 @@
 	// Ensure that libc is not included (since it has stubs and not listed in native_shared_libs)
 	ensureNotContains(t, copyCmds, "image.apex/lib64/bionic/libc.so")
 
-	mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_core_shared_myapex").Rule("ld").Args["libFlags"]
-	mylibCFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_core_static_myapex").Rule("cc").Args["cFlags"]
-	mylibSharedCFlags := ctx.ModuleForTests("mylib_shared", "android_arm64_armv8-a_core_shared_myapex").Rule("cc").Args["cFlags"]
+	mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_myapex").Rule("ld").Args["libFlags"]
+	mylibCFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static_myapex").Rule("cc").Args["cFlags"]
+	mylibSharedCFlags := ctx.ModuleForTests("mylib_shared", "android_arm64_armv8-a_shared_myapex").Rule("cc").Args["cFlags"]
 
 	// For dependency to libc
 	// Ensure that mylib is linking with the latest version of stubs
-	ensureContains(t, mylibLdFlags, "libc/android_arm64_armv8-a_core_shared_29_myapex/libc.so")
+	ensureContains(t, mylibLdFlags, "libc/android_arm64_armv8-a_shared_29/libc.so")
 	// ... and not linking to the non-stub (impl) variant
-	ensureNotContains(t, mylibLdFlags, "libc/android_arm64_armv8-a_core_shared_myapex/libc.so")
+	ensureNotContains(t, mylibLdFlags, "libc/android_arm64_armv8-a_shared/libc.so")
 	// ... Cflags from stub is correctly exported to mylib
 	ensureContains(t, mylibCFlags, "__LIBC_API__=29")
 	ensureContains(t, mylibSharedCFlags, "__LIBC_API__=29")
 
 	// For dependency to libm
 	// Ensure that mylib is linking with the non-stub (impl) variant
-	ensureContains(t, mylibLdFlags, "libm/android_arm64_armv8-a_core_shared_myapex/libm.so")
+	ensureContains(t, mylibLdFlags, "libm/android_arm64_armv8-a_shared_myapex/libm.so")
 	// ... and not linking to the stub variant
-	ensureNotContains(t, mylibLdFlags, "libm/android_arm64_armv8-a_core_shared_29_myapex/libm.so")
+	ensureNotContains(t, mylibLdFlags, "libm/android_arm64_armv8-a_shared_29/libm.so")
 	// ... and is not compiling with the stub
 	ensureNotContains(t, mylibCFlags, "__LIBM_API__=29")
 	ensureNotContains(t, mylibSharedCFlags, "__LIBM_API__=29")
 
 	// For dependency to libdl
 	// Ensure that mylib is linking with the specified version of stubs
-	ensureContains(t, mylibLdFlags, "libdl/android_arm64_armv8-a_core_shared_27_myapex/libdl.so")
+	ensureContains(t, mylibLdFlags, "libdl/android_arm64_armv8-a_shared_27/libdl.so")
 	// ... and not linking to the other versions of stubs
-	ensureNotContains(t, mylibLdFlags, "libdl/android_arm64_armv8-a_core_shared_28_myapex/libdl.so")
-	ensureNotContains(t, mylibLdFlags, "libdl/android_arm64_armv8-a_core_shared_29_myapex/libdl.so")
+	ensureNotContains(t, mylibLdFlags, "libdl/android_arm64_armv8-a_shared_28/libdl.so")
+	ensureNotContains(t, mylibLdFlags, "libdl/android_arm64_armv8-a_shared_29/libdl.so")
 	// ... and not linking to the non-stub (impl) variant
-	ensureNotContains(t, mylibLdFlags, "libdl/android_arm64_armv8-a_core_shared_myapex/libdl.so")
+	ensureNotContains(t, mylibLdFlags, "libdl/android_arm64_armv8-a_shared_myapex/libdl.so")
 	// ... Cflags from stub is correctly exported to mylib
 	ensureContains(t, mylibCFlags, "__LIBDL_API__=27")
 	ensureContains(t, mylibSharedCFlags, "__LIBDL_API__=27")
 
 	// Ensure that libBootstrap is depending on the platform variant of bionic libs
-	libFlags := ctx.ModuleForTests("libBootstrap", "android_arm64_armv8-a_core_shared").Rule("ld").Args["libFlags"]
-	ensureContains(t, libFlags, "libc/android_arm64_armv8-a_core_shared/libc.so")
-	ensureContains(t, libFlags, "libm/android_arm64_armv8-a_core_shared/libm.so")
-	ensureContains(t, libFlags, "libdl/android_arm64_armv8-a_core_shared/libdl.so")
+	libFlags := ctx.ModuleForTests("libBootstrap", "android_arm64_armv8-a_shared").Rule("ld").Args["libFlags"]
+	ensureContains(t, libFlags, "libc/android_arm64_armv8-a_shared/libc.so")
+	ensureContains(t, libFlags, "libm/android_arm64_armv8-a_shared/libm.so")
+	ensureContains(t, libFlags, "libdl/android_arm64_armv8-a_shared/libdl.so")
 }
 
 func TestFilesInSubDir(t *testing.T) {
-	ctx := testApex(t, `
+	ctx, _ := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
@@ -717,7 +1099,7 @@
 		}
 	`)
 
-	generateFsRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("generateFsConfig")
+	generateFsRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("generateFsConfig")
 	dirs := strings.Split(generateFsRule.Args["exec_paths"], " ")
 
 	// Ensure that the subdirectories are all listed
@@ -737,7 +1119,7 @@
 }
 
 func TestUseVendor(t *testing.T) {
-	ctx := testApex(t, `
+	ctx, _ := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
@@ -767,10 +1149,12 @@
 			vendor_available: true,
 			stl: "none",
 		}
-	`)
+	`, func(fs map[string][]byte, config android.Config) {
+		setUseVendorWhitelistForTest(config, []string{"myapex"})
+	})
 
 	inputsList := []string{}
-	for _, i := range ctx.ModuleForTests("myapex", "android_common_myapex").Module().BuildParamsForTests() {
+	for _, i := range ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().BuildParamsForTests() {
 		for _, implicit := range i.Implicits {
 			inputsList = append(inputsList, implicit.String())
 		}
@@ -778,16 +1162,72 @@
 	inputsString := strings.Join(inputsList, " ")
 
 	// ensure that the apex includes vendor variants of the direct and indirect deps
-	ensureContains(t, inputsString, "android_arm64_armv8-a_vendor_shared_myapex/mylib.so")
-	ensureContains(t, inputsString, "android_arm64_armv8-a_vendor_shared_myapex/mylib2.so")
+	ensureContains(t, inputsString, "android_vendor.VER_arm64_armv8-a_shared_myapex/mylib.so")
+	ensureContains(t, inputsString, "android_vendor.VER_arm64_armv8-a_shared_myapex/mylib2.so")
 
 	// ensure that the apex does not include core variants
-	ensureNotContains(t, inputsString, "android_arm64_armv8-a_core_shared_myapex/mylib.so")
-	ensureNotContains(t, inputsString, "android_arm64_armv8-a_core_shared_myapex/mylib2.so")
+	ensureNotContains(t, inputsString, "android_arm64_armv8-a_shared_myapex/mylib.so")
+	ensureNotContains(t, inputsString, "android_arm64_armv8-a_shared_myapex/mylib2.so")
+}
+
+func TestUseVendorRestriction(t *testing.T) {
+	testApexError(t, `module "myapex" .*: use_vendor: not allowed`, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			use_vendor: true,
+		}
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+	`, func(fs map[string][]byte, config android.Config) {
+		setUseVendorWhitelistForTest(config, []string{""})
+	})
+	// no error with whitelist
+	testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			use_vendor: true,
+		}
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+	`, func(fs map[string][]byte, config android.Config) {
+		setUseVendorWhitelistForTest(config, []string{"myapex"})
+	})
+}
+
+func TestUseVendorFailsIfNotVendorAvailable(t *testing.T) {
+	testApexError(t, `dependency "mylib" of "myapex" missing variant:\n.*image:vendor`, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			native_shared_libs: ["mylib"],
+			use_vendor: true,
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "mylib",
+			srcs: ["mylib.cpp"],
+			system_shared_libs: [],
+			stl: "none",
+		}
+	`)
 }
 
 func TestStaticLinking(t *testing.T) {
-	ctx := testApex(t, `
+	ctx, _ := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
@@ -820,19 +1260,20 @@
 		}
 	`)
 
-	ldFlags := ctx.ModuleForTests("not_in_apex", "android_arm64_armv8-a_core").Rule("ld").Args["libFlags"]
+	ldFlags := ctx.ModuleForTests("not_in_apex", "android_arm64_armv8-a").Rule("ld").Args["libFlags"]
 
 	// Ensure that not_in_apex is linking with the static variant of mylib
-	ensureContains(t, ldFlags, "mylib/android_arm64_armv8-a_core_static/mylib.a")
+	ensureContains(t, ldFlags, "mylib/android_arm64_armv8-a_static/mylib.a")
 }
 
 func TestKeys(t *testing.T) {
-	ctx := testApex(t, `
+	ctx, _ := testApex(t, `
 		apex {
 			name: "myapex_keytest",
 			key: "myapex.key",
 			certificate: ":myapex.certificate",
 			native_shared_libs: ["mylib"],
+			file_contexts: ":myapex-file_contexts",
 		}
 
 		cc_library {
@@ -873,15 +1314,143 @@
 	}
 
 	// check the APK certs. It should be overridden to myapex.certificate.override
-	certs := ctx.ModuleForTests("myapex_keytest", "android_common_myapex_keytest").Rule("signapk").Args["certificates"]
+	certs := ctx.ModuleForTests("myapex_keytest", "android_common_myapex_keytest_image").Rule("signapk").Args["certificates"]
 	if certs != "testkey.override.x509.pem testkey.override.pk8" {
 		t.Errorf("cert and private key %q are not %q", certs,
 			"testkey.override.509.pem testkey.override.pk8")
 	}
 }
 
+func TestCertificate(t *testing.T) {
+	t.Run("if unspecified, it defaults to DefaultAppCertificate", func(t *testing.T) {
+		ctx, _ := testApex(t, `
+			apex {
+				name: "myapex",
+				key: "myapex.key",
+			}
+			apex_key {
+				name: "myapex.key",
+				public_key: "testkey.avbpubkey",
+				private_key: "testkey.pem",
+			}`)
+		rule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("signapk")
+		expected := "vendor/foo/devkeys/test.x509.pem vendor/foo/devkeys/test.pk8"
+		if actual := rule.Args["certificates"]; actual != expected {
+			t.Errorf("certificates should be %q, not %q", expected, actual)
+		}
+	})
+	t.Run("override when unspecified", func(t *testing.T) {
+		ctx, _ := testApex(t, `
+			apex {
+				name: "myapex_keytest",
+				key: "myapex.key",
+				file_contexts: ":myapex-file_contexts",
+			}
+			apex_key {
+				name: "myapex.key",
+				public_key: "testkey.avbpubkey",
+				private_key: "testkey.pem",
+			}
+			android_app_certificate {
+				name: "myapex.certificate.override",
+				certificate: "testkey.override",
+			}`)
+		rule := ctx.ModuleForTests("myapex_keytest", "android_common_myapex_keytest_image").Rule("signapk")
+		expected := "testkey.override.x509.pem testkey.override.pk8"
+		if actual := rule.Args["certificates"]; actual != expected {
+			t.Errorf("certificates should be %q, not %q", expected, actual)
+		}
+	})
+	t.Run("if specified as :module, it respects the prop", func(t *testing.T) {
+		ctx, _ := testApex(t, `
+			apex {
+				name: "myapex",
+				key: "myapex.key",
+				certificate: ":myapex.certificate",
+			}
+			apex_key {
+				name: "myapex.key",
+				public_key: "testkey.avbpubkey",
+				private_key: "testkey.pem",
+			}
+			android_app_certificate {
+				name: "myapex.certificate",
+				certificate: "testkey",
+			}`)
+		rule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("signapk")
+		expected := "testkey.x509.pem testkey.pk8"
+		if actual := rule.Args["certificates"]; actual != expected {
+			t.Errorf("certificates should be %q, not %q", expected, actual)
+		}
+	})
+	t.Run("override when specifiec as <:module>", func(t *testing.T) {
+		ctx, _ := testApex(t, `
+			apex {
+				name: "myapex_keytest",
+				key: "myapex.key",
+				file_contexts: ":myapex-file_contexts",
+				certificate: ":myapex.certificate",
+			}
+			apex_key {
+				name: "myapex.key",
+				public_key: "testkey.avbpubkey",
+				private_key: "testkey.pem",
+			}
+			android_app_certificate {
+				name: "myapex.certificate.override",
+				certificate: "testkey.override",
+			}`)
+		rule := ctx.ModuleForTests("myapex_keytest", "android_common_myapex_keytest_image").Rule("signapk")
+		expected := "testkey.override.x509.pem testkey.override.pk8"
+		if actual := rule.Args["certificates"]; actual != expected {
+			t.Errorf("certificates should be %q, not %q", expected, actual)
+		}
+	})
+	t.Run("if specified as name, finds it from DefaultDevKeyDir", func(t *testing.T) {
+		ctx, _ := testApex(t, `
+			apex {
+				name: "myapex",
+				key: "myapex.key",
+				certificate: "testkey",
+			}
+			apex_key {
+				name: "myapex.key",
+				public_key: "testkey.avbpubkey",
+				private_key: "testkey.pem",
+			}`)
+		rule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("signapk")
+		expected := "vendor/foo/devkeys/testkey.x509.pem vendor/foo/devkeys/testkey.pk8"
+		if actual := rule.Args["certificates"]; actual != expected {
+			t.Errorf("certificates should be %q, not %q", expected, actual)
+		}
+	})
+	t.Run("override when specified as <name>", func(t *testing.T) {
+		ctx, _ := testApex(t, `
+			apex {
+				name: "myapex_keytest",
+				key: "myapex.key",
+				file_contexts: ":myapex-file_contexts",
+				certificate: "testkey",
+			}
+			apex_key {
+				name: "myapex.key",
+				public_key: "testkey.avbpubkey",
+				private_key: "testkey.pem",
+			}
+			android_app_certificate {
+				name: "myapex.certificate.override",
+				certificate: "testkey.override",
+			}`)
+		rule := ctx.ModuleForTests("myapex_keytest", "android_common_myapex_keytest_image").Rule("signapk")
+		expected := "testkey.override.x509.pem testkey.override.pk8"
+		if actual := rule.Args["certificates"]; actual != expected {
+			t.Errorf("certificates should be %q, not %q", expected, actual)
+		}
+	})
+}
+
 func TestMacro(t *testing.T) {
-	ctx := testApex(t, `
+	ctx, _ := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
@@ -908,24 +1477,27 @@
 		}
 	`)
 
-	// non-APEX variant does not have __ANDROID__APEX__ defined
-	mylibCFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_core_static").Rule("cc").Args["cFlags"]
-	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__=myapex")
-	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__=otherapex")
+	// non-APEX variant does not have __ANDROID_APEX(_NAME)__ defined
+	mylibCFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
+	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__")
+	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_MYAPEX__")
+	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_OTHERAPEX__")
 
-	// APEX variant has __ANDROID_APEX__=<apexname> defined
-	mylibCFlags = ctx.ModuleForTests("mylib", "android_arm64_armv8-a_core_static_myapex").Rule("cc").Args["cFlags"]
-	ensureContains(t, mylibCFlags, "-D__ANDROID_APEX__=myapex")
-	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__=otherapex")
+	// APEX variant has __ANDROID_APEX(_NAME)__ defined
+	mylibCFlags = ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static_myapex").Rule("cc").Args["cFlags"]
+	ensureContains(t, mylibCFlags, "-D__ANDROID_APEX__")
+	ensureContains(t, mylibCFlags, "-D__ANDROID_APEX_MYAPEX__")
+	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_OTHERAPEX__")
 
-	// APEX variant has __ANDROID_APEX__=<apexname> defined
-	mylibCFlags = ctx.ModuleForTests("mylib", "android_arm64_armv8-a_core_static_otherapex").Rule("cc").Args["cFlags"]
-	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__=myapex")
-	ensureContains(t, mylibCFlags, "-D__ANDROID_APEX__=otherapex")
+	// APEX variant has __ANDROID_APEX(_NAME)__ defined
+	mylibCFlags = ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static_otherapex").Rule("cc").Args["cFlags"]
+	ensureContains(t, mylibCFlags, "-D__ANDROID_APEX__")
+	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_MYAPEX__")
+	ensureContains(t, mylibCFlags, "-D__ANDROID_APEX_OTHERAPEX__")
 }
 
 func TestHeaderLibsDependency(t *testing.T) {
-	ctx := testApex(t, `
+	ctx, _ := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
@@ -966,14 +1538,599 @@
 		}
 	`)
 
-	cFlags := ctx.ModuleForTests("otherlib", "android_arm64_armv8-a_core_static").Rule("cc").Args["cFlags"]
+	cFlags := ctx.ModuleForTests("otherlib", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
 
 	// Ensure that the include path of the header lib is exported to 'otherlib'
 	ensureContains(t, cFlags, "-Imy_include")
 }
 
+func ensureExactContents(t *testing.T, ctx *android.TestContext, moduleName string, files []string) {
+	t.Helper()
+	apexRule := ctx.ModuleForTests(moduleName, "android_common_"+moduleName+"_image").Rule("apexRule")
+	copyCmds := apexRule.Args["copy_commands"]
+	imageApexDir := "/image.apex/"
+	var failed bool
+	var surplus []string
+	filesMatched := make(map[string]bool)
+	addContent := func(content string) {
+		for _, expected := range files {
+			if matched, _ := path.Match(expected, content); matched {
+				filesMatched[expected] = true
+				return
+			}
+		}
+		surplus = append(surplus, content)
+	}
+	for _, cmd := range strings.Split(copyCmds, "&&") {
+		cmd = strings.TrimSpace(cmd)
+		if cmd == "" {
+			continue
+		}
+		terms := strings.Split(cmd, " ")
+		switch terms[0] {
+		case "mkdir":
+		case "cp":
+			if len(terms) != 3 {
+				t.Fatal("copyCmds contains invalid cp command", cmd)
+			}
+			dst := terms[2]
+			index := strings.Index(dst, imageApexDir)
+			if index == -1 {
+				t.Fatal("copyCmds should copy a file to image.apex/", cmd)
+			}
+			dstFile := dst[index+len(imageApexDir):]
+			addContent(dstFile)
+		default:
+			t.Fatalf("copyCmds should contain mkdir/cp commands only: %q", cmd)
+		}
+	}
+
+	if len(surplus) > 0 {
+		sort.Strings(surplus)
+		t.Log("surplus files", surplus)
+		failed = true
+	}
+
+	if len(files) > len(filesMatched) {
+		var missing []string
+		for _, expected := range files {
+			if !filesMatched[expected] {
+				missing = append(missing, expected)
+			}
+		}
+		sort.Strings(missing)
+		t.Log("missing files", missing)
+		failed = true
+	}
+	if failed {
+		t.Fail()
+	}
+}
+
+func TestVndkApexCurrent(t *testing.T) {
+	ctx, _ := testApex(t, `
+		apex_vndk {
+			name: "myapex",
+			key: "myapex.key",
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "libvndk",
+			srcs: ["mylib.cpp"],
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+			},
+			system_shared_libs: [],
+			stl: "none",
+		}
+
+		cc_library {
+			name: "libvndksp",
+			srcs: ["mylib.cpp"],
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+				support_system_process: true,
+			},
+			system_shared_libs: [],
+			stl: "none",
+		}
+	`+vndkLibrariesTxtFiles("current"))
+
+	ensureExactContents(t, ctx, "myapex", []string{
+		"lib/libvndk.so",
+		"lib/libvndksp.so",
+		"lib64/libvndk.so",
+		"lib64/libvndksp.so",
+		"etc/llndk.libraries.VER.txt",
+		"etc/vndkcore.libraries.VER.txt",
+		"etc/vndksp.libraries.VER.txt",
+		"etc/vndkprivate.libraries.VER.txt",
+	})
+}
+
+func TestVndkApexWithPrebuilt(t *testing.T) {
+	ctx, _ := testApex(t, `
+		apex_vndk {
+			name: "myapex",
+			key: "myapex.key",
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_prebuilt_library_shared {
+			name: "libvndk",
+			srcs: ["libvndk.so"],
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+			},
+			system_shared_libs: [],
+			stl: "none",
+		}
+
+		cc_prebuilt_library_shared {
+			name: "libvndk.arm",
+			srcs: ["libvndk.arm.so"],
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+			},
+			enabled: false,
+			arch: {
+				arm: {
+					enabled: true,
+				},
+			},
+			system_shared_libs: [],
+			stl: "none",
+		}
+		`+vndkLibrariesTxtFiles("current"),
+		withFiles(map[string][]byte{
+			"libvndk.so":     nil,
+			"libvndk.arm.so": nil,
+		}))
+
+	ensureExactContents(t, ctx, "myapex", []string{
+		"lib/libvndk.so",
+		"lib/libvndk.arm.so",
+		"lib64/libvndk.so",
+		"etc/*",
+	})
+}
+
+func vndkLibrariesTxtFiles(vers ...string) (result string) {
+	for _, v := range vers {
+		if v == "current" {
+			for _, txt := range []string{"llndk", "vndkcore", "vndksp", "vndkprivate"} {
+				result += `
+					vndk_libraries_txt {
+						name: "` + txt + `.libraries.txt",
+					}
+				`
+			}
+		} else {
+			for _, txt := range []string{"llndk", "vndkcore", "vndksp", "vndkprivate"} {
+				result += `
+					prebuilt_etc {
+						name: "` + txt + `.libraries.` + v + `.txt",
+						src: "dummy.txt",
+					}
+				`
+			}
+		}
+	}
+	return
+}
+
+func TestVndkApexVersion(t *testing.T) {
+	ctx, _ := testApex(t, `
+		apex_vndk {
+			name: "myapex_v27",
+			key: "myapex.key",
+			file_contexts: ":myapex-file_contexts",
+			vndk_version: "27",
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		vndk_prebuilt_shared {
+			name: "libvndk27",
+			version: "27",
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+			},
+			target_arch: "arm64",
+			arch: {
+				arm: {
+					srcs: ["libvndk27_arm.so"],
+				},
+				arm64: {
+					srcs: ["libvndk27_arm64.so"],
+				},
+			},
+		}
+
+		vndk_prebuilt_shared {
+			name: "libvndk27",
+			version: "27",
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+			},
+			target_arch: "x86_64",
+			arch: {
+				x86: {
+					srcs: ["libvndk27_x86.so"],
+				},
+				x86_64: {
+					srcs: ["libvndk27_x86_64.so"],
+				},
+			},
+		}
+		`+vndkLibrariesTxtFiles("27"),
+		withFiles(map[string][]byte{
+			"libvndk27_arm.so":    nil,
+			"libvndk27_arm64.so":  nil,
+			"libvndk27_x86.so":    nil,
+			"libvndk27_x86_64.so": nil,
+		}))
+
+	ensureExactContents(t, ctx, "myapex_v27", []string{
+		"lib/libvndk27_arm.so",
+		"lib64/libvndk27_arm64.so",
+		"etc/*",
+	})
+}
+
+func TestVndkApexErrorWithDuplicateVersion(t *testing.T) {
+	testApexError(t, `module "myapex_v27.*" .*: vndk_version: 27 is already defined in "myapex_v27.*"`, `
+		apex_vndk {
+			name: "myapex_v27",
+			key: "myapex.key",
+			file_contexts: ":myapex-file_contexts",
+			vndk_version: "27",
+		}
+		apex_vndk {
+			name: "myapex_v27_other",
+			key: "myapex.key",
+			file_contexts: ":myapex-file_contexts",
+			vndk_version: "27",
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "libvndk",
+			srcs: ["mylib.cpp"],
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+			},
+			system_shared_libs: [],
+			stl: "none",
+		}
+
+		vndk_prebuilt_shared {
+			name: "libvndk",
+			version: "27",
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+			},
+			srcs: ["libvndk.so"],
+		}
+	`, withFiles(map[string][]byte{
+		"libvndk.so": nil,
+	}))
+}
+
+func TestVndkApexNameRule(t *testing.T) {
+	ctx, _ := testApex(t, `
+		apex_vndk {
+			name: "myapex",
+			key: "myapex.key",
+			file_contexts: ":myapex-file_contexts",
+		}
+		apex_vndk {
+			name: "myapex_v28",
+			key: "myapex.key",
+			file_contexts: ":myapex-file_contexts",
+			vndk_version: "28",
+		}
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}`+vndkLibrariesTxtFiles("28", "current"))
+
+	assertApexName := func(expected, moduleName string) {
+		bundle := ctx.ModuleForTests(moduleName, "android_common_"+moduleName+"_image").Module().(*apexBundle)
+		actual := proptools.String(bundle.properties.Apex_name)
+		if !reflect.DeepEqual(actual, expected) {
+			t.Errorf("Got '%v', expected '%v'", actual, expected)
+		}
+	}
+
+	assertApexName("com.android.vndk.vVER", "myapex")
+	assertApexName("com.android.vndk.v28", "myapex_v28")
+}
+
+func TestVndkApexSkipsNativeBridgeSupportedModules(t *testing.T) {
+	ctx, _ := testApex(t, `
+		apex_vndk {
+			name: "myapex",
+			key: "myapex.key",
+			file_contexts: ":myapex-file_contexts",
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "libvndk",
+			srcs: ["mylib.cpp"],
+			vendor_available: true,
+			native_bridge_supported: true,
+			host_supported: true,
+			vndk: {
+				enabled: true,
+			},
+			system_shared_libs: [],
+			stl: "none",
+		}
+		`+vndkLibrariesTxtFiles("current"),
+		withTargets(map[android.OsType][]android.Target{
+			android.Android: []android.Target{
+				{Os: android.Android, Arch: android.Arch{ArchType: android.Arm64, ArchVariant: "armv8-a", Abi: []string{"arm64-v8a"}}, NativeBridge: android.NativeBridgeDisabled, NativeBridgeHostArchName: "", NativeBridgeRelativePath: ""},
+				{Os: android.Android, Arch: android.Arch{ArchType: android.Arm, ArchVariant: "armv7-a-neon", Abi: []string{"armeabi-v7a"}}, NativeBridge: android.NativeBridgeDisabled, NativeBridgeHostArchName: "", NativeBridgeRelativePath: ""},
+				{Os: android.Android, Arch: android.Arch{ArchType: android.X86_64, ArchVariant: "silvermont", Abi: []string{"arm64-v8a"}}, NativeBridge: android.NativeBridgeEnabled, NativeBridgeHostArchName: "arm64", NativeBridgeRelativePath: "x86_64"},
+				{Os: android.Android, Arch: android.Arch{ArchType: android.X86, ArchVariant: "silvermont", Abi: []string{"armeabi-v7a"}}, NativeBridge: android.NativeBridgeEnabled, NativeBridgeHostArchName: "arm", NativeBridgeRelativePath: "x86"},
+			},
+		}))
+
+	ensureExactContents(t, ctx, "myapex", []string{
+		"lib/libvndk.so",
+		"lib64/libvndk.so",
+		"etc/*",
+	})
+}
+
+func TestVndkApexDoesntSupportNativeBridgeSupported(t *testing.T) {
+	testApexError(t, `module "myapex" .*: native_bridge_supported: .* doesn't support native bridge binary`, `
+		apex_vndk {
+			name: "myapex",
+			key: "myapex.key",
+			file_contexts: ":myapex-file_contexts",
+			native_bridge_supported: true,
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "libvndk",
+			srcs: ["mylib.cpp"],
+			vendor_available: true,
+			native_bridge_supported: true,
+			host_supported: true,
+			vndk: {
+				enabled: true,
+			},
+			system_shared_libs: [],
+			stl: "none",
+		}
+	`)
+}
+
+func TestVndkApexWithBinder32(t *testing.T) {
+	ctx, _ := testApex(t, `
+		apex_vndk {
+			name: "myapex_v27",
+			key: "myapex.key",
+			file_contexts: ":myapex-file_contexts",
+			vndk_version: "27",
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		vndk_prebuilt_shared {
+			name: "libvndk27",
+			version: "27",
+			target_arch: "arm",
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+			},
+			arch: {
+				arm: {
+					srcs: ["libvndk27.so"],
+				}
+			},
+		}
+
+		vndk_prebuilt_shared {
+			name: "libvndk27",
+			version: "27",
+			target_arch: "arm",
+			binder32bit: true,
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+			},
+			arch: {
+				arm: {
+					srcs: ["libvndk27binder32.so"],
+				}
+			},
+		}
+		`+vndkLibrariesTxtFiles("27"),
+		withFiles(map[string][]byte{
+			"libvndk27.so":         nil,
+			"libvndk27binder32.so": nil,
+		}),
+		withBinder32bit,
+		withTargets(map[android.OsType][]android.Target{
+			android.Android: []android.Target{
+				{Os: android.Android, Arch: android.Arch{ArchType: android.Arm, ArchVariant: "armv7-a-neon", Abi: []string{"armeabi-v7a"}}, NativeBridge: android.NativeBridgeDisabled, NativeBridgeHostArchName: "", NativeBridgeRelativePath: ""},
+			},
+		}),
+	)
+
+	ensureExactContents(t, ctx, "myapex_v27", []string{
+		"lib/libvndk27binder32.so",
+		"etc/*",
+	})
+}
+
+func TestDependenciesInApexManifest(t *testing.T) {
+	ctx, _ := testApex(t, `
+		apex {
+			name: "myapex_nodep",
+			key: "myapex.key",
+			native_shared_libs: ["lib_nodep"],
+			compile_multilib: "both",
+			file_contexts: ":myapex-file_contexts",
+		}
+
+		apex {
+			name: "myapex_dep",
+			key: "myapex.key",
+			native_shared_libs: ["lib_dep"],
+			compile_multilib: "both",
+			file_contexts: ":myapex-file_contexts",
+		}
+
+		apex {
+			name: "myapex_provider",
+			key: "myapex.key",
+			native_shared_libs: ["libfoo"],
+			compile_multilib: "both",
+			file_contexts: ":myapex-file_contexts",
+		}
+
+		apex {
+			name: "myapex_selfcontained",
+			key: "myapex.key",
+			native_shared_libs: ["lib_dep", "libfoo"],
+			compile_multilib: "both",
+			file_contexts: ":myapex-file_contexts",
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "lib_nodep",
+			srcs: ["mylib.cpp"],
+			system_shared_libs: [],
+			stl: "none",
+		}
+
+		cc_library {
+			name: "lib_dep",
+			srcs: ["mylib.cpp"],
+			shared_libs: ["libfoo"],
+			system_shared_libs: [],
+			stl: "none",
+		}
+
+		cc_library {
+			name: "libfoo",
+			srcs: ["mytest.cpp"],
+			stubs: {
+				versions: ["1"],
+			},
+			system_shared_libs: [],
+			stl: "none",
+		}
+	`)
+
+	var apexManifestRule android.TestingBuildParams
+	var provideNativeLibs, requireNativeLibs []string
+
+	apexManifestRule = ctx.ModuleForTests("myapex_nodep", "android_common_myapex_nodep_image").Rule("apexManifestRule")
+	provideNativeLibs = names(apexManifestRule.Args["provideNativeLibs"])
+	requireNativeLibs = names(apexManifestRule.Args["requireNativeLibs"])
+	ensureListEmpty(t, provideNativeLibs)
+	ensureListEmpty(t, requireNativeLibs)
+
+	apexManifestRule = ctx.ModuleForTests("myapex_dep", "android_common_myapex_dep_image").Rule("apexManifestRule")
+	provideNativeLibs = names(apexManifestRule.Args["provideNativeLibs"])
+	requireNativeLibs = names(apexManifestRule.Args["requireNativeLibs"])
+	ensureListEmpty(t, provideNativeLibs)
+	ensureListContains(t, requireNativeLibs, "libfoo.so")
+
+	apexManifestRule = ctx.ModuleForTests("myapex_provider", "android_common_myapex_provider_image").Rule("apexManifestRule")
+	provideNativeLibs = names(apexManifestRule.Args["provideNativeLibs"])
+	requireNativeLibs = names(apexManifestRule.Args["requireNativeLibs"])
+	ensureListContains(t, provideNativeLibs, "libfoo.so")
+	ensureListEmpty(t, requireNativeLibs)
+
+	apexManifestRule = ctx.ModuleForTests("myapex_selfcontained", "android_common_myapex_selfcontained_image").Rule("apexManifestRule")
+	provideNativeLibs = names(apexManifestRule.Args["provideNativeLibs"])
+	requireNativeLibs = names(apexManifestRule.Args["requireNativeLibs"])
+	ensureListContains(t, provideNativeLibs, "libfoo.so")
+	ensureListEmpty(t, requireNativeLibs)
+}
+
+func TestApexName(t *testing.T) {
+	ctx, _ := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			apex_name: "com.android.myapex",
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+	`)
+
+	module := ctx.ModuleForTests("myapex", "android_common_myapex_image")
+	apexManifestRule := module.Rule("apexManifestRule")
+	ensureContains(t, apexManifestRule.Args["opt"], "-v name com.android.myapex")
+	apexRule := module.Rule("apexRule")
+	ensureContains(t, apexRule.Args["opt_flags"], "--do_not_check_keyname")
+}
+
 func TestNonTestApex(t *testing.T) {
-	ctx := testApex(t, `
+	ctx, _ := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
@@ -994,7 +2151,7 @@
 		}
 	`)
 
-	module := ctx.ModuleForTests("myapex", "android_common_myapex")
+	module := ctx.ModuleForTests("myapex", "android_common_myapex_image")
 	apexRule := module.Rule("apexRule")
 	copyCmds := apexRule.Args["copy_commands"]
 
@@ -1006,13 +2163,13 @@
 	ensureContains(t, apexRule.Output.String(), "myapex.apex.unsigned")
 
 	// Ensure that apex variant is created for the direct dep
-	ensureListContains(t, ctx.ModuleVariantsForTests("mylib_common"), "android_arm64_armv8-a_core_shared_myapex")
+	ensureListContains(t, ctx.ModuleVariantsForTests("mylib_common"), "android_arm64_armv8-a_shared_myapex")
 
 	// Ensure that both direct and indirect deps are copied into apex
 	ensureContains(t, copyCmds, "image.apex/lib64/mylib_common.so")
 
-	// Ensure that the platform variant ends with _core_shared
-	ensureListContains(t, ctx.ModuleVariantsForTests("mylib_common"), "android_arm64_armv8-a_core_shared")
+	// Ensure that the platform variant ends with _shared
+	ensureListContains(t, ctx.ModuleVariantsForTests("mylib_common"), "android_arm64_armv8-a_shared")
 
 	if !android.InAnyApex("mylib_common") {
 		t.Log("Found mylib_common not in any apex!")
@@ -1024,7 +2181,7 @@
 	if android.InAnyApex("mylib_common_test") {
 		t.Fatal("mylib_common_test must not be used in any other tests since this checks that global state is not updated in an illegal way!")
 	}
-	ctx := testApex(t, `
+	ctx, _ := testApex(t, `
 		apex_test {
 			name: "myapex",
 			key: "myapex.key",
@@ -1045,7 +2202,7 @@
 		}
 	`)
 
-	module := ctx.ModuleForTests("myapex", "android_common_myapex")
+	module := ctx.ModuleForTests("myapex", "android_common_myapex_image")
 	apexRule := module.Rule("apexRule")
 	copyCmds := apexRule.Args["copy_commands"]
 
@@ -1057,13 +2214,13 @@
 	ensureContains(t, apexRule.Output.String(), "myapex.apex.unsigned")
 
 	// Ensure that apex variant is created for the direct dep
-	ensureListContains(t, ctx.ModuleVariantsForTests("mylib_common_test"), "android_arm64_armv8-a_core_shared_myapex")
+	ensureListContains(t, ctx.ModuleVariantsForTests("mylib_common_test"), "android_arm64_armv8-a_shared_myapex")
 
 	// Ensure that both direct and indirect deps are copied into apex
 	ensureContains(t, copyCmds, "image.apex/lib64/mylib_common_test.so")
 
-	// Ensure that the platform variant ends with _core_shared
-	ensureListContains(t, ctx.ModuleVariantsForTests("mylib_common_test"), "android_arm64_armv8-a_core_shared")
+	// Ensure that the platform variant ends with _shared
+	ensureListContains(t, ctx.ModuleVariantsForTests("mylib_common_test"), "android_arm64_armv8-a_shared")
 
 	if android.InAnyApex("mylib_common_test") {
 		t.Log("Found mylib_common_test in some apex!")
@@ -1072,7 +2229,7 @@
 }
 
 func TestApexWithTarget(t *testing.T) {
-	ctx := testApex(t, `
+	ctx, _ := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
@@ -1129,30 +2286,30 @@
 		}
 	`)
 
-	apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule")
+	apexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexRule")
 	copyCmds := apexRule.Args["copy_commands"]
 
 	// Ensure that main rule creates an output
 	ensureContains(t, apexRule.Output.String(), "myapex.apex.unsigned")
 
 	// Ensure that apex variant is created for the direct dep
-	ensureListContains(t, ctx.ModuleVariantsForTests("mylib"), "android_arm64_armv8-a_core_shared_myapex")
-	ensureListContains(t, ctx.ModuleVariantsForTests("mylib_common"), "android_arm64_armv8-a_core_shared_myapex")
-	ensureListNotContains(t, ctx.ModuleVariantsForTests("mylib2"), "android_arm64_armv8-a_core_shared_myapex")
+	ensureListContains(t, ctx.ModuleVariantsForTests("mylib"), "android_arm64_armv8-a_shared_myapex")
+	ensureListContains(t, ctx.ModuleVariantsForTests("mylib_common"), "android_arm64_armv8-a_shared_myapex")
+	ensureListNotContains(t, ctx.ModuleVariantsForTests("mylib2"), "android_arm64_armv8-a_shared_myapex")
 
 	// 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/mylib_common.so")
 	ensureNotContains(t, copyCmds, "image.apex/lib64/mylib2.so")
 
-	// Ensure that the platform variant ends with _core_shared
-	ensureListContains(t, ctx.ModuleVariantsForTests("mylib"), "android_arm64_armv8-a_core_shared")
-	ensureListContains(t, ctx.ModuleVariantsForTests("mylib_common"), "android_arm64_armv8-a_core_shared")
-	ensureListContains(t, ctx.ModuleVariantsForTests("mylib2"), "android_arm64_armv8-a_core_shared")
+	// Ensure that the platform variant ends with _shared
+	ensureListContains(t, ctx.ModuleVariantsForTests("mylib"), "android_arm64_armv8-a_shared")
+	ensureListContains(t, ctx.ModuleVariantsForTests("mylib_common"), "android_arm64_armv8-a_shared")
+	ensureListContains(t, ctx.ModuleVariantsForTests("mylib2"), "android_arm64_armv8-a_shared")
 }
 
 func TestApexWithShBinary(t *testing.T) {
-	ctx := testApex(t, `
+	ctx, _ := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
@@ -1173,46 +2330,164 @@
 		}
 	`)
 
-	apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule")
+	apexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexRule")
 	copyCmds := apexRule.Args["copy_commands"]
 
 	ensureContains(t, copyCmds, "image.apex/bin/script/myscript.sh")
 }
 
-func TestApexInProductPartition(t *testing.T) {
-	ctx := testApex(t, `
-		apex {
-			name: "myapex",
-			key: "myapex.key",
-			native_shared_libs: ["mylib"],
-			product_specific: true,
-		}
+func TestApexInVariousPartition(t *testing.T) {
+	testcases := []struct {
+		propName, parition, flattenedPartition string
+	}{
+		{"", "system", "system_ext"},
+		{"product_specific: true", "product", "product"},
+		{"soc_specific: true", "vendor", "vendor"},
+		{"proprietary: true", "vendor", "vendor"},
+		{"vendor: true", "vendor", "vendor"},
+		{"system_ext_specific: true", "system_ext", "system_ext"},
+	}
+	for _, tc := range testcases {
+		t.Run(tc.propName+":"+tc.parition, func(t *testing.T) {
+			ctx, _ := testApex(t, `
+				apex {
+					name: "myapex",
+					key: "myapex.key",
+					`+tc.propName+`
+				}
 
-		apex_key {
-			name: "myapex.key",
-			public_key: "testkey.avbpubkey",
-			private_key: "testkey.pem",
-			product_specific: true,
-		}
+				apex_key {
+					name: "myapex.key",
+					public_key: "testkey.avbpubkey",
+					private_key: "testkey.pem",
+				}
+			`)
 
-		cc_library {
-			name: "mylib",
-			srcs: ["mylib.cpp"],
-			system_shared_libs: [],
-			stl: "none",
-		}
+			apex := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
+			expected := buildDir + "/target/product/test_device/" + tc.parition + "/apex"
+			actual := apex.installDir.String()
+			if actual != expected {
+				t.Errorf("wrong install path. expected %q. actual %q", expected, actual)
+			}
+
+			flattened := ctx.ModuleForTests("myapex", "android_common_myapex_flattened").Module().(*apexBundle)
+			expected = buildDir + "/target/product/test_device/" + tc.flattenedPartition + "/apex"
+			actual = flattened.installDir.String()
+			if actual != expected {
+				t.Errorf("wrong install path. expected %q. actual %q", expected, actual)
+			}
+		})
+	}
+}
+
+func TestFileContexts(t *testing.T) {
+	ctx, _ := testApex(t, `
+	apex {
+		name: "myapex",
+		key: "myapex.key",
+	}
+
+	apex_key {
+		name: "myapex.key",
+		public_key: "testkey.avbpubkey",
+		private_key: "testkey.pem",
+	}
+	`)
+	module := ctx.ModuleForTests("myapex", "android_common_myapex_image")
+	apexRule := module.Rule("apexRule")
+	actual := apexRule.Args["file_contexts"]
+	expected := "system/sepolicy/apex/myapex-file_contexts"
+	if actual != expected {
+		t.Errorf("wrong file_contexts. expected %q. actual %q", expected, actual)
+	}
+
+	testApexError(t, `"myapex" .*: file_contexts: should be under system/sepolicy`, `
+	apex {
+		name: "myapex",
+		key: "myapex.key",
+		file_contexts: "my_own_file_contexts",
+	}
+
+	apex_key {
+		name: "myapex.key",
+		public_key: "testkey.avbpubkey",
+		private_key: "testkey.pem",
+	}
+	`, withFiles(map[string][]byte{
+		"my_own_file_contexts": nil,
+	}))
+
+	testApexError(t, `"myapex" .*: file_contexts: cannot find`, `
+	apex {
+		name: "myapex",
+		key: "myapex.key",
+		product_specific: true,
+		file_contexts: "product_specific_file_contexts",
+	}
+
+	apex_key {
+		name: "myapex.key",
+		public_key: "testkey.avbpubkey",
+		private_key: "testkey.pem",
+	}
 	`)
 
-	apex := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*apexBundle)
-	expected := "target/product/test_device/product/apex"
-	actual := apex.installDir.RelPathString()
+	ctx, _ = testApex(t, `
+	apex {
+		name: "myapex",
+		key: "myapex.key",
+		product_specific: true,
+		file_contexts: "product_specific_file_contexts",
+	}
+
+	apex_key {
+		name: "myapex.key",
+		public_key: "testkey.avbpubkey",
+		private_key: "testkey.pem",
+	}
+	`, withFiles(map[string][]byte{
+		"product_specific_file_contexts": nil,
+	}))
+	module = ctx.ModuleForTests("myapex", "android_common_myapex_image")
+	apexRule = module.Rule("apexRule")
+	actual = apexRule.Args["file_contexts"]
+	expected = "product_specific_file_contexts"
 	if actual != expected {
-		t.Errorf("wrong install path. expected %q. actual %q", expected, actual)
+		t.Errorf("wrong file_contexts. expected %q. actual %q", expected, actual)
+	}
+
+	ctx, _ = testApex(t, `
+	apex {
+		name: "myapex",
+		key: "myapex.key",
+		product_specific: true,
+		file_contexts: ":my-file-contexts",
+	}
+
+	apex_key {
+		name: "myapex.key",
+		public_key: "testkey.avbpubkey",
+		private_key: "testkey.pem",
+	}
+
+	filegroup {
+		name: "my-file-contexts",
+		srcs: ["product_specific_file_contexts"],
+	}
+	`, withFiles(map[string][]byte{
+		"product_specific_file_contexts": nil,
+	}))
+	module = ctx.ModuleForTests("myapex", "android_common_myapex_image")
+	apexRule = module.Rule("apexRule")
+	actual = apexRule.Args["file_contexts"]
+	expected = "product_specific_file_contexts"
+	if actual != expected {
+		t.Errorf("wrong file_contexts. expected %q. actual %q", expected, actual)
 	}
 }
 
 func TestApexKeyFromOtherModule(t *testing.T) {
-	ctx := testApex(t, `
+	ctx, _ := testApex(t, `
 		apex_key {
 			name: "myapex.key",
 			public_key: ":my.avbpubkey",
@@ -1245,7 +2520,7 @@
 }
 
 func TestPrebuilt(t *testing.T) {
-	ctx := testApex(t, `
+	ctx, _ := testApex(t, `
 		prebuilt_apex {
 			name: "myapex",
 			arch: {
@@ -1268,7 +2543,7 @@
 }
 
 func TestPrebuiltFilenameOverride(t *testing.T) {
-	ctx := testApex(t, `
+	ctx, _ := testApex(t, `
 		prebuilt_apex {
 			name: "myapex",
 			src: "myapex-arm.apex",
@@ -1283,3 +2558,833 @@
 		t.Errorf("installFilename invalid. expected: %q, actual: %q", expected, p.installFilename)
 	}
 }
+
+func TestPrebuiltOverrides(t *testing.T) {
+	ctx, config := testApex(t, `
+		prebuilt_apex {
+			name: "myapex.prebuilt",
+			src: "myapex-arm.apex",
+			overrides: [
+				"myapex",
+			],
+		}
+	`)
+
+	p := ctx.ModuleForTests("myapex.prebuilt", "android_common").Module().(*Prebuilt)
+
+	expected := []string{"myapex"}
+	actual := android.AndroidMkEntriesForTest(t, config, "", p)[0].EntryMap["LOCAL_OVERRIDES_MODULES"]
+	if !reflect.DeepEqual(actual, expected) {
+		t.Errorf("Incorrect LOCAL_OVERRIDES_MODULES value '%s', expected '%s'", actual, expected)
+	}
+}
+
+func TestApexWithTests(t *testing.T) {
+	ctx, config := testApex(t, `
+		apex_test {
+			name: "myapex",
+			key: "myapex.key",
+			tests: [
+				"mytest",
+				"mytests",
+			],
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_test {
+			name: "mytest",
+			gtest: false,
+			srcs: ["mytest.cpp"],
+			relative_install_path: "test",
+			system_shared_libs: [],
+			static_executable: true,
+			stl: "none",
+		}
+
+		cc_test {
+			name: "mytests",
+			gtest: false,
+			srcs: [
+				"mytest1.cpp",
+				"mytest2.cpp",
+				"mytest3.cpp",
+			],
+			test_per_src: true,
+			relative_install_path: "test",
+			system_shared_libs: [],
+			static_executable: true,
+			stl: "none",
+		}
+	`)
+
+	apexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexRule")
+	copyCmds := apexRule.Args["copy_commands"]
+
+	// Ensure that test dep is copied into apex.
+	ensureContains(t, copyCmds, "image.apex/bin/test/mytest")
+
+	// Ensure that test deps built with `test_per_src` are copied into apex.
+	ensureContains(t, copyCmds, "image.apex/bin/test/mytest1")
+	ensureContains(t, copyCmds, "image.apex/bin/test/mytest2")
+	ensureContains(t, copyCmds, "image.apex/bin/test/mytest3")
+
+	// Ensure the module is correctly translated.
+	apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
+	data := android.AndroidMkDataForTest(t, config, "", apexBundle)
+	name := apexBundle.BaseModuleName()
+	prefix := "TARGET_"
+	var builder strings.Builder
+	data.Custom(&builder, name, prefix, "", data)
+	androidMk := builder.String()
+	ensureContains(t, androidMk, "LOCAL_MODULE := mytest.myapex\n")
+	ensureContains(t, androidMk, "LOCAL_MODULE := mytest1.myapex\n")
+	ensureContains(t, androidMk, "LOCAL_MODULE := mytest2.myapex\n")
+	ensureContains(t, androidMk, "LOCAL_MODULE := mytest3.myapex\n")
+	ensureContains(t, androidMk, "LOCAL_MODULE := apex_manifest.pb.myapex\n")
+	ensureContains(t, androidMk, "LOCAL_MODULE := apex_pubkey.myapex\n")
+	ensureContains(t, androidMk, "LOCAL_MODULE := myapex\n")
+}
+
+func TestInstallExtraFlattenedApexes(t *testing.T) {
+	ctx, config := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+		}
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+	`, func(fs map[string][]byte, config android.Config) {
+		config.TestProductVariables.InstallExtraFlattenedApexes = proptools.BoolPtr(true)
+	})
+	ab := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
+	ensureListContains(t, ab.requiredDeps, "myapex.flattened")
+	mk := android.AndroidMkDataForTest(t, config, "", ab)
+	var builder strings.Builder
+	mk.Custom(&builder, ab.Name(), "TARGET_", "", mk)
+	androidMk := builder.String()
+	ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES += myapex.flattened")
+}
+
+func TestApexUsesOtherApex(t *testing.T) {
+	ctx, _ := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			native_shared_libs: ["mylib"],
+			uses: ["commonapex"],
+		}
+
+		apex {
+			name: "commonapex",
+			key: "myapex.key",
+			native_shared_libs: ["libcommon"],
+			provide_cpp_shared_libs: true,
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "mylib",
+			srcs: ["mylib.cpp"],
+			shared_libs: ["libcommon"],
+			system_shared_libs: [],
+			stl: "none",
+		}
+
+		cc_library {
+			name: "libcommon",
+			srcs: ["mylib_common.cpp"],
+			system_shared_libs: [],
+			stl: "none",
+		}
+	`)
+
+	module1 := ctx.ModuleForTests("myapex", "android_common_myapex_image")
+	apexRule1 := module1.Rule("apexRule")
+	copyCmds1 := apexRule1.Args["copy_commands"]
+
+	module2 := ctx.ModuleForTests("commonapex", "android_common_commonapex_image")
+	apexRule2 := module2.Rule("apexRule")
+	copyCmds2 := apexRule2.Args["copy_commands"]
+
+	ensureListContains(t, ctx.ModuleVariantsForTests("mylib"), "android_arm64_armv8-a_shared_myapex")
+	ensureListContains(t, ctx.ModuleVariantsForTests("libcommon"), "android_arm64_armv8-a_shared_commonapex")
+	ensureContains(t, copyCmds1, "image.apex/lib64/mylib.so")
+	ensureContains(t, copyCmds2, "image.apex/lib64/libcommon.so")
+	ensureNotContains(t, copyCmds1, "image.apex/lib64/libcommon.so")
+}
+
+func TestApexUsesFailsIfNotProvided(t *testing.T) {
+	testApexError(t, `uses: "commonapex" does not provide native_shared_libs`, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			uses: ["commonapex"],
+		}
+
+		apex {
+			name: "commonapex",
+			key: "myapex.key",
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+	`)
+	testApexError(t, `uses: "commonapex" is not a provider`, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			uses: ["commonapex"],
+		}
+
+		cc_library {
+			name: "commonapex",
+			system_shared_libs: [],
+			stl: "none",
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+	`)
+}
+
+func TestApexUsesFailsIfUseVenderMismatch(t *testing.T) {
+	testApexError(t, `use_vendor: "commonapex" has different value of use_vendor`, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			use_vendor: true,
+			uses: ["commonapex"],
+		}
+
+		apex {
+			name: "commonapex",
+			key: "myapex.key",
+			provide_cpp_shared_libs: true,
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+	`, func(fs map[string][]byte, config android.Config) {
+		setUseVendorWhitelistForTest(config, []string{"myapex"})
+	})
+}
+
+func TestErrorsIfDepsAreNotEnabled(t *testing.T) {
+	testApexError(t, `module "myapex" .* depends on disabled module "libfoo"`, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			native_shared_libs: ["libfoo"],
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "libfoo",
+			stl: "none",
+			system_shared_libs: [],
+			enabled: false,
+		}
+	`)
+	testApexError(t, `module "myapex" .* depends on disabled module "myjar"`, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			java_libs: ["myjar"],
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		java_library {
+			name: "myjar",
+			srcs: ["foo/bar/MyClass.java"],
+			sdk_version: "none",
+			system_modules: "none",
+			compile_dex: true,
+			enabled: false,
+		}
+	`)
+}
+
+func TestApexWithApps(t *testing.T) {
+	ctx, _ := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			apps: [
+				"AppFoo",
+				"AppFooPriv",
+			],
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		android_app {
+			name: "AppFoo",
+			srcs: ["foo/bar/MyClass.java"],
+			sdk_version: "none",
+			system_modules: "none",
+			jni_libs: ["libjni"],
+		}
+
+		android_app {
+			name: "AppFooPriv",
+			srcs: ["foo/bar/MyClass.java"],
+			sdk_version: "none",
+			system_modules: "none",
+			privileged: true,
+		}
+
+		cc_library_shared {
+			name: "libjni",
+			srcs: ["mylib.cpp"],
+			stl: "none",
+			system_shared_libs: [],
+		}
+	`)
+
+	module := ctx.ModuleForTests("myapex", "android_common_myapex_image")
+	apexRule := module.Rule("apexRule")
+	copyCmds := apexRule.Args["copy_commands"]
+
+	ensureContains(t, copyCmds, "image.apex/app/AppFoo/AppFoo.apk")
+	ensureContains(t, copyCmds, "image.apex/priv-app/AppFooPriv/AppFooPriv.apk")
+
+	// JNI libraries are embedded inside APK
+	appZipRule := ctx.ModuleForTests("AppFoo", "android_common_myapex").Rule("zip")
+	libjniOutput := ctx.ModuleForTests("libjni", "android_arm64_armv8-a_shared_myapex").Module().(*cc.Module).OutputFile()
+	ensureListContains(t, appZipRule.Implicits.Strings(), libjniOutput.String())
+	// ... uncompressed
+	if args := appZipRule.Args["jarArgs"]; !strings.Contains(args, "-L 0") {
+		t.Errorf("jni lib is not uncompressed for AppFoo")
+	}
+	// ... and not directly inside the APEX
+	ensureNotContains(t, copyCmds, "image.apex/lib64/libjni.so")
+}
+
+func TestApexWithAppImports(t *testing.T) {
+	ctx, _ := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			apps: [
+				"AppFooPrebuilt",
+				"AppFooPrivPrebuilt",
+			],
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		android_app_import {
+			name: "AppFooPrebuilt",
+			apk: "PrebuiltAppFoo.apk",
+			presigned: true,
+			dex_preopt: {
+				enabled: false,
+			},
+		}
+
+		android_app_import {
+			name: "AppFooPrivPrebuilt",
+			apk: "PrebuiltAppFooPriv.apk",
+			privileged: true,
+			presigned: true,
+			dex_preopt: {
+				enabled: false,
+			},
+		}
+	`)
+
+	module := ctx.ModuleForTests("myapex", "android_common_myapex_image")
+	apexRule := module.Rule("apexRule")
+	copyCmds := apexRule.Args["copy_commands"]
+
+	ensureContains(t, copyCmds, "image.apex/app/AppFooPrebuilt/AppFooPrebuilt.apk")
+	ensureContains(t, copyCmds, "image.apex/priv-app/AppFooPrivPrebuilt/AppFooPrivPrebuilt.apk")
+}
+
+func TestApexWithTestHelperApp(t *testing.T) {
+	ctx, _ := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			apps: [
+				"TesterHelpAppFoo",
+			],
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		android_test_helper_app {
+			name: "TesterHelpAppFoo",
+			srcs: ["foo/bar/MyClass.java"],
+		}
+
+	`)
+
+	module := ctx.ModuleForTests("myapex", "android_common_myapex_image")
+	apexRule := module.Rule("apexRule")
+	copyCmds := apexRule.Args["copy_commands"]
+
+	ensureContains(t, copyCmds, "image.apex/app/TesterHelpAppFoo/TesterHelpAppFoo.apk")
+}
+
+func TestApexPropertiesShouldBeDefaultable(t *testing.T) {
+	// libfoo's apex_available comes from cc_defaults
+	testApexError(t, `"myapex" .*: requires "libfoo" that is not available for the APEX`, `
+	apex {
+		name: "myapex",
+		key: "myapex.key",
+		native_shared_libs: ["libfoo"],
+	}
+
+	apex_key {
+		name: "myapex.key",
+		public_key: "testkey.avbpubkey",
+		private_key: "testkey.pem",
+	}
+
+	apex {
+		name: "otherapex",
+		key: "myapex.key",
+		native_shared_libs: ["libfoo"],
+	}
+
+	cc_defaults {
+		name: "libfoo-defaults",
+		apex_available: ["otherapex"],
+	}
+
+	cc_library {
+		name: "libfoo",
+		defaults: ["libfoo-defaults"],
+		stl: "none",
+		system_shared_libs: [],
+	}`)
+}
+
+func TestApexAvailable(t *testing.T) {
+	// libfoo is not available to myapex, but only to otherapex
+	testApexError(t, "requires \"libfoo\" that is not available for the APEX", `
+	apex {
+		name: "myapex",
+		key: "myapex.key",
+		native_shared_libs: ["libfoo"],
+	}
+
+	apex_key {
+		name: "myapex.key",
+		public_key: "testkey.avbpubkey",
+		private_key: "testkey.pem",
+	}
+
+	apex {
+		name: "otherapex",
+		key: "otherapex.key",
+		native_shared_libs: ["libfoo"],
+	}
+
+	apex_key {
+		name: "otherapex.key",
+		public_key: "testkey.avbpubkey",
+		private_key: "testkey.pem",
+	}
+
+	cc_library {
+		name: "libfoo",
+		stl: "none",
+		system_shared_libs: [],
+		apex_available: ["otherapex"],
+	}`)
+
+	// libbar is an indirect dep
+	testApexError(t, "requires \"libbar\" that is not available for the APEX", `
+	apex {
+		name: "myapex",
+		key: "myapex.key",
+		native_shared_libs: ["libfoo"],
+	}
+
+	apex_key {
+		name: "myapex.key",
+		public_key: "testkey.avbpubkey",
+		private_key: "testkey.pem",
+	}
+
+	apex {
+		name: "otherapex",
+		key: "otherapex.key",
+		native_shared_libs: ["libfoo"],
+	}
+
+	apex_key {
+		name: "otherapex.key",
+		public_key: "testkey.avbpubkey",
+		private_key: "testkey.pem",
+	}
+
+	cc_library {
+		name: "libfoo",
+		stl: "none",
+		shared_libs: ["libbar"],
+		system_shared_libs: [],
+		apex_available: ["myapex", "otherapex"],
+	}
+
+	cc_library {
+		name: "libbar",
+		stl: "none",
+		system_shared_libs: [],
+		apex_available: ["otherapex"],
+	}`)
+
+	testApexError(t, "\"otherapex\" is not a valid module name", `
+	apex {
+		name: "myapex",
+		key: "myapex.key",
+		native_shared_libs: ["libfoo"],
+	}
+
+	apex_key {
+		name: "myapex.key",
+		public_key: "testkey.avbpubkey",
+		private_key: "testkey.pem",
+	}
+
+	cc_library {
+		name: "libfoo",
+		stl: "none",
+		system_shared_libs: [],
+		apex_available: ["otherapex"],
+	}`)
+
+	ctx, _ := testApex(t, `
+	apex {
+		name: "myapex",
+		key: "myapex.key",
+		native_shared_libs: ["libfoo", "libbar"],
+	}
+
+	apex_key {
+		name: "myapex.key",
+		public_key: "testkey.avbpubkey",
+		private_key: "testkey.pem",
+	}
+
+	cc_library {
+		name: "libfoo",
+		stl: "none",
+		system_shared_libs: [],
+		apex_available: ["myapex"],
+	}
+
+	cc_library {
+		name: "libbar",
+		stl: "none",
+		system_shared_libs: [],
+		apex_available: ["//apex_available:anyapex"],
+	}`)
+
+	// check that libfoo and libbar are created only for myapex, but not for the platform
+	ensureListContains(t, ctx.ModuleVariantsForTests("libfoo"), "android_arm64_armv8-a_shared_myapex")
+	ensureListNotContains(t, ctx.ModuleVariantsForTests("libfoo"), "android_arm64_armv8-a_shared")
+	ensureListContains(t, ctx.ModuleVariantsForTests("libbar"), "android_arm64_armv8-a_shared_myapex")
+	ensureListNotContains(t, ctx.ModuleVariantsForTests("libbar"), "android_arm64_armv8-a_shared")
+
+	ctx, _ = testApex(t, `
+	apex {
+		name: "myapex",
+		key: "myapex.key",
+	}
+
+	apex_key {
+		name: "myapex.key",
+		public_key: "testkey.avbpubkey",
+		private_key: "testkey.pem",
+	}
+
+	cc_library {
+		name: "libfoo",
+		stl: "none",
+		system_shared_libs: [],
+		apex_available: ["//apex_available:platform"],
+	}`)
+
+	// check that libfoo is created only for the platform
+	ensureListNotContains(t, ctx.ModuleVariantsForTests("libfoo"), "android_arm64_armv8-a_shared_myapex")
+	ensureListContains(t, ctx.ModuleVariantsForTests("libfoo"), "android_arm64_armv8-a_shared")
+
+	ctx, _ = testApex(t, `
+	apex {
+		name: "myapex",
+		key: "myapex.key",
+		native_shared_libs: ["libfoo"],
+	}
+
+	apex_key {
+		name: "myapex.key",
+		public_key: "testkey.avbpubkey",
+		private_key: "testkey.pem",
+	}
+
+	cc_library {
+		name: "libfoo",
+		stl: "none",
+		system_shared_libs: [],
+		apex_available: ["myapex"],
+		static: {
+			apex_available: ["//apex_available:platform"],
+		},
+	}`)
+
+	// shared variant of libfoo is only available to myapex
+	ensureListContains(t, ctx.ModuleVariantsForTests("libfoo"), "android_arm64_armv8-a_shared_myapex")
+	ensureListNotContains(t, ctx.ModuleVariantsForTests("libfoo"), "android_arm64_armv8-a_shared")
+	// but the static variant is available to both myapex and the platform
+	ensureListContains(t, ctx.ModuleVariantsForTests("libfoo"), "android_arm64_armv8-a_static_myapex")
+	ensureListContains(t, ctx.ModuleVariantsForTests("libfoo"), "android_arm64_armv8-a_static")
+}
+
+func TestOverrideApex(t *testing.T) {
+	ctx, config := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			apps: ["app"],
+			overrides: ["oldapex"],
+		}
+
+		override_apex {
+			name: "override_myapex",
+			base: "myapex",
+			apps: ["override_app"],
+			overrides: ["unknownapex"],
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		android_app {
+			name: "app",
+			srcs: ["foo/bar/MyClass.java"],
+			package_name: "foo",
+			sdk_version: "none",
+			system_modules: "none",
+		}
+
+		override_android_app {
+			name: "override_app",
+			base: "app",
+			package_name: "bar",
+		}
+	`)
+
+	originalVariant := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(android.OverridableModule)
+	overriddenVariant := ctx.ModuleForTests("myapex", "android_common_override_myapex_myapex_image").Module().(android.OverridableModule)
+	if originalVariant.GetOverriddenBy() != "" {
+		t.Errorf("GetOverriddenBy should be empty, but was %q", originalVariant.GetOverriddenBy())
+	}
+	if overriddenVariant.GetOverriddenBy() != "override_myapex" {
+		t.Errorf("GetOverriddenBy should be \"override_myapex\", but was %q", overriddenVariant.GetOverriddenBy())
+	}
+
+	module := ctx.ModuleForTests("myapex", "android_common_override_myapex_myapex_image")
+	apexRule := module.Rule("apexRule")
+	copyCmds := apexRule.Args["copy_commands"]
+
+	ensureNotContains(t, copyCmds, "image.apex/app/app/app.apk")
+	ensureContains(t, copyCmds, "image.apex/app/app/override_app.apk")
+
+	apexBundle := module.Module().(*apexBundle)
+	name := apexBundle.Name()
+	if name != "override_myapex" {
+		t.Errorf("name should be \"override_myapex\", but was %q", name)
+	}
+
+	data := android.AndroidMkDataForTest(t, config, "", apexBundle)
+	var builder strings.Builder
+	data.Custom(&builder, name, "TARGET_", "", data)
+	androidMk := builder.String()
+	ensureContains(t, androidMk, "LOCAL_MODULE := override_app.override_myapex")
+	ensureContains(t, androidMk, "LOCAL_MODULE := apex_manifest.pb.override_myapex")
+	ensureContains(t, androidMk, "LOCAL_MODULE_STEM := override_myapex.apex")
+	ensureContains(t, androidMk, "LOCAL_OVERRIDES_MODULES := unknownapex myapex")
+	ensureNotContains(t, androidMk, "LOCAL_MODULE := app.myapex")
+	ensureNotContains(t, androidMk, "LOCAL_MODULE := override_app.myapex")
+	ensureNotContains(t, androidMk, "LOCAL_MODULE := apex_manifest.pb.myapex")
+	ensureNotContains(t, androidMk, "LOCAL_MODULE_STEM := myapex.apex")
+}
+
+func TestLegacyAndroid10Support(t *testing.T) {
+	ctx, _ := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			legacy_android10_support: true,
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+	`)
+
+	module := ctx.ModuleForTests("myapex", "android_common_myapex_image")
+	args := module.Rule("apexRule").Args
+	ensureContains(t, args["opt_flags"], "--manifest_json "+module.Output("apex_manifest.json").Output.String())
+}
+
+func TestJavaSDKLibrary(t *testing.T) {
+	ctx, _ := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			java_libs: ["foo"],
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		java_sdk_library {
+			name: "foo",
+			srcs: ["a.java"],
+			api_packages: ["foo"],
+		}
+	`, withFiles(map[string][]byte{
+		"api/current.txt":        nil,
+		"api/removed.txt":        nil,
+		"api/system-current.txt": nil,
+		"api/system-removed.txt": nil,
+		"api/test-current.txt":   nil,
+		"api/test-removed.txt":   nil,
+	}))
+
+	// java_sdk_library installs both impl jar and permission XML
+	ensureExactContents(t, ctx, "myapex", []string{
+		"javalib/foo.jar",
+		"etc/permissions/foo.xml",
+	})
+	// Permission XML should point to the activated path of impl jar of java_sdk_library
+	xml := ctx.ModuleForTests("foo", "android_common_myapex").Output("foo.xml")
+	ensureContains(t, xml.Args["content"], `<library name="foo" file="/apex/myapex/javalib/foo.jar"`)
+}
+
+func TestRejectNonInstallableJavaLibrary(t *testing.T) {
+	testApexError(t, `"myjar" is not configured to be compiled into dex`, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			java_libs: ["myjar"],
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		java_library {
+			name: "myjar",
+			srcs: ["foo/bar/MyClass.java"],
+			sdk_version: "none",
+			system_modules: "none",
+		}
+	`)
+}
+
+func TestCarryRequiredModuleNames(t *testing.T) {
+	ctx, config := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			native_shared_libs: ["mylib"],
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "mylib",
+			srcs: ["mylib.cpp"],
+			system_shared_libs: [],
+			stl: "none",
+			required: ["a", "b"],
+			host_required: ["c", "d"],
+			target_required: ["e", "f"],
+		}
+	`)
+
+	apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
+	data := android.AndroidMkDataForTest(t, config, "", apexBundle)
+	name := apexBundle.BaseModuleName()
+	prefix := "TARGET_"
+	var builder strings.Builder
+	data.Custom(&builder, name, prefix, "", data)
+	androidMk := builder.String()
+	ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES += a b\n")
+	ensureContains(t, androidMk, "LOCAL_HOST_REQUIRED_MODULES += c d\n")
+	ensureContains(t, androidMk, "LOCAL_TARGET_REQUIRED_MODULES += e f\n")
+}
+
+func TestMain(m *testing.M) {
+	run := func() int {
+		setUp()
+		defer tearDown()
+
+		return m.Run()
+	}
+
+	os.Exit(run())
+}
diff --git a/apex/builder.go b/apex/builder.go
new file mode 100644
index 0000000..2f1e453
--- /dev/null
+++ b/apex/builder.go
@@ -0,0 +1,604 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package apex
+
+import (
+	"fmt"
+	"path/filepath"
+	"runtime"
+	"sort"
+	"strings"
+
+	"android/soong/android"
+	"android/soong/java"
+
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+)
+
+var (
+	pctx = android.NewPackageContext("android/apex")
+)
+
+func init() {
+	pctx.Import("android/soong/android")
+	pctx.Import("android/soong/java")
+	pctx.HostBinToolVariable("apexer", "apexer")
+	// ART minimal builds (using the master-art manifest) do not have the "frameworks/base"
+	// projects, and hence cannot built 'aapt2'. Use the SDK prebuilt instead.
+	hostBinToolVariableWithPrebuilt := func(name, prebuiltDir, tool string) {
+		pctx.VariableFunc(name, func(ctx android.PackageVarContext) string {
+			if !ctx.Config().FrameworksBaseDirExists(ctx) {
+				return filepath.Join(prebuiltDir, runtime.GOOS, "bin", tool)
+			} else {
+				return ctx.Config().HostToolPath(ctx, tool).String()
+			}
+		})
+	}
+	hostBinToolVariableWithPrebuilt("aapt2", "prebuilts/sdk/tools", "aapt2")
+	pctx.HostBinToolVariable("avbtool", "avbtool")
+	pctx.HostBinToolVariable("e2fsdroid", "e2fsdroid")
+	pctx.HostBinToolVariable("merge_zips", "merge_zips")
+	pctx.HostBinToolVariable("mke2fs", "mke2fs")
+	pctx.HostBinToolVariable("resize2fs", "resize2fs")
+	pctx.HostBinToolVariable("sefcontext_compile", "sefcontext_compile")
+	pctx.HostBinToolVariable("soong_zip", "soong_zip")
+	pctx.HostBinToolVariable("zip2zip", "zip2zip")
+	pctx.HostBinToolVariable("zipalign", "zipalign")
+	pctx.HostBinToolVariable("jsonmodify", "jsonmodify")
+	pctx.HostBinToolVariable("conv_apex_manifest", "conv_apex_manifest")
+}
+
+var (
+	// Create a canned fs config file where all files and directories are
+	// 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")
+
+	apexManifestRule = pctx.StaticRule("apexManifestRule", blueprint.RuleParams{
+		Command: `rm -f $out && ${jsonmodify} $in ` +
+			`-a provideNativeLibs ${provideNativeLibs} ` +
+			`-a requireNativeLibs ${requireNativeLibs} ` +
+			`${opt} ` +
+			`-o $out`,
+		CommandDeps: []string{"${jsonmodify}"},
+		Description: "prepare ${out}",
+	}, "provideNativeLibs", "requireNativeLibs", "opt")
+
+	stripApexManifestRule = pctx.StaticRule("stripApexManifestRule", blueprint.RuleParams{
+		Command:     `rm -f $out && ${conv_apex_manifest} strip $in -o $out`,
+		CommandDeps: []string{"${conv_apex_manifest}"},
+		Description: "strip ${in}=>${out}",
+	})
+
+	pbApexManifestRule = pctx.StaticRule("pbApexManifestRule", blueprint.RuleParams{
+		Command:     `rm -f $out && ${conv_apex_manifest} proto $in -o $out`,
+		CommandDeps: []string{"${conv_apex_manifest}"},
+		Description: "convert ${in}=>${out}",
+	})
+
+	// TODO(b/113233103): make sure that file_contexts is sane, i.e., validate
+	// against the binary policy using sefcontext_compiler -p <policy>.
+
+	// TODO(b/114327326): automate the generation of file_contexts
+	apexRule = pctx.StaticRule("apexRule", blueprint.RuleParams{
+		Command: `rm -rf ${image_dir} && mkdir -p ${image_dir} && ` +
+			`(. ${out}.copy_commands) && ` +
+			`APEXER_TOOL_PATH=${tool_path} ` +
+			`${apexer} --force --manifest ${manifest} ` +
+			`--file_contexts ${file_contexts} ` +
+			`--canned_fs_config ${canned_fs_config} ` +
+			`--include_build_info ` +
+			`--payload_type image ` +
+			`--key ${key} ${opt_flags} ${image_dir} ${out} `,
+		CommandDeps: []string{"${apexer}", "${avbtool}", "${e2fsdroid}", "${merge_zips}",
+			"${mke2fs}", "${resize2fs}", "${sefcontext_compile}",
+			"${soong_zip}", "${zipalign}", "${aapt2}", "prebuilts/sdk/current/public/android.jar"},
+		Rspfile:        "${out}.copy_commands",
+		RspfileContent: "${copy_commands}",
+		Description:    "APEX ${image_dir} => ${out}",
+	}, "tool_path", "image_dir", "copy_commands", "file_contexts", "canned_fs_config", "key", "opt_flags", "manifest")
+
+	zipApexRule = pctx.StaticRule("zipApexRule", blueprint.RuleParams{
+		Command: `rm -rf ${image_dir} && mkdir -p ${image_dir} && ` +
+			`(. ${out}.copy_commands) && ` +
+			`APEXER_TOOL_PATH=${tool_path} ` +
+			`${apexer} --force --manifest ${manifest} ` +
+			`--payload_type zip ` +
+			`${image_dir} ${out} `,
+		CommandDeps:    []string{"${apexer}", "${merge_zips}", "${soong_zip}", "${zipalign}", "${aapt2}"},
+		Rspfile:        "${out}.copy_commands",
+		RspfileContent: "${copy_commands}",
+		Description:    "ZipAPEX ${image_dir} => ${out}",
+	}, "tool_path", "image_dir", "copy_commands", "manifest")
+
+	apexProtoConvertRule = pctx.AndroidStaticRule("apexProtoConvertRule",
+		blueprint.RuleParams{
+			Command:     `${aapt2} convert --output-format proto $in -o $out`,
+			CommandDeps: []string{"${aapt2}"},
+		})
+
+	apexBundleRule = pctx.StaticRule("apexBundleRule", blueprint.RuleParams{
+		Command: `${zip2zip} -i $in -o $out ` +
+			`apex_payload.img:apex/${abi}.img ` +
+			`apex_manifest.json:root/apex_manifest.json ` +
+			`apex_manifest.pb:root/apex_manifest.pb ` +
+			`AndroidManifest.xml:manifest/AndroidManifest.xml ` +
+			`assets/NOTICE.html.gz:assets/NOTICE.html.gz`,
+		CommandDeps: []string{"${zip2zip}"},
+		Description: "app bundle",
+	}, "abi")
+
+	emitApexContentRule = pctx.StaticRule("emitApexContentRule", blueprint.RuleParams{
+		Command:        `rm -f ${out} && touch ${out} && (. ${out}.emit_commands)`,
+		Rspfile:        "${out}.emit_commands",
+		RspfileContent: "${emit_commands}",
+		Description:    "Emit APEX image content",
+	}, "emit_commands")
+
+	diffApexContentRule = pctx.StaticRule("diffApexContentRule", blueprint.RuleParams{
+		Command: `diff --unchanged-group-format='' \` +
+			`--changed-group-format='%<' \` +
+			`${image_content_file} ${whitelisted_files_file} || (` +
+			`echo -e "New unexpected files were added to ${apex_module_name}." ` +
+			` "To fix the build run following command:" && ` +
+			`echo "system/apex/tools/update_whitelist.sh ${whitelisted_files_file} ${image_content_file}" && ` +
+			`exit 1)`,
+		Description: "Diff ${image_content_file} and ${whitelisted_files_file}",
+	}, "image_content_file", "whitelisted_files_file", "apex_module_name")
+)
+
+func (a *apexBundle) buildManifest(ctx android.ModuleContext, provideNativeLibs, requireNativeLibs []string) {
+	manifestSrc := android.PathForModuleSrc(ctx, proptools.StringDefault(a.properties.Manifest, "apex_manifest.json"))
+
+	manifestJsonFullOut := android.PathForModuleOut(ctx, "apex_manifest_full.json")
+
+	// put dependency({provide|require}NativeLibs) in apex_manifest.json
+	provideNativeLibs = android.SortedUniqueStrings(provideNativeLibs)
+	requireNativeLibs = android.SortedUniqueStrings(android.RemoveListFromList(requireNativeLibs, provideNativeLibs))
+
+	// apex name can be overridden
+	optCommands := []string{}
+	if a.properties.Apex_name != nil {
+		optCommands = append(optCommands, "-v name "+*a.properties.Apex_name)
+	}
+
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   apexManifestRule,
+		Input:  manifestSrc,
+		Output: manifestJsonFullOut,
+		Args: map[string]string{
+			"provideNativeLibs": strings.Join(provideNativeLibs, " "),
+			"requireNativeLibs": strings.Join(requireNativeLibs, " "),
+			"opt":               strings.Join(optCommands, " "),
+		},
+	})
+
+	if proptools.Bool(a.properties.Legacy_android10_support) {
+		// b/143654022 Q apexd can't understand newly added keys in apex_manifest.json
+		// prepare stripped-down version so that APEX modules built from R+ can be installed to Q
+		a.manifestJsonOut = android.PathForModuleOut(ctx, "apex_manifest.json")
+		ctx.Build(pctx, android.BuildParams{
+			Rule:   stripApexManifestRule,
+			Input:  manifestJsonFullOut,
+			Output: a.manifestJsonOut,
+		})
+	}
+
+	// from R+, protobuf binary format (.pb) is the standard format for apex_manifest
+	a.manifestPbOut = android.PathForModuleOut(ctx, "apex_manifest.pb")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   pbApexManifestRule,
+		Input:  manifestJsonFullOut,
+		Output: a.manifestPbOut,
+	})
+}
+
+func (a *apexBundle) buildNoticeFile(ctx android.ModuleContext, apexFileName string) android.OptionalPath {
+	noticeFiles := []android.Path{}
+	for _, f := range a.filesInfo {
+		if f.module != nil {
+			notice := f.module.NoticeFile()
+			if notice.Valid() {
+				noticeFiles = append(noticeFiles, notice.Path())
+			}
+		}
+	}
+	// append the notice file specified in the apex module itself
+	if a.NoticeFile().Valid() {
+		noticeFiles = append(noticeFiles, a.NoticeFile().Path())
+	}
+
+	if len(noticeFiles) == 0 {
+		return android.OptionalPath{}
+	}
+
+	return android.BuildNoticeOutput(ctx, a.installDir, apexFileName, android.FirstUniquePaths(noticeFiles)).HtmlGzOutput
+}
+
+func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext) {
+	var abis []string
+	for _, target := range ctx.MultiTargets() {
+		if len(target.Arch.Abi) > 0 {
+			abis = append(abis, target.Arch.Abi[0])
+		}
+	}
+
+	abis = android.FirstUniqueStrings(abis)
+
+	apexType := a.properties.ApexType
+	suffix := apexType.suffix()
+	unsignedOutputFile := android.PathForModuleOut(ctx, a.Name()+suffix+".unsigned")
+
+	filesToCopy := []android.Path{}
+	for _, f := range a.filesInfo {
+		filesToCopy = append(filesToCopy, f.builtFile)
+	}
+
+	copyCommands := []string{}
+	emitCommands := []string{}
+	imageContentFile := android.PathForModuleOut(ctx, a.Name()+"-content.txt")
+	emitCommands = append(emitCommands, "echo ./apex_manifest.pb >> "+imageContentFile.String())
+	if proptools.Bool(a.properties.Legacy_android10_support) {
+		emitCommands = append(emitCommands, "echo ./apex_manifest.json >> "+imageContentFile.String())
+	}
+	for i, src := range filesToCopy {
+		dest := filepath.Join(a.filesInfo[i].installDir, src.Base())
+		emitCommands = append(emitCommands, "echo './"+dest+"' >> "+imageContentFile.String())
+		dest_path := filepath.Join(android.PathForModuleOut(ctx, "image"+suffix).String(), dest)
+		copyCommands = append(copyCommands, "mkdir -p "+filepath.Dir(dest_path))
+		copyCommands = append(copyCommands, "cp "+src.String()+" "+dest_path)
+		for _, sym := range a.filesInfo[i].symlinks {
+			symlinkDest := filepath.Join(filepath.Dir(dest_path), sym)
+			copyCommands = append(copyCommands, "ln -s "+filepath.Base(dest)+" "+symlinkDest)
+		}
+	}
+	emitCommands = append(emitCommands, "sort -o "+imageContentFile.String()+" "+imageContentFile.String())
+
+	implicitInputs := append(android.Paths(nil), filesToCopy...)
+	implicitInputs = append(implicitInputs, a.manifestPbOut)
+
+	if a.properties.Whitelisted_files != nil {
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        emitApexContentRule,
+			Implicits:   implicitInputs,
+			Output:      imageContentFile,
+			Description: "emit apex image content",
+			Args: map[string]string{
+				"emit_commands": strings.Join(emitCommands, " && "),
+			},
+		})
+		implicitInputs = append(implicitInputs, imageContentFile)
+		whitelistedFilesFile := android.PathForModuleSrc(ctx, proptools.String(a.properties.Whitelisted_files))
+
+		phonyOutput := android.PathForModuleOut(ctx, a.Name()+"-diff-phony-output")
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        diffApexContentRule,
+			Implicits:   implicitInputs,
+			Output:      phonyOutput,
+			Description: "diff apex image content",
+			Args: map[string]string{
+				"whitelisted_files_file": whitelistedFilesFile.String(),
+				"image_content_file":     imageContentFile.String(),
+				"apex_module_name":       a.Name(),
+			},
+		})
+
+		implicitInputs = append(implicitInputs, phonyOutput)
+	}
+
+	outHostBinDir := android.PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "bin").String()
+	prebuiltSdkToolsBinDir := filepath.Join("prebuilts", "sdk", "tools", runtime.GOOS, "bin")
+
+	if apexType == imageApex {
+		// 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
+		for _, f := range a.filesInfo {
+			pathInApex := filepath.Join(f.installDir, f.builtFile.Base())
+			if f.installDir == "bin" || strings.HasPrefix(f.installDir, "bin/") {
+				executablePaths = append(executablePaths, pathInApex)
+				for _, s := range f.symlinks {
+					executablePaths = append(executablePaths, filepath.Join(f.installDir, s))
+				}
+			} else {
+				readOnlyPaths = append(readOnlyPaths, pathInApex)
+			}
+			dir := f.installDir
+			for !android.InList(dir, executablePaths) && dir != "" {
+				executablePaths = append(executablePaths, dir)
+				dir, _ = filepath.Split(dir) // move up to the parent
+				if len(dir) > 0 {
+					// remove trailing slash
+					dir = dir[:len(dir)-1]
+				}
+			}
+		}
+		sort.Strings(readOnlyPaths)
+		sort.Strings(executablePaths)
+		cannedFsConfig := android.PathForModuleOut(ctx, "canned_fs_config")
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        generateFsConfig,
+			Output:      cannedFsConfig,
+			Description: "generate fs config",
+			Args: map[string]string{
+				"ro_paths":   strings.Join(readOnlyPaths, " "),
+				"exec_paths": strings.Join(executablePaths, " "),
+			},
+		})
+
+		optFlags := []string{}
+
+		// Additional implicit inputs.
+		implicitInputs = append(implicitInputs, cannedFsConfig, a.fileContexts, a.private_key_file, a.public_key_file)
+		optFlags = append(optFlags, "--pubkey "+a.public_key_file.String())
+
+		manifestPackageName := a.getOverrideManifestPackageName(ctx)
+		if manifestPackageName != "" {
+			optFlags = append(optFlags, "--override_apk_package_name "+manifestPackageName)
+		}
+
+		if a.properties.AndroidManifest != nil {
+			androidManifestFile := android.PathForModuleSrc(ctx, proptools.String(a.properties.AndroidManifest))
+			implicitInputs = append(implicitInputs, androidManifestFile)
+			optFlags = append(optFlags, "--android_manifest "+androidManifestFile.String())
+		}
+
+		targetSdkVersion := ctx.Config().DefaultAppTargetSdk()
+		if targetSdkVersion == ctx.Config().PlatformSdkCodename() &&
+			ctx.Config().UnbundledBuild() &&
+			!ctx.Config().UnbundledBuildUsePrebuiltSdks() &&
+			ctx.Config().IsEnvTrue("UNBUNDLED_BUILD_TARGET_SDK_WITH_API_FINGERPRINT") {
+			apiFingerprint := java.ApiFingerprintPath(ctx)
+			targetSdkVersion += fmt.Sprintf(".$$(cat %s)", apiFingerprint.String())
+			implicitInputs = append(implicitInputs, apiFingerprint)
+		}
+		optFlags = append(optFlags, "--target_sdk_version "+targetSdkVersion)
+
+		noticeFile := a.buildNoticeFile(ctx, a.Name()+suffix)
+		if noticeFile.Valid() {
+			// If there's a NOTICE file, embed it as an asset file in the APEX.
+			implicitInputs = append(implicitInputs, noticeFile.Path())
+			optFlags = append(optFlags, "--assets_dir "+filepath.Dir(noticeFile.String()))
+		}
+
+		if ctx.ModuleDir() != "system/apex/apexd/apexd_testdata" && ctx.ModuleDir() != "system/apex/shim/build" && a.testOnlyShouldSkipHashtreeGeneration() {
+			ctx.PropertyErrorf("test_only_no_hashtree", "not available")
+			return
+		}
+		if (!ctx.Config().UnbundledBuild() && a.installable()) || a.testOnlyShouldSkipHashtreeGeneration() {
+			// Apexes which are supposed to be installed in builtin dirs(/system, etc)
+			// don't need hashtree for activation. Therefore, by removing hashtree from
+			// apex bundle (filesystem image in it, to be specific), we can save storage.
+			optFlags = append(optFlags, "--no_hashtree")
+		}
+
+		if a.properties.Apex_name != nil {
+			// If apex_name is set, apexer can skip checking if key name matches with apex name.
+			// Note that apex_manifest is also mended.
+			optFlags = append(optFlags, "--do_not_check_keyname")
+		}
+
+		if proptools.Bool(a.properties.Legacy_android10_support) {
+			implicitInputs = append(implicitInputs, a.manifestJsonOut)
+			optFlags = append(optFlags, "--manifest_json "+a.manifestJsonOut.String())
+		}
+
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        apexRule,
+			Implicits:   implicitInputs,
+			Output:      unsignedOutputFile,
+			Description: "apex (" + apexType.name() + ")",
+			Args: map[string]string{
+				"tool_path":        outHostBinDir + ":" + prebuiltSdkToolsBinDir,
+				"image_dir":        android.PathForModuleOut(ctx, "image"+suffix).String(),
+				"copy_commands":    strings.Join(copyCommands, " && "),
+				"manifest":         a.manifestPbOut.String(),
+				"file_contexts":    a.fileContexts.String(),
+				"canned_fs_config": cannedFsConfig.String(),
+				"key":              a.private_key_file.String(),
+				"opt_flags":        strings.Join(optFlags, " "),
+			},
+		})
+
+		apexProtoFile := android.PathForModuleOut(ctx, a.Name()+".pb"+suffix)
+		bundleModuleFile := android.PathForModuleOut(ctx, a.Name()+suffix+"-base.zip")
+		a.bundleModuleFile = bundleModuleFile
+
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        apexProtoConvertRule,
+			Input:       unsignedOutputFile,
+			Output:      apexProtoFile,
+			Description: "apex proto convert",
+		})
+
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        apexBundleRule,
+			Input:       apexProtoFile,
+			Output:      a.bundleModuleFile,
+			Description: "apex bundle module",
+			Args: map[string]string{
+				"abi": strings.Join(abis, "."),
+			},
+		})
+	} else {
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        zipApexRule,
+			Implicits:   implicitInputs,
+			Output:      unsignedOutputFile,
+			Description: "apex (" + apexType.name() + ")",
+			Args: map[string]string{
+				"tool_path":     outHostBinDir + ":" + prebuiltSdkToolsBinDir,
+				"image_dir":     android.PathForModuleOut(ctx, "image"+suffix).String(),
+				"copy_commands": strings.Join(copyCommands, " && "),
+				"manifest":      a.manifestPbOut.String(),
+			},
+		})
+	}
+
+	a.outputFile = android.PathForModuleOut(ctx, a.Name()+suffix)
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        java.Signapk,
+		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
+		},
+	})
+
+	// Install to $OUT/soong/{target,host}/.../apex
+	if a.installable() {
+		ctx.InstallFile(a.installDir, a.Name()+suffix, a.outputFile)
+	}
+	a.buildFilesInfo(ctx)
+}
+
+func (a *apexBundle) buildFlattenedApex(ctx android.ModuleContext) {
+	// Temporarily wrap the original `ctx` into a `flattenedApexContext` to have it
+	// reply true to `InstallBypassMake()` (thus making the call
+	// `android.PathForModuleInstall` below use `android.pathForInstallInMakeDir`
+	// instead of `android.PathForOutput`) to return the correct path to the flattened
+	// APEX (as its contents is installed by Make, not Soong).
+	factx := flattenedApexContext{ctx}
+	apexName := proptools.StringDefault(a.properties.Apex_name, ctx.ModuleName())
+	a.outputFile = android.PathForModuleInstall(&factx, "apex", apexName)
+
+	if a.installable() && a.GetOverriddenBy() == "" {
+		installPath := android.PathForModuleInstall(ctx, "apex", apexName)
+		devicePath := android.InstallPathToOnDevicePath(ctx, installPath)
+		addFlattenedFileContextsInfos(ctx, apexName+":"+devicePath+":"+a.fileContexts.String())
+	}
+	a.buildFilesInfo(ctx)
+}
+
+func (a *apexBundle) setCertificateAndPrivateKey(ctx android.ModuleContext) {
+	if a.container_certificate_file == nil {
+		cert := String(a.properties.Certificate)
+		if cert == "" {
+			pem, key := ctx.Config().DefaultAppCertificate(ctx)
+			a.container_certificate_file = pem
+			a.container_private_key_file = key
+		} else {
+			defaultDir := ctx.Config().DefaultAppCertificateDir(ctx)
+			a.container_certificate_file = defaultDir.Join(ctx, cert+".x509.pem")
+			a.container_private_key_file = defaultDir.Join(ctx, cert+".pk8")
+		}
+	}
+}
+
+func (a *apexBundle) buildFilesInfo(ctx android.ModuleContext) {
+	if a.installable() {
+		// For flattened APEX, do nothing but make sure that APEX manifest and apex_pubkey are also copied along
+		// with other ordinary files.
+		a.filesInfo = append(a.filesInfo, newApexFile(ctx, a.manifestPbOut, "apex_manifest.pb."+a.Name()+a.suffix, ".", etc, nil))
+
+		// rename to apex_pubkey
+		copiedPubkey := android.PathForModuleOut(ctx, "apex_pubkey")
+		ctx.Build(pctx, android.BuildParams{
+			Rule:   android.Cp,
+			Input:  a.public_key_file,
+			Output: copiedPubkey,
+		})
+		a.filesInfo = append(a.filesInfo, newApexFile(ctx, copiedPubkey, "apex_pubkey."+a.Name()+a.suffix, ".", etc, nil))
+
+		if a.properties.ApexType == flattenedApex {
+			apexName := proptools.StringDefault(a.properties.Apex_name, a.Name())
+			for _, fi := range a.filesInfo {
+				dir := filepath.Join("apex", apexName, fi.installDir)
+				target := ctx.InstallFile(android.PathForModuleInstall(ctx, dir), fi.builtFile.Base(), fi.builtFile)
+				for _, sym := range fi.symlinks {
+					ctx.InstallSymlink(android.PathForModuleInstall(ctx, dir), sym, target)
+				}
+			}
+		}
+	}
+}
+
+func (a *apexBundle) getOverrideManifestPackageName(ctx android.ModuleContext) string {
+	// For VNDK APEXes, check "com.android.vndk" in PRODUCT_MANIFEST_PACKAGE_NAME_OVERRIDES
+	// to see if it should be overridden because their <apex name> is dynamically generated
+	// according to its VNDK version.
+	if a.vndkApex {
+		overrideName, overridden := ctx.DeviceConfig().OverrideManifestPackageNameFor(vndkApexName)
+		if overridden {
+			return strings.Replace(*a.properties.Apex_name, vndkApexName, overrideName, 1)
+		}
+		return ""
+	}
+	manifestPackageName, overridden := ctx.DeviceConfig().OverrideManifestPackageNameFor(a.Name())
+	if overridden {
+		return manifestPackageName
+	}
+	return ""
+}
+
+func (a *apexBundle) buildApexDependencyInfo(ctx android.ModuleContext) {
+	if !a.primaryApexType {
+		return
+	}
+
+	if a.properties.IsCoverageVariant {
+		// Otherwise, we will have duplicated rules for coverage and
+		// non-coverage variants of the same APEX
+		return
+	}
+
+	if ctx.Host() {
+		// No need to generate dependency info for host variant
+		return
+	}
+
+	internalDeps := a.internalDeps
+	externalDeps := a.externalDeps
+
+	internalDeps = android.SortedUniqueStrings(internalDeps)
+	externalDeps = android.SortedUniqueStrings(externalDeps)
+	externalDeps = android.RemoveListFromList(externalDeps, internalDeps)
+
+	var content strings.Builder
+	for _, name := range internalDeps {
+		fmt.Fprintf(&content, "internal %s\\n", name)
+	}
+	for _, name := range externalDeps {
+		fmt.Fprintf(&content, "external %s\\n", name)
+	}
+
+	depsInfoFile := android.PathForOutput(ctx, a.Name()+"-deps-info.txt")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        android.WriteFile,
+		Description: "Dependency Info",
+		Output:      depsInfoFile,
+		Args: map[string]string{
+			"content": content.String(),
+		},
+	})
+
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   android.Phony,
+		Output: android.PathForPhony(ctx, a.Name()+"-deps-info"),
+		Inputs: []android.Path{depsInfoFile},
+	})
+}
diff --git a/apex/key.go b/apex/key.go
index 08cd45e..ffde315 100644
--- a/apex/key.go
+++ b/apex/key.go
@@ -27,7 +27,7 @@
 var String = proptools.String
 
 func init() {
-	android.RegisterModuleType("apex_key", apexKeyFactory)
+	android.RegisterModuleType("apex_key", ApexKeyFactory)
 	android.RegisterSingletonType("apex_keys_text", apexKeysTextFactory)
 }
 
@@ -53,7 +53,7 @@
 	Installable *bool
 }
 
-func apexKeyFactory() android.Module {
+func ApexKeyFactory() android.Module {
 	module := &apexKey{}
 	module.AddProperties(&module.properties)
 	android.InitAndroidArchModule(module, android.HostAndDeviceDefault, android.MultilibCommon)
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
new file mode 100644
index 0000000..d089c28
--- /dev/null
+++ b/apex/prebuilt.go
@@ -0,0 +1,210 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package apex
+
+import (
+	"fmt"
+	"strings"
+
+	"android/soong/android"
+
+	"github.com/google/blueprint/proptools"
+)
+
+type Prebuilt struct {
+	android.ModuleBase
+	prebuilt android.Prebuilt
+
+	properties PrebuiltProperties
+
+	inputApex       android.Path
+	installDir      android.InstallPath
+	installFilename string
+	outputApex      android.WritablePath
+
+	// list of commands to create symlinks for backward compatibility.
+	// these commands will be attached as LOCAL_POST_INSTALL_CMD
+	compatSymlinks []string
+}
+
+type PrebuiltProperties struct {
+	// the path to the prebuilt .apex file to import.
+	Source       string `blueprint:"mutated"`
+	ForceDisable bool   `blueprint:"mutated"`
+
+	Src  *string
+	Arch struct {
+		Arm struct {
+			Src *string
+		}
+		Arm64 struct {
+			Src *string
+		}
+		X86 struct {
+			Src *string
+		}
+		X86_64 struct {
+			Src *string
+		}
+	}
+
+	Installable *bool
+	// Optional name for the installed apex. If unspecified, name of the
+	// module is used as the file name
+	Filename *string
+
+	// Names of modules to be overridden. Listed modules can only be other binaries
+	// (in Make or Soong).
+	// This does not completely prevent installation of the overridden binaries, but if both
+	// binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will be removed
+	// from PRODUCT_PACKAGES.
+	Overrides []string
+}
+
+func (p *Prebuilt) installable() bool {
+	return p.properties.Installable == nil || proptools.Bool(p.properties.Installable)
+}
+
+func (p *Prebuilt) isForceDisabled() bool {
+	return p.properties.ForceDisable
+}
+
+func (p *Prebuilt) OutputFiles(tag string) (android.Paths, error) {
+	switch tag {
+	case "":
+		return android.Paths{p.outputApex}, nil
+	default:
+		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+	}
+}
+
+func (p *Prebuilt) InstallFilename() string {
+	return proptools.StringDefault(p.properties.Filename, p.BaseModuleName()+imageApexSuffix)
+}
+
+func (p *Prebuilt) Prebuilt() *android.Prebuilt {
+	return &p.prebuilt
+}
+
+func (p *Prebuilt) Name() string {
+	return p.prebuilt.Name(p.ModuleBase.Name())
+}
+
+// prebuilt_apex imports an `.apex` file into the build graph as if it was built with apex.
+func PrebuiltFactory() android.Module {
+	module := &Prebuilt{}
+	module.AddProperties(&module.properties)
+	android.InitSingleSourcePrebuiltModule(module, &module.properties, "Source")
+	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	return module
+}
+
+func (p *Prebuilt) DepsMutator(ctx android.BottomUpMutatorContext) {
+	// If the device is configured to use flattened APEX, force disable the prebuilt because
+	// the prebuilt is a non-flattened one.
+	forceDisable := ctx.Config().FlattenApex()
+
+	// Force disable the prebuilts when we are doing unbundled build. We do unbundled build
+	// to build the prebuilts themselves.
+	forceDisable = forceDisable || ctx.Config().UnbundledBuild()
+
+	// Force disable the prebuilts when coverage is enabled.
+	forceDisable = forceDisable || ctx.DeviceConfig().NativeCoverageEnabled()
+	forceDisable = forceDisable || ctx.Config().IsEnvTrue("EMMA_INSTRUMENT")
+
+	// b/137216042 don't use prebuilts when address sanitizer is on
+	forceDisable = forceDisable || android.InList("address", ctx.Config().SanitizeDevice()) ||
+		android.InList("hwaddress", ctx.Config().SanitizeDevice())
+
+	if forceDisable && p.prebuilt.SourceExists() {
+		p.properties.ForceDisable = true
+		return
+	}
+
+	// This is called before prebuilt_select and prebuilt_postdeps mutators
+	// The mutators requires that src to be set correctly for each arch so that
+	// arch variants are disabled when src is not provided for the arch.
+	if len(ctx.MultiTargets()) != 1 {
+		ctx.ModuleErrorf("compile_multilib shouldn't be \"both\" for prebuilt_apex")
+		return
+	}
+	var src string
+	switch ctx.MultiTargets()[0].Arch.ArchType {
+	case android.Arm:
+		src = String(p.properties.Arch.Arm.Src)
+	case android.Arm64:
+		src = String(p.properties.Arch.Arm64.Src)
+	case android.X86:
+		src = String(p.properties.Arch.X86.Src)
+	case android.X86_64:
+		src = String(p.properties.Arch.X86_64.Src)
+	default:
+		ctx.ModuleErrorf("prebuilt_apex does not support %q", ctx.MultiTargets()[0].Arch.String())
+		return
+	}
+	if src == "" {
+		src = String(p.properties.Src)
+	}
+	p.properties.Source = src
+}
+
+func (p *Prebuilt) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	if p.properties.ForceDisable {
+		return
+	}
+
+	// TODO(jungjw): Check the key validity.
+	p.inputApex = p.Prebuilt().SingleSourcePath(ctx)
+	p.installDir = android.PathForModuleInstall(ctx, "apex")
+	p.installFilename = p.InstallFilename()
+	if !strings.HasSuffix(p.installFilename, imageApexSuffix) {
+		ctx.ModuleErrorf("filename should end in %s for prebuilt_apex", imageApexSuffix)
+	}
+	p.outputApex = android.PathForModuleOut(ctx, p.installFilename)
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   android.Cp,
+		Input:  p.inputApex,
+		Output: p.outputApex,
+	})
+	if p.installable() {
+		ctx.InstallFile(p.installDir, p.installFilename, p.inputApex)
+	}
+
+	// in case that prebuilt_apex replaces source apex (using prefer: prop)
+	p.compatSymlinks = makeCompatSymlinks(p.BaseModuleName(), ctx)
+	// or that prebuilt_apex overrides other apexes (using overrides: prop)
+	for _, overridden := range p.properties.Overrides {
+		p.compatSymlinks = append(p.compatSymlinks, makeCompatSymlinks(overridden, ctx)...)
+	}
+}
+
+func (p *Prebuilt) AndroidMkEntries() []android.AndroidMkEntries {
+	return []android.AndroidMkEntries{android.AndroidMkEntries{
+		Class:      "ETC",
+		OutputFile: android.OptionalPathForPath(p.inputApex),
+		Include:    "$(BUILD_PREBUILT)",
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(entries *android.AndroidMkEntries) {
+				entries.SetString("LOCAL_MODULE_PATH", p.installDir.ToMakePath().String())
+				entries.SetString("LOCAL_MODULE_STEM", p.installFilename)
+				entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !p.installable())
+				entries.AddStrings("LOCAL_OVERRIDES_MODULES", p.properties.Overrides...)
+				if len(p.compatSymlinks) > 0 {
+					entries.SetString("LOCAL_POST_INSTALL_CMD", strings.Join(p.compatSymlinks, " && "))
+				}
+			},
+		},
+	}}
+}
diff --git a/apex/vndk.go b/apex/vndk.go
new file mode 100644
index 0000000..f2e913e
--- /dev/null
+++ b/apex/vndk.go
@@ -0,0 +1,156 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package apex
+
+import (
+	"path/filepath"
+	"strings"
+	"sync"
+
+	"android/soong/android"
+	"android/soong/cc"
+
+	"github.com/google/blueprint/proptools"
+)
+
+const (
+	vndkApexName       = "com.android.vndk"
+	vndkApexNamePrefix = vndkApexName + ".v"
+)
+
+// apex_vndk creates a special variant of apex modules which contains only VNDK libraries.
+// If `vndk_version` is specified, the VNDK libraries of the specified VNDK version are gathered automatically.
+// If not specified, then the "current" versions are gathered.
+func vndkApexBundleFactory() android.Module {
+	bundle := newApexBundle()
+	bundle.vndkApex = true
+	bundle.AddProperties(&bundle.vndkProperties)
+	android.AddLoadHook(bundle, func(ctx android.LoadHookContext) {
+		ctx.AppendProperties(&struct {
+			Compile_multilib *string
+		}{
+			proptools.StringPtr("both"),
+		})
+	})
+	return bundle
+}
+
+func (a *apexBundle) vndkVersion(config android.DeviceConfig) string {
+	vndkVersion := proptools.StringDefault(a.vndkProperties.Vndk_version, "current")
+	if vndkVersion == "current" {
+		vndkVersion = config.PlatformVndkVersion()
+	}
+	return vndkVersion
+}
+
+type apexVndkProperties struct {
+	// Indicates VNDK version of which this VNDK APEX bundles VNDK libs. Default is Platform VNDK Version.
+	Vndk_version *string
+}
+
+var (
+	vndkApexListKey   = android.NewOnceKey("vndkApexList")
+	vndkApexListMutex sync.Mutex
+)
+
+func vndkApexList(config android.Config) map[string]string {
+	return config.Once(vndkApexListKey, func() interface{} {
+		return map[string]string{}
+	}).(map[string]string)
+}
+
+func apexVndkMutator(mctx android.TopDownMutatorContext) {
+	if ab, ok := mctx.Module().(*apexBundle); ok && ab.vndkApex {
+		if ab.IsNativeBridgeSupported() {
+			mctx.PropertyErrorf("native_bridge_supported", "%q doesn't support native bridge binary.", mctx.ModuleType())
+		}
+
+		vndkVersion := ab.vndkVersion(mctx.DeviceConfig())
+		// Ensure VNDK APEX mount point is formatted as com.android.vndk.v###
+		ab.properties.Apex_name = proptools.StringPtr(vndkApexNamePrefix + vndkVersion)
+
+		// vndk_version should be unique
+		vndkApexListMutex.Lock()
+		defer vndkApexListMutex.Unlock()
+		vndkApexList := vndkApexList(mctx.Config())
+		if other, ok := vndkApexList[vndkVersion]; ok {
+			mctx.PropertyErrorf("vndk_version", "%v is already defined in %q", vndkVersion, other)
+		}
+		vndkApexList[vndkVersion] = mctx.ModuleName()
+	}
+}
+
+func apexVndkDepsMutator(mctx android.BottomUpMutatorContext) {
+	if m, ok := mctx.Module().(*cc.Module); ok && cc.IsForVndkApex(mctx, m) {
+		vndkVersion := m.VndkVersion()
+		vndkApexList := vndkApexList(mctx.Config())
+		if vndkApex, ok := vndkApexList[vndkVersion]; ok {
+			mctx.AddReverseDependency(mctx.Module(), sharedLibTag, vndkApex)
+		}
+	} else if a, ok := mctx.Module().(*apexBundle); ok && a.vndkApex {
+		vndkVersion := proptools.StringDefault(a.vndkProperties.Vndk_version, "current")
+		mctx.AddDependency(mctx.Module(), prebuiltTag, cc.VndkLibrariesTxtModules(vndkVersion)...)
+	}
+}
+
+// name is module.BaseModuleName() which is used as LOCAL_MODULE_NAME and also LOCAL_OVERRIDES_*
+func makeCompatSymlinks(name string, ctx android.ModuleContext) (symlinks []string) {
+	// small helper to add symlink commands
+	addSymlink := func(target, dir, linkName string) {
+		link := filepath.Join(dir, linkName)
+		symlinks = append(symlinks, "mkdir -p "+dir+" && rm -rf "+link+" && ln -sf "+target+" "+link)
+	}
+
+	// TODO(b/142911355): [VNDK APEX] Fix hard-coded references to /system/lib/vndk
+	// When all hard-coded references are fixed, remove symbolic links
+	// Note that  we should keep following symlinks for older VNDKs (<=29)
+	// Since prebuilt vndk libs still depend on system/lib/vndk path
+	if strings.HasPrefix(name, vndkApexName) {
+		vndkVersion := ctx.DeviceConfig().PlatformVndkVersion()
+		if strings.HasPrefix(name, vndkApexNamePrefix) {
+			vndkVersion = strings.TrimPrefix(name, vndkApexNamePrefix)
+		}
+		// the name of vndk apex is formatted "com.android.vndk.v" + version
+		apexName := vndkApexNamePrefix + vndkVersion
+		if ctx.Config().Android64() {
+			addSymlink("/apex/"+apexName+"/lib64", "$(TARGET_OUT)/lib64", "vndk-sp-"+vndkVersion)
+			addSymlink("/apex/"+apexName+"/lib64", "$(TARGET_OUT)/lib64", "vndk-"+vndkVersion)
+		}
+		if !ctx.Config().Android64() || ctx.DeviceConfig().DeviceSecondaryArch() != "" {
+			addSymlink("/apex/"+apexName+"/lib", "$(TARGET_OUT)/lib", "vndk-sp-"+vndkVersion)
+			addSymlink("/apex/"+apexName+"/lib", "$(TARGET_OUT)/lib", "vndk-"+vndkVersion)
+		}
+		return
+	}
+
+	// http://b/121248172 - create a link from /system/usr/icu to
+	// /apex/com.android.i18n/etc/icu so that apps can find the ICU .dat file.
+	// A symlink can't overwrite a directory and the /system/usr/icu directory once
+	// existed so the required structure must be created whatever we find.
+	if name == "com.android.i18n" {
+		addSymlink("/apex/com.android.i18n/etc/icu", "$(TARGET_OUT)/usr", "icu")
+		return
+	}
+
+	// TODO(b/124106384): Clean up compat symlinks for ART binaries.
+	if strings.HasPrefix(name, "com.android.art.") {
+		artBinaries := []string{"dalvikvm", "dex2oat"}
+		for _, b := range artBinaries {
+			addSymlink("/apex/com.android.art/bin/"+b, "$(TARGET_OUT)/bin", b)
+		}
+		return
+	}
+	return
+}
diff --git a/bpf/bpf.go b/bpf/bpf.go
index 90ec963..024fcbc 100644
--- a/bpf/bpf.go
+++ b/bpf/bpf.go
@@ -33,7 +33,7 @@
 var (
 	pctx = android.NewPackageContext("android/soong/bpf")
 
-	ccRule = pctx.AndroidGomaStaticRule("ccRule",
+	ccRule = pctx.AndroidRemoteStaticRule("ccRule", android.SUPPORTS_GOMA,
 		blueprint.RuleParams{
 			Depfile:     "${out}.d",
 			Deps:        blueprint.DepsGCC,
diff --git a/bpf/bpf_test.go b/bpf/bpf_test.go
index cbb251f..eeca057 100644
--- a/bpf/bpf_test.go
+++ b/bpf/bpf_test.go
@@ -48,21 +48,24 @@
 	os.Exit(run())
 }
 
-func testContext(bp string) *android.TestContext {
+func testConfig(buildDir string, env map[string]string, bp string) android.Config {
 	mockFS := map[string][]byte{
 		"bpf.c":       nil,
 		"BpfTest.cpp": nil,
 	}
 
-	ctx := cc.CreateTestContext(bp, mockFS, android.Android)
-	ctx.RegisterModuleType("bpf", android.ModuleFactoryAdaptor(bpfFactory))
-	ctx.Register()
+	return cc.TestConfig(buildDir, android.Android, env, bp, mockFS)
+}
+
+func testContext(config android.Config) *android.TestContext {
+	ctx := cc.CreateTestContext()
+	ctx.RegisterModuleType("bpf", bpfFactory)
+	ctx.Register(config)
 
 	return ctx
 }
 
 func TestBpfDataDependency(t *testing.T) {
-	config := android.TestArchConfig(buildDir, nil)
 	bp := `
 		bpf {
 			name: "bpf.o",
@@ -77,7 +80,9 @@
 		}
 	`
 
-	ctx := testContext(bp)
+	config := testConfig(buildDir, nil, bp)
+	ctx := testContext(config)
+
 	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
 	if errs == nil {
 		_, errs = ctx.PrepareBuildActions(config)
diff --git a/bpfix/Android.bp b/bpfix/Android.bp
index 90a453d..e291578 100644
--- a/bpfix/Android.bp
+++ b/bpfix/Android.bp
@@ -19,7 +19,18 @@
 blueprint_go_binary {
     name: "bpfix",
     srcs: [
-        "cmd/bpfix.go",
+        "cmd/main.go",
+    ],
+    deps: [
+        "bpfix-cmd",
+    ],
+}
+
+bootstrap_go_package {
+    name: "bpfix-cmd",
+    pkgPath: "android/soong/bpfix/cmd_lib",
+    srcs: [
+        "cmd_lib/bpfix.go",
     ],
     deps: [
         "bpfix-lib",
diff --git a/bpfix/bpfix/bpfix.go b/bpfix/bpfix/bpfix.go
index cac4d9a..4633aa6 100644
--- a/bpfix/bpfix/bpfix.go
+++ b/bpfix/bpfix/bpfix.go
@@ -45,66 +45,80 @@
 // A FixRequest specifies the details of which fixes to apply to an individual file
 // A FixRequest doesn't specify whether to do a dry run or where to write the results; that's in cmd/bpfix.go
 type FixRequest struct {
-	steps []fixStep
+	steps []FixStep
+}
+type FixStepsExtension struct {
+	Name  string
+	Steps []FixStep
 }
 
-type fixStep struct {
-	name string
-	fix  func(f *Fixer) error
+type FixStep struct {
+	Name string
+	Fix  func(f *Fixer) error
 }
 
-var fixSteps = []fixStep{
+var fixStepsExtensions = []*FixStepsExtension(nil)
+
+func RegisterFixStepExtension(extension *FixStepsExtension) {
+	fixStepsExtensions = append(fixStepsExtensions, extension)
+}
+
+var fixSteps = []FixStep{
 	{
-		name: "simplifyKnownRedundantVariables",
-		fix:  runPatchListMod(simplifyKnownPropertiesDuplicatingEachOther),
+		Name: "simplifyKnownRedundantVariables",
+		Fix:  runPatchListMod(simplifyKnownPropertiesDuplicatingEachOther),
 	},
 	{
-		name: "rewriteIncorrectAndroidmkPrebuilts",
-		fix:  rewriteIncorrectAndroidmkPrebuilts,
+		Name: "rewriteIncorrectAndroidmkPrebuilts",
+		Fix:  rewriteIncorrectAndroidmkPrebuilts,
 	},
 	{
-		name: "rewriteCtsModuleTypes",
-		fix:  rewriteCtsModuleTypes,
+		Name: "rewriteCtsModuleTypes",
+		Fix:  rewriteCtsModuleTypes,
 	},
 	{
-		name: "rewriteIncorrectAndroidmkAndroidLibraries",
-		fix:  rewriteIncorrectAndroidmkAndroidLibraries,
+		Name: "rewriteIncorrectAndroidmkAndroidLibraries",
+		Fix:  rewriteIncorrectAndroidmkAndroidLibraries,
 	},
 	{
-		name: "rewriteTestModuleTypes",
-		fix:  rewriteTestModuleTypes,
+		Name: "rewriteTestModuleTypes",
+		Fix:  rewriteTestModuleTypes,
 	},
 	{
-		name: "rewriteAndroidmkJavaLibs",
-		fix:  rewriteAndroidmkJavaLibs,
+		Name: "rewriteAndroidmkJavaLibs",
+		Fix:  rewriteAndroidmkJavaLibs,
 	},
 	{
-		name: "rewriteJavaStaticLibs",
-		fix:  rewriteJavaStaticLibs,
+		Name: "rewriteJavaStaticLibs",
+		Fix:  rewriteJavaStaticLibs,
 	},
 	{
-		name: "rewritePrebuiltEtc",
-		fix:  rewriteAndroidmkPrebuiltEtc,
+		Name: "rewritePrebuiltEtc",
+		Fix:  rewriteAndroidmkPrebuiltEtc,
 	},
 	{
-		name: "mergeMatchingModuleProperties",
-		fix:  runPatchListMod(mergeMatchingModuleProperties),
+		Name: "mergeMatchingModuleProperties",
+		Fix:  runPatchListMod(mergeMatchingModuleProperties),
 	},
 	{
-		name: "reorderCommonProperties",
-		fix:  runPatchListMod(reorderCommonProperties),
+		Name: "reorderCommonProperties",
+		Fix:  runPatchListMod(reorderCommonProperties),
 	},
 	{
-		name: "removeTags",
-		fix:  runPatchListMod(removeTags),
+		Name: "removeTags",
+		Fix:  runPatchListMod(removeTags),
 	},
 	{
-		name: "rewriteAndroidTest",
-		fix:  rewriteAndroidTest,
+		Name: "rewriteAndroidTest",
+		Fix:  rewriteAndroidTest,
 	},
 	{
-		name: "rewriteAndroidAppImport",
-		fix:  rewriteAndroidAppImport,
+		Name: "rewriteAndroidAppImport",
+		Fix:  rewriteAndroidAppImport,
+	},
+	{
+		Name: "removeEmptyLibDependencies",
+		Fix:  removeEmptyLibDependencies,
 	},
 }
 
@@ -113,8 +127,27 @@
 }
 
 func (r FixRequest) AddAll() (result FixRequest) {
-	result.steps = append([]fixStep(nil), r.steps...)
+	result.steps = append([]FixStep(nil), r.steps...)
 	result.steps = append(result.steps, fixSteps...)
+	for _, extension := range fixStepsExtensions {
+		result.steps = append(result.steps, extension.Steps...)
+	}
+	return result
+}
+
+func (r FixRequest) AddBase() (result FixRequest) {
+	result.steps = append([]FixStep(nil), r.steps...)
+	result.steps = append(result.steps, fixSteps...)
+	return result
+}
+
+func (r FixRequest) AddMatchingExtensions(pattern string) (result FixRequest) {
+	result.steps = append([]FixStep(nil), r.steps...)
+	for _, extension := range fixStepsExtensions {
+		if match, _ := filepath.Match(pattern, extension.Name); match {
+			result.steps = append(result.steps, extension.Steps...)
+		}
+	}
 	return result
 }
 
@@ -122,6 +155,10 @@
 	tree *parser.File
 }
 
+func (f Fixer) Tree() *parser.File {
+	return f.tree
+}
+
 func NewFixer(tree *parser.File) *Fixer {
 	fixer := &Fixer{tree}
 
@@ -198,7 +235,7 @@
 
 func (f *Fixer) fixTreeOnce(config FixRequest) error {
 	for _, fix := range config.steps {
-		err := fix.fix(f)
+		err := fix.Fix(f)
 		if err != nil {
 			return err
 		}
@@ -508,15 +545,15 @@
 	"TARGET_OUT": {{prefix: "/usr/share", modType: "prebuilt_usr_share"}, {prefix: "/fonts", modType: "prebuilt_font"},
 		{prefix: "/etc/firmware", modType: "prebuilt_firmware"}, {prefix: "/vendor/firmware", modType: "prebuilt_firmware", flags: []string{"proprietary"}},
 		{prefix: "/etc"}},
-	"TARGET_OUT_ETC":                  {{prefix: "/firmware", modType: "prebuilt_firmware"}, {prefix: ""}},
-	"TARGET_OUT_PRODUCT":              {{prefix: "/etc", flags: []string{"product_specific"}}, {prefix: "/fonts", modType: "prebuilt_font", flags: []string{"product_specific"}}},
-	"TARGET_OUT_PRODUCT_ETC":          {{prefix: "", flags: []string{"product_specific"}}},
-	"TARGET_OUT_ODM":                  {{prefix: "/etc", flags: []string{"device_specific"}}},
-	"TARGET_OUT_PRODUCT_SERVICES":     {{prefix: "/etc", flags: []string{"product_services_specific"}}},
-	"TARGET_OUT_PRODUCT_SERVICES_ETC": {{prefix: "", flags: []string{"product_services_specific"}}},
-	"TARGET_OUT_VENDOR":               {{prefix: "/etc", flags: []string{"proprietary"}}, {prefix: "/firmware", modType: "prebuilt_firmware", flags: []string{"proprietary"}}},
-	"TARGET_OUT_VENDOR_ETC":           {{prefix: "", flags: []string{"proprietary"}}},
-	"TARGET_RECOVERY_ROOT_OUT":        {{prefix: "/system/etc", flags: []string{"recovery"}}},
+	"TARGET_OUT_ETC":            {{prefix: "/firmware", modType: "prebuilt_firmware"}, {prefix: ""}},
+	"TARGET_OUT_PRODUCT":        {{prefix: "/etc", flags: []string{"product_specific"}}, {prefix: "/fonts", modType: "prebuilt_font", flags: []string{"product_specific"}}},
+	"TARGET_OUT_PRODUCT_ETC":    {{prefix: "", flags: []string{"product_specific"}}},
+	"TARGET_OUT_ODM":            {{prefix: "/etc", flags: []string{"device_specific"}}},
+	"TARGET_OUT_SYSTEM_EXT":     {{prefix: "/etc", flags: []string{"system_ext_specific"}}},
+	"TARGET_OUT_SYSTEM_EXT_ETC": {{prefix: "", flags: []string{"system_ext_specific"}}},
+	"TARGET_OUT_VENDOR":         {{prefix: "/etc", flags: []string{"proprietary"}}, {prefix: "/firmware", modType: "prebuilt_firmware", flags: []string{"proprietary"}}},
+	"TARGET_OUT_VENDOR_ETC":     {{prefix: "", flags: []string{"proprietary"}}},
+	"TARGET_RECOVERY_ROOT_OUT":  {{prefix: "/system/etc", flags: []string{"recovery"}}},
 }
 
 // rewriteAndroidPrebuiltEtc fixes prebuilt_etc rule
@@ -617,6 +654,50 @@
 	return nil
 }
 
+// Removes library dependencies which are empty (and restricted from usage in Soong)
+func removeEmptyLibDependencies(f *Fixer) error {
+	emptyLibraries := []string{
+		"libhidltransport",
+		"libhwbinder",
+	}
+	relevantFields := []string{
+		"export_shared_lib_headers",
+		"export_static_lib_headers",
+		"static_libs",
+		"whole_static_libs",
+		"shared_libs",
+	}
+	for _, def := range f.tree.Defs {
+		mod, ok := def.(*parser.Module)
+		if !ok {
+			continue
+		}
+		for _, field := range relevantFields {
+			listValue, ok := getLiteralListProperty(mod, field)
+			if !ok {
+				continue
+			}
+			newValues := []parser.Expression{}
+			for _, v := range listValue.Values {
+				stringValue, ok := v.(*parser.String)
+				if !ok {
+					return fmt.Errorf("Expecting string for %s.%s fields", mod.Type, field)
+				}
+				if inList(stringValue.Value, emptyLibraries) {
+					continue
+				}
+				newValues = append(newValues, stringValue)
+			}
+			if len(newValues) == 0 && len(listValue.Values) != 0 {
+				removeProperty(mod, field)
+			} else {
+				listValue.Values = newValues
+			}
+		}
+	}
+	return nil
+}
+
 // Converts the default source list property, 'srcs', to a single source property with a given name.
 // "LOCAL_MODULE" reference is also resolved during the conversion process.
 func convertToSingleSource(mod *parser.Module, srcPropertyName string) {
@@ -1051,3 +1132,12 @@
 	}
 	mod.Properties = newList
 }
+
+func inList(s string, list []string) bool {
+	for _, v := range list {
+		if s == v {
+			return true
+		}
+	}
+	return false
+}
diff --git a/bpfix/bpfix/bpfix_test.go b/bpfix/bpfix/bpfix_test.go
index 5e0b817..032282f 100644
--- a/bpfix/bpfix/bpfix_test.go
+++ b/bpfix/bpfix/bpfix_test.go
@@ -833,3 +833,57 @@
 		})
 	}
 }
+
+func TestRemoveEmptyLibDependencies(t *testing.T) {
+	tests := []struct {
+		name string
+		in   string
+		out  string
+	}{
+		{
+			name: "remove sole shared lib",
+			in: `
+				cc_library {
+					name: "foo",
+					shared_libs: ["libhwbinder"],
+				}
+			`,
+			out: `
+				cc_library {
+					name: "foo",
+
+				}
+			`,
+		},
+		{
+			name: "remove a shared lib",
+			in: `
+				cc_library {
+					name: "foo",
+					shared_libs: [
+						"libhwbinder",
+						"libfoo",
+						"libhidltransport",
+					],
+				}
+			`,
+			out: `
+				cc_library {
+					name: "foo",
+					shared_libs: [
+
+						"libfoo",
+
+					],
+				}
+			`,
+		},
+	}
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			runPass(t, test.in, test.out, func(fixer *Fixer) error {
+				return removeEmptyLibDependencies(fixer)
+			})
+		})
+	}
+}
diff --git a/bpfix/cmd/main.go b/bpfix/cmd/main.go
new file mode 100644
index 0000000..ad68144
--- /dev/null
+++ b/bpfix/cmd/main.go
@@ -0,0 +1,23 @@
+// Copyright 2017 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.
+
+// This file provides a wrapper to the bpfix command-line library
+
+package main
+
+import "android/soong/bpfix/cmd_lib"
+
+func main() {
+	cmd_lib.Run()
+}
diff --git a/bpfix/cmd/bpfix.go b/bpfix/cmd_lib/bpfix.go
similarity index 97%
rename from bpfix/cmd/bpfix.go
rename to bpfix/cmd_lib/bpfix.go
index ccdae16..f90f65b 100644
--- a/bpfix/cmd/bpfix.go
+++ b/bpfix/cmd_lib/bpfix.go
@@ -12,11 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// This file provides a command-line interface to bpfix
+// This file provides a bpfix command-line library
 
 // TODO(jeffrygaston) should this file be consolidated with bpfmt.go?
 
-package main
+package cmd_lib
 
 import (
 	"bytes"
@@ -128,7 +128,7 @@
 	filepath.Walk(path, makeFileVisitor(fixRequest))
 }
 
-func main() {
+func Run() {
 	flag.Parse()
 
 	fixRequest := bpfix.NewFixRequest().AddAll()
diff --git a/build_kzip.bash b/build_kzip.bash
new file mode 100755
index 0000000..02b346d
--- /dev/null
+++ b/build_kzip.bash
@@ -0,0 +1,38 @@
+#! /bin/bash -uv
+#
+# Build kzip files (source files for the indexing pipeline) for the given configuration,
+# merge them and place the resulting all.kzip into $DIST_DIR.
+# It is assumed that the current directory is the top of the source tree.
+# The following environment variables affect the result:
+#   BUILD_NUMBER          build number, used to generate unique ID (will use UUID if not set)
+#   DIST_DIR              where the resulting all.kzip will be placed
+#   OUT_DIR               output directory (out if not specified})
+#   TARGET_BUILD_VARIANT  variant, e.g., `userdebug`
+#   TARGET_PRODUCT        target device name, e.g., 'aosp_blueline'
+#   XREF_CORPUS           source code repository URI, e.g., 'android.googlesource.com/platform/superproject'
+
+: ${BUILD_NUMBER:=$(uuidgen)}
+
+# The extraction might fail for some source files, so run with -k and then check that
+# sufficiently many files were generated.
+declare -r out="${OUT_DIR:-out}"
+# Build extraction files for C++ and Java. Build `merge_zips` which we use later.
+build/soong/soong_ui.bash --build-mode --all-modules --dir=$PWD -k merge_zips xref_cxx xref_java
+#Build extraction file for Go files in build/soong directory.
+declare -r abspath_out=$(realpath "${out}")
+(cd build/soong;
+ ../../prebuilts/build-tools/linux-x86/bin/go_extractor \
+    --goroot="${PWD}/../../prebuilts/go/linux-x86" \
+    --rules=vnames.go.json \
+    --canonicalize_package_corpus \
+    --output "${abspath_out}/soong/all.go.kzip" \
+    ./... )
+
+declare -r kzip_count=$(find "$out" -name '*.kzip' | wc -l)
+(($kzip_count>100000)) || { printf "Too few kzip files were generated: %d\n" $kzip_count; exit 1; }
+
+# Pack
+# TODO(asmundak): this should be done by soong.
+declare -r allkzip="$BUILD_NUMBER.kzip"
+"$out/soong/host/linux-x86/bin/merge_zips" "$DIST_DIR/$allkzip" @<(find "$out" -name '*.kzip')
+
diff --git a/cc/androidmk.go b/cc/androidmk.go
index 272d3d4..ff88091 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -25,6 +25,7 @@
 
 var (
 	nativeBridgeSuffix = ".native_bridge"
+	productSuffix      = ".product"
 	vendorSuffix       = ".vendor"
 	recoverySuffix     = ".recovery"
 )
@@ -36,9 +37,10 @@
 	Arch() android.Arch
 	Os() android.OsType
 	Host() bool
-	useVndk() bool
+	UseVndk() bool
+	VndkVersion() string
 	static() bool
-	inRecovery() bool
+	InRecovery() bool
 }
 
 type subAndroidMkProvider interface {
@@ -68,7 +70,7 @@
 		OutputFile: c.outputFile,
 		// TODO(jiyong): add the APEXes providing shared libs to the required modules
 		// Currently, adding c.Properties.ApexesProvidingSharedLibs is causing multiple
-		// runtime APEXes (com.android.runtime.debug|release) to be installed. And this
+		// ART APEXes (com.android.art.debug|release) to be installed. And this
 		// is breaking some older devices (like marlin) where system.img is small.
 		Required: c.Properties.AndroidMkRuntimeLibs,
 		Include:  "$(BUILD_SYSTEM)/soong_cc_prebuilt.mk",
@@ -88,10 +90,15 @@
 					fmt.Fprintln(w, "LOCAL_WHOLE_STATIC_LIBRARIES := "+strings.Join(c.Properties.AndroidMkWholeStaticLibs, " "))
 				}
 				fmt.Fprintln(w, "LOCAL_SOONG_LINK_TYPE :=", c.makeLinkType)
-				if c.useVndk() {
+				if c.UseVndk() {
 					fmt.Fprintln(w, "LOCAL_USE_VNDK := true")
-					if c.isVndk() && !c.static() {
-						fmt.Fprintln(w, "LOCAL_SOONG_VNDK_VERSION := "+c.vndkVersion())
+					if c.IsVndk() && !c.static() {
+						fmt.Fprintln(w, "LOCAL_SOONG_VNDK_VERSION := "+c.VndkVersion())
+						// VNDK libraries available to vendor are not installed because
+						// they are packaged in VNDK APEX and installed by APEX packages (apex/apex.go)
+						if !c.isVndkExt() {
+							fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := true")
+						}
 					}
 				}
 			},
@@ -109,17 +116,7 @@
 	}
 	c.subAndroidMk(&ret, c.installer)
 
-	if c.Target().NativeBridge == android.NativeBridgeEnabled {
-		ret.SubName += nativeBridgeSuffix
-	}
-
-	if c.useVndk() && c.hasVendorVariant() {
-		// .vendor suffix is added only when we will have two variants: core and vendor.
-		// The suffix is not added for vendor-only module.
-		ret.SubName += vendorSuffix
-	} else if c.inRecovery() && !c.onlyInRecovery() {
-		ret.SubName += recoverySuffix
-	}
+	ret.SubName += c.Properties.SubName
 
 	return ret
 }
@@ -157,10 +154,10 @@
 func (library *libraryDecorator) androidMkWriteExportedFlags(w io.Writer) {
 	exportedFlags := library.exportedFlags()
 	for _, dir := range library.exportedDirs() {
-		exportedFlags = append(exportedFlags, "-I"+dir)
+		exportedFlags = append(exportedFlags, "-I"+dir.String())
 	}
 	for _, dir := range library.exportedSystemDirs() {
-		exportedFlags = append(exportedFlags, "-isystem "+dir)
+		exportedFlags = append(exportedFlags, "-isystem "+dir.String())
 	}
 	if len(exportedFlags) > 0 {
 		fmt.Fprintln(w, "LOCAL_EXPORT_CFLAGS :=", strings.Join(exportedFlags, " "))
@@ -207,7 +204,7 @@
 		library.androidMkWriteExportedFlags(w)
 		library.androidMkWriteAdditionalDependenciesForSourceAbiDiff(w)
 
-		_, _, ext := splitFileExt(outputFile.Base())
+		_, _, ext := android.SplitFileExt(outputFile.Base())
 
 		fmt.Fprintln(w, "LOCAL_BUILT_MODULE_STEM := $(LOCAL_MODULE)"+ext)
 
@@ -233,7 +230,7 @@
 		})
 	}
 	if len(library.Properties.Stubs.Versions) > 0 &&
-		android.DirectlyInAnyApex(ctx, ctx.Name()) && !ctx.inRecovery() && !ctx.useVndk() &&
+		android.DirectlyInAnyApex(ctx, ctx.Name()) && !ctx.InRecovery() && !ctx.UseVndk() &&
 		!ctx.static() {
 		if !library.buildStubs() {
 			ret.SubName = ".bootstrap"
@@ -287,6 +284,9 @@
 			fmt.Fprintln(w, "LOCAL_FULL_TEST_CONFIG :=", benchmark.testConfig.String())
 		}
 		fmt.Fprintln(w, "LOCAL_NATIVE_BENCHMARK := true")
+		if !BoolDefault(benchmark.Properties.Auto_gen_config, true) {
+			fmt.Fprintln(w, "LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG := true")
+		}
 	})
 
 	androidMkWriteTestData(benchmark.data, ctx, ret)
@@ -307,11 +307,50 @@
 		if test.testConfig != nil {
 			fmt.Fprintln(w, "LOCAL_FULL_TEST_CONFIG :=", test.testConfig.String())
 		}
+		if !BoolDefault(test.Properties.Auto_gen_config, true) {
+			fmt.Fprintln(w, "LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG := true")
+		}
 	})
 
 	androidMkWriteTestData(test.data, ctx, ret)
 }
 
+func (fuzz *fuzzBinary) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
+	ctx.subAndroidMk(ret, fuzz.binaryDecorator)
+
+	var fuzzFiles []string
+	for _, d := range fuzz.corpus {
+		fuzzFiles = append(fuzzFiles,
+			filepath.Dir(fuzz.corpusIntermediateDir.String())+":corpus/"+d.Base())
+	}
+
+	for _, d := range fuzz.data {
+		fuzzFiles = append(fuzzFiles,
+			filepath.Dir(fuzz.dataIntermediateDir.String())+":data/"+d.Rel())
+	}
+
+	if fuzz.dictionary != nil {
+		fuzzFiles = append(fuzzFiles,
+			filepath.Dir(fuzz.dictionary.String())+":"+fuzz.dictionary.Base())
+	}
+
+	if fuzz.config != nil {
+		fuzzFiles = append(fuzzFiles,
+			filepath.Dir(fuzz.config.String())+":config.json")
+	}
+
+	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
+		fmt.Fprintln(w, "LOCAL_IS_FUZZ_TARGET := true")
+		if len(fuzzFiles) > 0 {
+			fmt.Fprintln(w, "LOCAL_TEST_DATA := "+strings.Join(fuzzFiles, " "))
+		}
+		if fuzz.installedSharedDeps != nil {
+			fmt.Fprintln(w, "LOCAL_FUZZ_INSTALLED_SHARED_DEPS :="+
+				strings.Join(fuzz.installedSharedDeps, " "))
+		}
+	})
+}
+
 func (test *testLibrary) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
 	ctx.subAndroidMk(ret, test.libraryDecorator)
 }
@@ -319,7 +358,7 @@
 func (library *toolchainLibraryDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
 	ret.Class = "STATIC_LIBRARIES"
 	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
-		_, suffix, _ := splitFileExt(outputFile.Base())
+		_, suffix, _ := android.SplitFileExt(outputFile.Base())
 		fmt.Fprintln(w, "LOCAL_MODULE_SUFFIX := "+suffix)
 	})
 }
@@ -332,11 +371,10 @@
 	}
 
 	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
-		path := installer.path.RelPathString()
-		dir, file := filepath.Split(path)
-		stem, suffix, _ := splitFileExt(file)
+		path, file := filepath.Split(installer.path.ToMakePath().String())
+		stem, suffix, _ := android.SplitFileExt(file)
 		fmt.Fprintln(w, "LOCAL_MODULE_SUFFIX := "+suffix)
-		fmt.Fprintln(w, "LOCAL_MODULE_PATH := $(OUT_DIR)/"+filepath.Clean(dir))
+		fmt.Fprintln(w, "LOCAL_MODULE_PATH := "+path)
 		fmt.Fprintln(w, "LOCAL_MODULE_STEM := "+stem)
 	})
 }
@@ -347,7 +385,7 @@
 
 	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
 		path, file := filepath.Split(c.installPath.String())
-		stem, suffix, _ := splitFileExt(file)
+		stem, suffix, _ := android.SplitFileExt(file)
 		fmt.Fprintln(w, "LOCAL_MODULE_SUFFIX := "+suffix)
 		fmt.Fprintln(w, "LOCAL_MODULE_PATH := "+path)
 		fmt.Fprintln(w, "LOCAL_MODULE_STEM := "+stem)
@@ -357,11 +395,10 @@
 
 func (c *llndkStubDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
 	ret.Class = "SHARED_LIBRARIES"
-	ret.SubName = vendorSuffix
 
 	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
 		c.libraryDecorator.androidMkWriteExportedFlags(w)
-		_, _, ext := splitFileExt(outputFile.Base())
+		_, _, ext := android.SplitFileExt(outputFile.Base())
 
 		fmt.Fprintln(w, "LOCAL_BUILT_MODULE_STEM := $(LOCAL_MODULE)"+ext)
 		fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := true")
@@ -378,12 +415,11 @@
 	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
 		c.libraryDecorator.androidMkWriteExportedFlags(w)
 
-		path := c.path.RelPathString()
-		dir, file := filepath.Split(path)
-		stem, suffix, ext := splitFileExt(file)
+		path, file := filepath.Split(c.path.ToMakePath().String())
+		stem, suffix, ext := android.SplitFileExt(file)
 		fmt.Fprintln(w, "LOCAL_BUILT_MODULE_STEM := $(LOCAL_MODULE)"+ext)
 		fmt.Fprintln(w, "LOCAL_MODULE_SUFFIX := "+suffix)
-		fmt.Fprintln(w, "LOCAL_MODULE_PATH := $(OUT_DIR)/"+filepath.Clean(dir))
+		fmt.Fprintln(w, "LOCAL_MODULE_PATH := "+path)
 		fmt.Fprintln(w, "LOCAL_MODULE_STEM := "+stem)
 	})
 }
@@ -398,7 +434,7 @@
 
 	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
 		c.libraryDecorator.androidMkWriteExportedFlags(w)
-		_, _, ext := splitFileExt(outputFile.Base())
+		_, _, ext := android.SplitFileExt(outputFile.Base())
 
 		fmt.Fprintln(w, "LOCAL_BUILT_MODULE_STEM := $(LOCAL_MODULE)"+ext)
 		fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := true")
diff --git a/cc/binary.go b/cc/binary.go
index 1757f1c..617d4dd 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -50,6 +50,9 @@
 	// binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will be removed
 	// from PRODUCT_PACKAGES.
 	Overrides []string
+
+	// Inject boringssl hash into the shared library.  This is only intended for use by external/boringssl.
+	Inject_bssl_hash *bool `android:"arch_variant"`
 }
 
 func init() {
@@ -155,7 +158,7 @@
 
 		if binary.static() {
 			if ctx.selectedStl() == "libc++_static" {
-				deps.StaticLibs = append(deps.StaticLibs, "libm", "libc", "libdl")
+				deps.StaticLibs = append(deps.StaticLibs, "libm", "libc")
 			}
 			// static libraries libcompiler_rt, libc and libc_nomalloc need to be linked with
 			// --start-group/--end-group along with libgcc.  If they are in deps.StaticLibs,
@@ -224,7 +227,7 @@
 
 	if ctx.Host() && !ctx.Windows() && !binary.static() {
 		if !ctx.Config().IsEnvTrue("DISABLE_HOST_PIE") {
-			flags.LdFlags = append(flags.LdFlags, "-pie")
+			flags.Global.LdFlags = append(flags.Global.LdFlags, "-pie")
 		}
 	}
 
@@ -232,7 +235,7 @@
 	// all code is position independent, and then those warnings get promoted to
 	// errors.
 	if !ctx.Windows() {
-		flags.CFlags = append(flags.CFlags, "-fPIE")
+		flags.Global.CFlags = append(flags.Global.CFlags, "-fPIE")
 	}
 
 	if ctx.toolchain().Bionic() {
@@ -241,11 +244,11 @@
 			// However, bionic/linker uses -shared to overwrite.
 			// Linker for x86 targets does not allow coexistance of -static and -shared,
 			// so we add -static only if -shared is not used.
-			if !inList("-shared", flags.LdFlags) {
-				flags.LdFlags = append(flags.LdFlags, "-static")
+			if !inList("-shared", flags.Local.LdFlags) {
+				flags.Global.LdFlags = append(flags.Global.LdFlags, "-static")
 			}
 
-			flags.LdFlags = append(flags.LdFlags,
+			flags.Global.LdFlags = append(flags.Global.LdFlags,
 				"-nostdlib",
 				"-Bstatic",
 				"-Wl,--gc-sections",
@@ -275,14 +278,14 @@
 				if ctx.Os() == android.LinuxBionic {
 					// Use the dlwrap entry point, but keep _start around so
 					// that it can be used by host_bionic_inject
-					flags.LdFlags = append(flags.LdFlags,
+					flags.Global.LdFlags = append(flags.Global.LdFlags,
 						"-Wl,--entry=__dlwrap__start",
 						"-Wl,--undefined=_start",
 					)
 				}
 			}
 
-			flags.LdFlags = append(flags.LdFlags,
+			flags.Global.LdFlags = append(flags.Global.LdFlags,
 				"-pie",
 				"-nostdlib",
 				"-Bdynamic",
@@ -292,10 +295,10 @@
 		}
 	} else {
 		if binary.static() {
-			flags.LdFlags = append(flags.LdFlags, "-static")
+			flags.Global.LdFlags = append(flags.Global.LdFlags, "-static")
 		}
 		if ctx.Darwin() {
-			flags.LdFlags = append(flags.LdFlags, "-Wl,-headerpad_max_install_names")
+			flags.Global.LdFlags = append(flags.Global.LdFlags, "-Wl,-headerpad_max_install_names")
 		}
 	}
 
@@ -312,14 +315,14 @@
 	var linkerDeps android.Paths
 
 	if deps.LinkerFlagsFile.Valid() {
-		flags.LdFlags = append(flags.LdFlags, "$$(cat "+deps.LinkerFlagsFile.String()+")")
+		flags.Local.LdFlags = append(flags.Local.LdFlags, "$$(cat "+deps.LinkerFlagsFile.String()+")")
 		linkerDeps = append(linkerDeps, deps.LinkerFlagsFile.Path())
 	}
 
 	if flags.DynamicLinker != "" {
-		flags.LdFlags = append(flags.LdFlags, "-Wl,-dynamic-linker,"+flags.DynamicLinker)
+		flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-dynamic-linker,"+flags.DynamicLinker)
 	} else if ctx.toolchain().Bionic() && !binary.static() {
-		flags.LdFlags = append(flags.LdFlags, "-Wl,--no-dynamic-linker")
+		flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--no-dynamic-linker")
 	}
 
 	builderFlags := flagsToBuilderFlags(flags)
@@ -342,6 +345,8 @@
 			flagsToBuilderFlags(flags), afterPrefixSymbols)
 	}
 
+	outputFile = maybeInjectBoringSSLHash(ctx, outputFile, binary.Properties.Inject_bssl_hash, fileName)
+
 	if Bool(binary.baseLinker.Properties.Use_version_lib) {
 		if ctx.Host() {
 			versionedOutputFile := outputFile
@@ -425,6 +430,10 @@
 	return true
 }
 
+func (binary *binaryDecorator) coverageOutputFilePath() android.OptionalPath {
+	return binary.coverageOutputFile
+}
+
 // /system/bin/linker -> /apex/com.android.runtime/bin/linker
 func (binary *binaryDecorator) installSymlinkToRuntimeApex(ctx ModuleContext, file android.Path) {
 	dir := binary.baseInstaller.installDir(ctx)
@@ -444,7 +453,8 @@
 	// Bionic binaries (e.g. linker) is installed to the bootstrap subdirectory.
 	// The original path becomes a symlink to the corresponding file in the
 	// runtime APEX.
-	if installToBootstrap(ctx.baseModuleName(), ctx.Config()) && ctx.Arch().Native && ctx.apexName() == "" && !ctx.inRecovery() {
+	translatedArch := ctx.Target().NativeBridge == android.NativeBridgeEnabled
+	if InstallToBootstrap(ctx.baseModuleName(), ctx.Config()) && !translatedArch && ctx.apexName() == "" && !ctx.inRecovery() {
 		if ctx.Device() && isBionic(ctx.baseModuleName()) {
 			binary.installSymlinkToRuntimeApex(ctx, file)
 		}
diff --git a/cc/builder.go b/cc/builder.go
index 1e12361..1ec323f 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -46,7 +46,7 @@
 var (
 	pctx = android.NewPackageContext("android/soong/cc")
 
-	cc = pctx.AndroidGomaStaticRule("cc",
+	cc = pctx.AndroidRemoteStaticRule("cc", android.SUPPORTS_BOTH,
 		blueprint.RuleParams{
 			Depfile:     "${out}.d",
 			Deps:        blueprint.DepsGCC,
@@ -55,7 +55,7 @@
 		},
 		"ccCmd", "cFlags")
 
-	ccNoDeps = pctx.AndroidGomaStaticRule("ccNoDeps",
+	ccNoDeps = pctx.AndroidRemoteStaticRule("ccNoDeps", android.SUPPORTS_GOMA,
 		blueprint.RuleParams{
 			Command:     "$relPwd ${config.CcWrapper}$ccCmd -c $cFlags -o $out $in",
 			CommandDeps: []string{"$ccCmd"},
@@ -65,20 +65,20 @@
 	ld = pctx.AndroidStaticRule("ld",
 		blueprint.RuleParams{
 			Command: "$ldCmd ${crtBegin} @${out}.rsp " +
-				"${libFlags} ${crtEnd} -o ${out} ${ldFlags}",
+				"${libFlags} ${crtEnd} -o ${out} ${ldFlags} ${extraLibFlags}",
 			CommandDeps:    []string{"$ldCmd"},
 			Rspfile:        "${out}.rsp",
 			RspfileContent: "${in}",
 			// clang -Wl,--out-implib doesn't update its output file if it hasn't changed.
 			Restat: true,
 		},
-		"ldCmd", "crtBegin", "libFlags", "crtEnd", "ldFlags")
+		"ldCmd", "crtBegin", "libFlags", "crtEnd", "ldFlags", "extraLibFlags")
 
 	partialLd = pctx.AndroidStaticRule("partialLd",
 		blueprint.RuleParams{
 			// Without -no-pie, clang 7.0 adds -pie to link Android files,
 			// but -r and -pie cannot be used together.
-			Command:     "$ldCmd -nostdlib -no-pie -Wl,-r ${in} -o ${out} ${ldFlags}",
+			Command:     "$ldCmd -fuse-ld=lld -nostdlib -no-pie -Wl,-r ${in} -o ${out} ${ldFlags}",
 			CommandDeps: []string{"$ldCmd"},
 		},
 		"ldCmd", "ldFlags")
@@ -130,6 +130,17 @@
 		},
 		"args", "crossCompile")
 
+	_ = pctx.SourcePathVariable("archiveRepackPath", "build/soong/scripts/archive_repack.sh")
+
+	archiveRepack = pctx.AndroidStaticRule("archiveRepack",
+		blueprint.RuleParams{
+			Depfile:     "${out}.d",
+			Deps:        blueprint.DepsGCC,
+			Command:     "CLANG_BIN=${config.ClangBin} $archiveRepackPath -i ${in} -o ${out} -d ${out}.d ${objects}",
+			CommandDeps: []string{"$archiveRepackPath"},
+		},
+		"objects")
+
 	emptyFile = pctx.AndroidStaticRule("emptyFile",
 		blueprint.RuleParams{
 			Command: "rm -f $out && touch $out",
@@ -195,7 +206,7 @@
 
 	_ = pctx.SourcePathVariable("sAbiDiffer", "prebuilts/clang-tools/${config.HostPrebuiltTag}/bin/header-abi-diff")
 
-	sAbiDiff = pctx.AndroidRuleFunc("sAbiDiff",
+	sAbiDiff = pctx.RuleFunc("sAbiDiff",
 		func(ctx android.PackageRuleContext) blueprint.RuleParams {
 			// TODO(b/78139997): Add -check-all-apis back
 			commandStr := "($sAbiDiffer ${allowFlags} -lib ${libName} -arch ${arch} -o ${out} -new ${in} -old ${referenceDump})"
@@ -221,6 +232,18 @@
 			Rspfile:        "$out.rsp",
 			RspfileContent: "$in",
 		})
+
+	_ = pctx.SourcePathVariable("cxxExtractor",
+		"prebuilts/clang-tools/${config.HostPrebuiltTag}/bin/cxx_extractor")
+	_ = pctx.SourcePathVariable("kytheVnames", "build/soong/vnames.json")
+	_ = pctx.VariableFunc("kytheCorpus",
+		func(ctx android.PackageVarContext) string { return ctx.Config().XrefCorpusName() })
+	kytheExtract = pctx.StaticRule("kythe",
+		blueprint.RuleParams{
+			Command:     "rm -f $out && KYTHE_CORPUS=${kytheCorpus} KYTHE_OUTPUT_FILE=$out KYTHE_VNAMES=$kytheVnames $cxxExtractor $cFlags $in ",
+			CommandDeps: []string{"$cxxExtractor", "$kytheVnames"},
+		},
+		"cFlags")
 )
 
 func init() {
@@ -238,25 +261,39 @@
 }
 
 type builderFlags struct {
-	globalFlags     string
-	arFlags         string
-	asFlags         string
-	cFlags          string
-	toolingCFlags   string // A separate set of cFlags for clang LibTooling tools
-	toolingCppFlags string // A separate set of cppFlags for clang LibTooling tools
-	conlyFlags      string
-	cppFlags        string
-	ldFlags         string
-	libFlags        string
-	tidyFlags       string
-	sAbiFlags       string
-	yasmFlags       string
-	aidlFlags       string
-	rsFlags         string
-	toolchain       config.Toolchain
-	tidy            bool
-	coverage        bool
-	sAbiDump        bool
+	globalCommonFlags     string
+	globalAsFlags         string
+	globalYasmFlags       string
+	globalCFlags          string
+	globalToolingCFlags   string // A separate set of cFlags for clang LibTooling tools
+	globalToolingCppFlags string // A separate set of cppFlags for clang LibTooling tools
+	globalConlyFlags      string
+	globalCppFlags        string
+	globalLdFlags         string
+
+	localCommonFlags     string
+	localAsFlags         string
+	localYasmFlags       string
+	localCFlags          string
+	localToolingCFlags   string // A separate set of cFlags for clang LibTooling tools
+	localToolingCppFlags string // A separate set of cppFlags for clang LibTooling tools
+	localConlyFlags      string
+	localCppFlags        string
+	localLdFlags         string
+
+	libFlags      string
+	extraLibFlags string
+	tidyFlags     string
+	sAbiFlags     string
+	aidlFlags     string
+	rsFlags       string
+	toolchain     config.Toolchain
+	tidy          bool
+	coverage      bool
+	sAbiDump      bool
+	emitXrefs     bool
+
+	assemblerWithCpp bool
 
 	systemIncludeFlags string
 
@@ -281,6 +318,7 @@
 	tidyFiles     android.Paths
 	coverageFiles android.Paths
 	sAbiDumpFiles android.Paths
+	kytheFiles    android.Paths
 }
 
 func (a Objects) Copy() Objects {
@@ -289,6 +327,7 @@
 		tidyFiles:     append(android.Paths{}, a.tidyFiles...),
 		coverageFiles: append(android.Paths{}, a.coverageFiles...),
 		sAbiDumpFiles: append(android.Paths{}, a.sAbiDumpFiles...),
+		kytheFiles:    append(android.Paths{}, a.kytheFiles...),
 	}
 }
 
@@ -298,6 +337,7 @@
 		tidyFiles:     append(a.tidyFiles, b.tidyFiles...),
 		coverageFiles: append(a.coverageFiles, b.coverageFiles...),
 		sAbiDumpFiles: append(a.sAbiDumpFiles, b.sAbiDumpFiles...),
+		kytheFiles:    append(a.kytheFiles, b.kytheFiles...),
 	}
 }
 
@@ -314,40 +354,50 @@
 	if flags.coverage {
 		coverageFiles = make(android.Paths, 0, len(srcFiles))
 	}
+	var kytheFiles android.Paths
+	if flags.emitXrefs {
+		kytheFiles = make(android.Paths, 0, len(srcFiles))
+	}
 
-	commonFlags := strings.Join([]string{
-		flags.globalFlags,
-		flags.systemIncludeFlags,
-	}, " ")
+	// Produce fully expanded flags for use by C tools, C compiles, C++ tools, C++ compiles, and asm compiles
+	// respectively.
+	toolingCflags := flags.globalCommonFlags + " " +
+		flags.globalToolingCFlags + " " +
+		flags.globalConlyFlags + " " +
+		flags.localCommonFlags + " " +
+		flags.localToolingCFlags + " " +
+		flags.localConlyFlags + " " +
+		flags.systemIncludeFlags
 
-	toolingCflags := strings.Join([]string{
-		commonFlags,
-		flags.toolingCFlags,
-		flags.conlyFlags,
-	}, " ")
+	cflags := flags.globalCommonFlags + " " +
+		flags.globalCFlags + " " +
+		flags.globalConlyFlags + " " +
+		flags.localCommonFlags + " " +
+		flags.localCFlags + " " +
+		flags.localConlyFlags + " " +
+		flags.systemIncludeFlags
 
-	cflags := strings.Join([]string{
-		commonFlags,
-		flags.cFlags,
-		flags.conlyFlags,
-	}, " ")
+	toolingCppflags := flags.globalCommonFlags + " " +
+		flags.globalToolingCFlags + " " +
+		flags.globalToolingCppFlags + " " +
+		flags.localCommonFlags + " " +
+		flags.localToolingCFlags + " " +
+		flags.localToolingCppFlags + " " +
+		flags.systemIncludeFlags
 
-	toolingCppflags := strings.Join([]string{
-		commonFlags,
-		flags.toolingCFlags,
-		flags.toolingCppFlags,
-	}, " ")
+	cppflags := flags.globalCommonFlags + " " +
+		flags.globalCFlags + " " +
+		flags.globalCppFlags + " " +
+		flags.localCommonFlags + " " +
+		flags.localCFlags + " " +
+		flags.localCppFlags + " " +
+		flags.systemIncludeFlags
 
-	cppflags := strings.Join([]string{
-		commonFlags,
-		flags.cFlags,
-		flags.cppFlags,
-	}, " ")
-
-	asflags := strings.Join([]string{
-		commonFlags,
-		flags.asFlags,
-	}, " ")
+	asflags := flags.globalCommonFlags + " " +
+		flags.globalAsFlags + " " +
+		flags.localCommonFlags + " " +
+		flags.localAsFlags + " " +
+		flags.systemIncludeFlags
 
 	var sAbiDumpFiles android.Paths
 	if flags.sAbiDump {
@@ -374,7 +424,7 @@
 				Implicits:   cFlagsDeps,
 				OrderOnly:   pathDeps,
 				Args: map[string]string{
-					"asFlags": flags.yasmFlags,
+					"asFlags": flags.globalYasmFlags + " " + flags.localYasmFlags,
 				},
 			})
 			continue
@@ -392,34 +442,42 @@
 				},
 			})
 			continue
+		case ".o":
+			objFiles[i] = srcFile
+			continue
 		}
 
-		var moduleCflags string
-		var moduleToolingCflags string
+		var moduleFlags string
+		var moduleToolingFlags string
+
 		var ccCmd string
 		tidy := flags.tidy
 		coverage := flags.coverage
 		dump := flags.sAbiDump
 		rule := cc
+		emitXref := flags.emitXrefs
 
 		switch srcFile.Ext() {
 		case ".s":
-			rule = ccNoDeps
+			if !flags.assemblerWithCpp {
+				rule = ccNoDeps
+			}
 			fallthrough
 		case ".S":
 			ccCmd = "clang"
-			moduleCflags = asflags
+			moduleFlags = asflags
 			tidy = false
 			coverage = false
 			dump = false
+			emitXref = false
 		case ".c":
 			ccCmd = "clang"
-			moduleCflags = cflags
-			moduleToolingCflags = toolingCflags
-		case ".cpp", ".cc", ".mm":
+			moduleFlags = cflags
+			moduleToolingFlags = toolingCflags
+		case ".cpp", ".cc", ".cxx", ".mm":
 			ccCmd = "clang++"
-			moduleCflags = cppflags
-			moduleToolingCflags = toolingCppflags
+			moduleFlags = cppflags
+			moduleToolingFlags = toolingCppflags
 		default:
 			ctx.ModuleErrorf("File %s has unknown extension", srcFile)
 			continue
@@ -445,11 +503,27 @@
 			Implicits:       cFlagsDeps,
 			OrderOnly:       pathDeps,
 			Args: map[string]string{
-				"cFlags": moduleCflags,
+				"cFlags": moduleFlags,
 				"ccCmd":  ccCmd,
 			},
 		})
 
+		if emitXref {
+			kytheFile := android.ObjPathWithExt(ctx, subdir, srcFile, "kzip")
+			ctx.Build(pctx, android.BuildParams{
+				Rule:        kytheExtract,
+				Description: "Xref C++ extractor " + srcFile.Rel(),
+				Output:      kytheFile,
+				Input:       srcFile,
+				Implicits:   cFlagsDeps,
+				OrderOnly:   pathDeps,
+				Args: map[string]string{
+					"cFlags": moduleFlags,
+				},
+			})
+			kytheFiles = append(kytheFiles, kytheFile)
+		}
+
 		if tidy {
 			tidyFile := android.ObjPathWithExt(ctx, subdir, srcFile, "tidy")
 			tidyFiles = append(tidyFiles, tidyFile)
@@ -461,9 +535,11 @@
 				Input:       srcFile,
 				// We must depend on objFile, since clang-tidy doesn't
 				// support exporting dependencies.
-				Implicit: objFile,
+				Implicit:  objFile,
+				Implicits: cFlagsDeps,
+				OrderOnly: pathDeps,
 				Args: map[string]string{
-					"cFlags":    moduleToolingCflags,
+					"cFlags":    moduleToolingFlags,
 					"tidyFlags": flags.tidyFlags,
 				},
 			})
@@ -479,8 +555,10 @@
 				Output:      sAbiDumpFile,
 				Input:       srcFile,
 				Implicit:    objFile,
+				Implicits:   cFlagsDeps,
+				OrderOnly:   pathDeps,
 				Args: map[string]string{
-					"cFlags":     moduleToolingCflags,
+					"cFlags":     moduleToolingFlags,
 					"exportDirs": flags.sAbiFlags,
 				},
 			})
@@ -493,6 +571,7 @@
 		tidyFiles:     tidyFiles,
 		coverageFiles: coverageFiles,
 		sAbiDumpFiles: sAbiDumpFiles,
+		kytheFiles:    kytheFiles,
 	}
 }
 
@@ -501,13 +580,10 @@
 	flags builderFlags, outputFile android.ModuleOutPath, deps android.Paths) {
 
 	arCmd := "${config.ClangBin}/llvm-ar"
-	arFlags := "crsD"
+	arFlags := "crsPD"
 	if !ctx.Darwin() {
 		arFlags += " -format=gnu"
 	}
-	if flags.arFlags != "" {
-		arFlags += " " + flags.arFlags
-	}
 
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        ar,
@@ -585,11 +661,12 @@
 		Inputs:          objFiles,
 		Implicits:       deps,
 		Args: map[string]string{
-			"ldCmd":    ldCmd,
-			"crtBegin": crtBegin.String(),
-			"libFlags": strings.Join(libFlagsList, " "),
-			"ldFlags":  flags.ldFlags,
-			"crtEnd":   crtEnd.String(),
+			"ldCmd":         ldCmd,
+			"crtBegin":      crtBegin.String(),
+			"libFlags":      strings.Join(libFlagsList, " "),
+			"extraLibFlags": flags.extraLibFlags,
+			"ldFlags":       flags.globalLdFlags + " " + flags.localLdFlags,
+			"crtEnd":        crtEnd.String(),
 		},
 	})
 }
@@ -601,9 +678,6 @@
 	excludedSymbolVersions, excludedSymbolTags []string) android.OptionalPath {
 
 	outputFile := android.PathForModuleOut(ctx, baseName+".lsdump")
-	sabiLock.Lock()
-	lsdumpPaths = append(lsdumpPaths, outputFile.String())
-	sabiLock.Unlock()
 
 	implicits := android.Paths{soFile}
 	symbolFilterStr := "-so " + soFile.String()
@@ -716,7 +790,7 @@
 
 // Generate a rule for compiling multiple .o files to a .o using ld partial linking
 func TransformObjsToObj(ctx android.ModuleContext, objFiles android.Paths,
-	flags builderFlags, outputFile android.WritablePath) {
+	flags builderFlags, outputFile android.WritablePath, deps android.Paths) {
 
 	ldCmd := "${config.ClangBin}/clang++"
 
@@ -725,9 +799,10 @@
 		Description: "link " + outputFile.Base(),
 		Output:      outputFile,
 		Inputs:      objFiles,
+		Implicits:   deps,
 		Args: map[string]string{
 			"ldCmd":   ldCmd,
-			"ldFlags": flags.ldFlags,
+			"ldFlags": flags.globalLdFlags + " " + flags.localLdFlags,
 		},
 	})
 }
@@ -816,6 +891,20 @@
 	return android.OptionalPath{}
 }
 
+func TransformArchiveRepack(ctx android.ModuleContext, inputFile android.Path,
+	outputFile android.WritablePath, objects []string) {
+
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        archiveRepack,
+		Description: "Repack archive " + outputFile.Base(),
+		Output:      outputFile,
+		Input:       inputFile,
+		Args: map[string]string{
+			"objects": strings.Join(objects, " "),
+		},
+	})
+}
+
 func gccCmd(toolchain config.Toolchain, cmd string) string {
 	return filepath.Join(toolchain.GccRoot(), "bin", toolchain.GccTriple()+"-"+cmd)
 }
diff --git a/cc/cc.go b/cc/cc.go
index 53ec899..d1b97b4 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -33,20 +33,25 @@
 )
 
 func init() {
-	android.RegisterModuleType("cc_defaults", defaultsFactory)
+	RegisterCCBuildComponents(android.InitRegistrationContext)
 
-	android.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.BottomUp("image", ImageMutator).Parallel()
-		ctx.BottomUp("link", LinkageMutator).Parallel()
+	pctx.Import("android/soong/cc/config")
+}
+
+func RegisterCCBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("cc_defaults", defaultsFactory)
+
+	ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
 		ctx.BottomUp("vndk", VndkMutator).Parallel()
-		ctx.BottomUp("ndk_api", ndkApiMutator).Parallel()
-		ctx.BottomUp("test_per_src", testPerSrcMutator).Parallel()
+		ctx.BottomUp("link", LinkageMutator).Parallel()
+		ctx.BottomUp("ndk_api", NdkApiMutator).Parallel()
+		ctx.BottomUp("test_per_src", TestPerSrcMutator).Parallel()
 		ctx.BottomUp("version", VersionMutator).Parallel()
 		ctx.BottomUp("begin", BeginMutator).Parallel()
-		ctx.BottomUp("sysprop", SyspropMutator).Parallel()
+		ctx.BottomUp("sysprop_cc", SyspropMutator).Parallel()
 	})
 
-	android.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
+	ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
 		ctx.TopDown("asan_deps", sanitizerDepsMutator(asan))
 		ctx.BottomUp("asan", sanitizerMutator(asan)).Parallel()
 
@@ -56,6 +61,8 @@
 		ctx.TopDown("fuzzer_deps", sanitizerDepsMutator(fuzzer))
 		ctx.BottomUp("fuzzer", sanitizerMutator(fuzzer)).Parallel()
 
+		// cfi mutator shouldn't run before sanitizers that return true for
+		// incompatibleWithCfi()
 		ctx.TopDown("cfi_deps", sanitizerDepsMutator(cfi))
 		ctx.BottomUp("cfi", sanitizerMutator(cfi)).Parallel()
 
@@ -65,7 +72,7 @@
 		ctx.TopDown("tsan_deps", sanitizerDepsMutator(tsan))
 		ctx.BottomUp("tsan", sanitizerMutator(tsan)).Parallel()
 
-		ctx.TopDown("sanitize_runtime_deps", sanitizerRuntimeDepsMutator)
+		ctx.TopDown("sanitize_runtime_deps", sanitizerRuntimeDepsMutator).Parallel()
 		ctx.BottomUp("sanitize_runtime", sanitizerRuntimeMutator).Parallel()
 
 		ctx.BottomUp("coverage", coverageMutator).Parallel()
@@ -77,7 +84,7 @@
 		ctx.TopDown("double_loadable", checkDoubleLoadableLibraries).Parallel()
 	})
 
-	pctx.Import("android/soong/cc/config")
+	android.RegisterSingletonType("kythe_extract_all", kytheExtractAllFactory)
 }
 
 type Deps struct {
@@ -92,6 +99,7 @@
 
 	GeneratedSources []string
 	GeneratedHeaders []string
+	GeneratedDeps    []string
 
 	ReexportGeneratedHeaders []string
 
@@ -118,14 +126,16 @@
 	// Paths to generated source files
 	GeneratedSources android.Paths
 	GeneratedHeaders android.Paths
+	GeneratedDeps    android.Paths
 
-	Flags                []string
-	IncludeDirs          []string
-	SystemIncludeDirs    []string
-	ReexportedDirs       []string
-	ReexportedSystemDirs []string
-	ReexportedFlags      []string
-	ReexportedDeps       android.Paths
+	Flags                      []string
+	IncludeDirs                android.Paths
+	SystemIncludeDirs          android.Paths
+	ReexportedDirs             android.Paths
+	ReexportedSystemDirs       android.Paths
+	ReexportedFlags            []string
+	ReexportedGeneratedHeaders android.Paths
+	ReexportedDeps             android.Paths
 
 	// Paths to crt*.o files
 	CrtBegin, CrtEnd android.OptionalPath
@@ -137,31 +147,41 @@
 	DynamicLinker android.OptionalPath
 }
 
-type Flags struct {
-	GlobalFlags     []string // Flags that apply to C, C++, and assembly source files
-	ArFlags         []string // Flags that apply to ar
+// LocalOrGlobalFlags contains flags that need to have values set globally by the build system or locally by the module
+// tracked separately, in order to maintain the required ordering (most of the global flags need to go first on the
+// command line so they can be overridden by the local module flags).
+type LocalOrGlobalFlags struct {
+	CommonFlags     []string // Flags that apply to C, C++, and assembly source files
 	AsFlags         []string // Flags that apply to assembly source files
+	YasmFlags       []string // Flags that apply to yasm assembly source files
 	CFlags          []string // Flags that apply to C and C++ source files
 	ToolingCFlags   []string // Flags that apply to C and C++ source files parsed by clang LibTooling tools
 	ConlyFlags      []string // Flags that apply to C source files
 	CppFlags        []string // Flags that apply to C++ source files
 	ToolingCppFlags []string // Flags that apply to C++ source files parsed by clang LibTooling tools
-	aidlFlags       []string // Flags that apply to aidl source files
-	rsFlags         []string // Flags that apply to renderscript source files
 	LdFlags         []string // Flags that apply to linker command lines
-	libFlags        []string // Flags to add libraries early to the link order
-	TidyFlags       []string // Flags that apply to clang-tidy
-	SAbiFlags       []string // Flags that apply to header-abi-dumper
-	YasmFlags       []string // Flags that apply to yasm assembly source files
+}
+
+type Flags struct {
+	Local  LocalOrGlobalFlags
+	Global LocalOrGlobalFlags
+
+	aidlFlags     []string // Flags that apply to aidl source files
+	rsFlags       []string // Flags that apply to renderscript source files
+	libFlags      []string // Flags to add libraries early to the link order
+	extraLibFlags []string // Flags to add libraries late in the link order after LdFlags
+	TidyFlags     []string // Flags that apply to clang-tidy
+	SAbiFlags     []string // Flags that apply to header-abi-dumper
 
 	// Global include flags that apply to C, C++, and assembly source files
-	// These must be after any module include flags, which will be in GlobalFlags.
+	// These must be after any module include flags, which will be in CommonFlags.
 	SystemIncludeFlags []string
 
 	Toolchain config.Toolchain
 	Tidy      bool
 	Coverage  bool
 	SAbiDump  bool
+	EmitXrefs bool // If true, generate Ninja rules to generate emitXrefs input files for Kythe
 
 	RequiredInstructionSet string
 	DynamicLinker          string
@@ -169,7 +189,8 @@
 	CFlagsDeps  android.Paths // Files depended on by compiler flags
 	LdFlagsDeps android.Paths // Files depended on by linker flags
 
-	GroupStaticLibs bool
+	AssemblerWithCpp bool
+	GroupStaticLibs  bool
 
 	proto            android.ProtoFlags
 	protoC           bool // Whether to use C instead of C++
@@ -178,14 +199,6 @@
 	Yacc *YaccProperties
 }
 
-type ObjectLinkerProperties struct {
-	// names of other cc_object modules to link into this module using partial linking
-	Objs []string `android:"arch_variant"`
-
-	// if set, add an extra objcopy --prefix-symbols= step
-	Prefix_symbols *string
-}
-
 // Properties used to compile all C or C++ modules
 type BaseProperties struct {
 	// Deprecated. true is the default, false is invalid.
@@ -202,7 +215,9 @@
 	PreventInstall            bool     `blueprint:"mutated"`
 	ApexesProvidingSharedLibs []string `blueprint:"mutated"`
 
-	UseVndk bool `blueprint:"mutated"`
+	ImageVariationPrefix string `blueprint:"mutated"`
+	VndkVersion          string `blueprint:"mutated"`
+	SubName              string `blueprint:"mutated"`
 
 	// *.logtags files, to combine together in order to generate the /system/etc/event-log-tags
 	// file
@@ -211,30 +226,41 @@
 	// Make this module available when building for recovery
 	Recovery_available *bool
 
-	InRecovery bool `blueprint:"mutated"`
+	// Set by imageMutator
+	CoreVariantNeeded     bool     `blueprint:"mutated"`
+	RecoveryVariantNeeded bool     `blueprint:"mutated"`
+	ExtraVariants         []string `blueprint:"mutated"`
 
 	// Allows this module to use non-APEX version of libraries. Useful
 	// for building binaries that are started before APEXes are activated.
 	Bootstrap *bool
+
+	// Even if DeviceConfig().VndkUseCoreVariant() is set, this module must use vendor variant.
+	// see soong/cc/config/vndk.go
+	MustUseVendorVariant bool `blueprint:"mutated"`
 }
 
 type VendorProperties struct {
 	// whether this module should be allowed to be directly depended by other
 	// modules with `vendor: true`, `proprietary: true`, or `vendor_available:true`.
-	// If set to true, two variants will be built separately, one like
-	// normal, and the other limited to the set of libraries and headers
-	// that are exposed to /vendor modules.
+	// In addition, this module should be allowed to be directly depended by
+	// product modules with `product_specific: true`.
+	// If set to true, three variants will be built separately, one like
+	// normal, another limited to the set of libraries and headers
+	// that are exposed to /vendor modules, and the other to /product modules.
 	//
-	// The vendor variant may be used with a different (newer) /system,
+	// The vendor and product variants may be used with a different (newer) /system,
 	// so it shouldn't have any unversioned runtime dependencies, or
 	// make assumptions about the system that may not be true in the
 	// future.
 	//
-	// If set to false, this module becomes inaccessible from /vendor modules.
+	// If set to false, this module becomes inaccessible from /vendor or /product
+	// modules.
 	//
 	// Default value is true when vndk: {enabled: true} or vendor: true.
 	//
 	// Nothing happens if BOARD_VNDK_VERSION isn't set in the BoardConfig.mk
+	// If PRODUCT_PRODUCT_VNDK_VERSION isn't set, product variant will not be used.
 	Vendor_available *bool
 
 	// whether this module is capable of being loaded with other instance
@@ -250,6 +276,7 @@
 type ModuleContextIntf interface {
 	static() bool
 	staticBinary() bool
+	header() bool
 	toolchain() config.Toolchain
 	useSdk() bool
 	sdkVersion() string
@@ -261,14 +288,17 @@
 	isVndk() bool
 	isVndkSp() bool
 	isVndkExt() bool
+	inProduct() bool
+	inVendor() bool
 	inRecovery() bool
-	shouldCreateVndkSourceAbiDump(config android.Config) bool
+	shouldCreateSourceAbiDump() bool
 	selectedStl() string
 	baseModuleName() string
 	getVndkExtendsModuleName() string
 	isPgoCompile() bool
 	isNDKStubLibrary() bool
 	useClangLld(actx ModuleContext) bool
+	isForPlatform() bool
 	apexName() string
 	hasStubsVariants() bool
 	isStubs() bool
@@ -322,6 +352,7 @@
 	unstrippedOutputFilePath() android.Path
 
 	nativeCoverage() bool
+	coverageOutputFilePath() android.OptionalPath
 }
 
 type installer interface {
@@ -333,43 +364,57 @@
 	relativeInstallPath() string
 }
 
-type dependencyTag struct {
-	blueprint.BaseDependencyTag
-	name    string
-	library bool
-
-	reexportFlags bool
-
-	explicitlyVersioned bool
+type xref interface {
+	XrefCcFiles() android.Paths
 }
 
 var (
-	sharedDepTag          = dependencyTag{name: "shared", library: true}
-	sharedExportDepTag    = dependencyTag{name: "shared", library: true, reexportFlags: true}
-	earlySharedDepTag     = dependencyTag{name: "early_shared", library: true}
-	lateSharedDepTag      = dependencyTag{name: "late shared", library: true}
-	staticDepTag          = dependencyTag{name: "static", library: true}
-	staticExportDepTag    = dependencyTag{name: "static", library: true, reexportFlags: true}
-	lateStaticDepTag      = dependencyTag{name: "late static", library: true}
-	wholeStaticDepTag     = dependencyTag{name: "whole static", library: true, reexportFlags: true}
-	headerDepTag          = dependencyTag{name: "header", library: true}
-	headerExportDepTag    = dependencyTag{name: "header", library: true, reexportFlags: true}
-	genSourceDepTag       = dependencyTag{name: "gen source"}
-	genHeaderDepTag       = dependencyTag{name: "gen header"}
-	genHeaderExportDepTag = dependencyTag{name: "gen header", reexportFlags: true}
-	objDepTag             = dependencyTag{name: "obj"}
-	crtBeginDepTag        = dependencyTag{name: "crtbegin"}
-	crtEndDepTag          = dependencyTag{name: "crtend"}
-	linkerFlagsDepTag     = dependencyTag{name: "linker flags file"}
-	dynamicLinkerDepTag   = dependencyTag{name: "dynamic linker"}
-	reuseObjTag           = dependencyTag{name: "reuse objects"}
-	staticVariantTag      = dependencyTag{name: "static variant"}
-	ndkStubDepTag         = dependencyTag{name: "ndk stub", library: true}
-	ndkLateStubDepTag     = dependencyTag{name: "ndk late stub", library: true}
-	vndkExtDepTag         = dependencyTag{name: "vndk extends", library: true}
-	runtimeDepTag         = dependencyTag{name: "runtime lib"}
+	sharedExportDepTag    = DependencyTag{Name: "shared", Library: true, Shared: true, ReexportFlags: true}
+	earlySharedDepTag     = DependencyTag{Name: "early_shared", Library: true, Shared: true}
+	lateSharedDepTag      = DependencyTag{Name: "late shared", Library: true, Shared: true}
+	staticExportDepTag    = DependencyTag{Name: "static", Library: true, ReexportFlags: true}
+	lateStaticDepTag      = DependencyTag{Name: "late static", Library: true}
+	wholeStaticDepTag     = DependencyTag{Name: "whole static", Library: true, ReexportFlags: true}
+	headerDepTag          = DependencyTag{Name: "header", Library: true}
+	headerExportDepTag    = DependencyTag{Name: "header", Library: true, ReexportFlags: true}
+	genSourceDepTag       = DependencyTag{Name: "gen source"}
+	genHeaderDepTag       = DependencyTag{Name: "gen header"}
+	genHeaderExportDepTag = DependencyTag{Name: "gen header", ReexportFlags: true}
+	objDepTag             = DependencyTag{Name: "obj"}
+	linkerFlagsDepTag     = DependencyTag{Name: "linker flags file"}
+	dynamicLinkerDepTag   = DependencyTag{Name: "dynamic linker"}
+	reuseObjTag           = DependencyTag{Name: "reuse objects"}
+	staticVariantTag      = DependencyTag{Name: "static variant"}
+	ndkStubDepTag         = DependencyTag{Name: "ndk stub", Library: true}
+	ndkLateStubDepTag     = DependencyTag{Name: "ndk late stub", Library: true}
+	vndkExtDepTag         = DependencyTag{Name: "vndk extends", Library: true}
+	runtimeDepTag         = DependencyTag{Name: "runtime lib"}
+	coverageDepTag        = DependencyTag{Name: "coverage"}
+	testPerSrcDepTag      = DependencyTag{Name: "test_per_src"}
 )
 
+func IsSharedDepTag(depTag blueprint.DependencyTag) bool {
+	ccDepTag, ok := depTag.(DependencyTag)
+	return ok && ccDepTag.Shared
+}
+
+func IsStaticDepTag(depTag blueprint.DependencyTag) bool {
+	ccDepTag, ok := depTag.(DependencyTag)
+	return ok && (ccDepTag == staticExportDepTag ||
+		ccDepTag == lateStaticDepTag ||
+		ccDepTag == wholeStaticDepTag)
+}
+
+func IsRuntimeDepTag(depTag blueprint.DependencyTag) bool {
+	ccDepTag, ok := depTag.(DependencyTag)
+	return ok && ccDepTag == runtimeDepTag
+}
+
+func IsTestPerSrcDepTag(depTag blueprint.DependencyTag) bool {
+	ccDepTag, ok := depTag.(DependencyTag)
+	return ok && ccDepTag == testPerSrcDepTag
+}
+
 // Module contains the properties and members used by all C/C++ module types, and implements
 // the blueprint.Module interface.  It delegates to compiler, linker, and installer interfaces
 // to construct the output file.  Behavior can be customized with a Customizer interface
@@ -377,6 +422,7 @@
 	android.ModuleBase
 	android.DefaultableModuleBase
 	android.ApexModuleBase
+	android.SdkBase
 
 	Properties       BaseProperties
 	VendorProperties VendorProperties
@@ -385,6 +431,9 @@
 	hod      android.HostOrDeviceSupported
 	multilib android.Multilib
 
+	// Allowable SdkMemberTypes of this module type.
+	sdkMemberTypes []android.SdkMemberType
+
 	// delegates, initialize before calling Init
 	features  []feature
 	compiler  compiler
@@ -416,15 +465,219 @@
 	depsInLinkOrder android.Paths
 
 	// only non-nil when this is a shared library that reuses the objects of a static library
-	staticVariant *Module
+	staticVariant LinkableInterface
 
 	makeLinkType string
+	// Kythe (source file indexer) paths for this compilation module
+	kytheFiles android.Paths
+
+	// name of the modules that are direct or indirect static deps of this module
+	allStaticDeps []string
+}
+
+func (c *Module) Toc() android.OptionalPath {
+	if c.linker != nil {
+		if library, ok := c.linker.(libraryInterface); ok {
+			return library.toc()
+		}
+	}
+	panic(fmt.Errorf("Toc() called on non-library module: %q", c.BaseModuleName()))
+}
+
+func (c *Module) ApiLevel() string {
+	if c.linker != nil {
+		if stub, ok := c.linker.(*stubDecorator); ok {
+			return stub.properties.ApiLevel
+		}
+	}
+	panic(fmt.Errorf("ApiLevel() called on non-stub library module: %q", c.BaseModuleName()))
+}
+
+func (c *Module) Static() bool {
+	if c.linker != nil {
+		if library, ok := c.linker.(libraryInterface); ok {
+			return library.static()
+		}
+	}
+	panic(fmt.Errorf("Static() called on non-library module: %q", c.BaseModuleName()))
+}
+
+func (c *Module) Shared() bool {
+	if c.linker != nil {
+		if library, ok := c.linker.(libraryInterface); ok {
+			return library.shared()
+		}
+	}
+	panic(fmt.Errorf("Shared() called on non-library module: %q", c.BaseModuleName()))
+}
+
+func (c *Module) SelectedStl() string {
+	return c.stl.Properties.SelectedStl
+}
+
+func (c *Module) ToolchainLibrary() bool {
+	if _, ok := c.linker.(*toolchainLibraryDecorator); ok {
+		return true
+	}
+	return false
+}
+
+func (c *Module) NdkPrebuiltStl() bool {
+	if _, ok := c.linker.(*ndkPrebuiltStlLinker); ok {
+		return true
+	}
+	return false
+}
+
+func (c *Module) StubDecorator() bool {
+	if _, ok := c.linker.(*stubDecorator); ok {
+		return true
+	}
+	return false
+}
+
+func (c *Module) SdkVersion() string {
+	return String(c.Properties.Sdk_version)
+}
+
+func (c *Module) IncludeDirs() android.Paths {
+	if c.linker != nil {
+		if library, ok := c.linker.(exportedFlagsProducer); ok {
+			return library.exportedDirs()
+		}
+	}
+	panic(fmt.Errorf("IncludeDirs called on non-exportedFlagsProducer module: %q", c.BaseModuleName()))
+}
+
+func (c *Module) HasStaticVariant() bool {
+	if c.staticVariant != nil {
+		return true
+	}
+	return false
+}
+
+func (c *Module) GetStaticVariant() LinkableInterface {
+	return c.staticVariant
+}
+
+func (c *Module) SetDepsInLinkOrder(depsInLinkOrder []android.Path) {
+	c.depsInLinkOrder = depsInLinkOrder
+}
+
+func (c *Module) GetDepsInLinkOrder() []android.Path {
+	return c.depsInLinkOrder
+}
+
+func (c *Module) StubsVersions() []string {
+	if c.linker != nil {
+		if library, ok := c.linker.(*libraryDecorator); ok {
+			return library.Properties.Stubs.Versions
+		}
+	}
+	panic(fmt.Errorf("StubsVersions called on non-library module: %q", c.BaseModuleName()))
+}
+
+func (c *Module) CcLibrary() bool {
+	if c.linker != nil {
+		if _, ok := c.linker.(*libraryDecorator); ok {
+			return true
+		}
+	}
+	return false
+}
+
+func (c *Module) CcLibraryInterface() bool {
+	if _, ok := c.linker.(libraryInterface); ok {
+		return true
+	}
+	return false
+}
+
+func (c *Module) NonCcVariants() bool {
+	return false
+}
+
+func (c *Module) SetBuildStubs() {
+	if c.linker != nil {
+		if library, ok := c.linker.(*libraryDecorator); ok {
+			library.MutatedProperties.BuildStubs = true
+			c.Properties.HideFromMake = true
+			c.sanitize = nil
+			c.stl = nil
+			c.Properties.PreventInstall = true
+			return
+		}
+	}
+	panic(fmt.Errorf("SetBuildStubs called on non-library module: %q", c.BaseModuleName()))
+}
+
+func (c *Module) BuildStubs() bool {
+	if c.linker != nil {
+		if library, ok := c.linker.(*libraryDecorator); ok {
+			return library.buildStubs()
+		}
+	}
+	panic(fmt.Errorf("BuildStubs called on non-library module: %q", c.BaseModuleName()))
+}
+
+func (c *Module) SetStubsVersions(version string) {
+	if c.linker != nil {
+		if library, ok := c.linker.(*libraryDecorator); ok {
+			library.MutatedProperties.StubsVersion = version
+			return
+		}
+	}
+	panic(fmt.Errorf("SetStubsVersions called on non-library module: %q", c.BaseModuleName()))
+}
+
+func (c *Module) SetStatic() {
+	if c.linker != nil {
+		if library, ok := c.linker.(libraryInterface); ok {
+			library.setStatic()
+			return
+		}
+	}
+	panic(fmt.Errorf("SetStatic called on non-library module: %q", c.BaseModuleName()))
+}
+
+func (c *Module) SetShared() {
+	if c.linker != nil {
+		if library, ok := c.linker.(libraryInterface); ok {
+			library.setShared()
+			return
+		}
+	}
+	panic(fmt.Errorf("SetShared called on non-library module: %q", c.BaseModuleName()))
+}
+
+func (c *Module) BuildStaticVariant() bool {
+	if c.linker != nil {
+		if library, ok := c.linker.(libraryInterface); ok {
+			return library.buildStatic()
+		}
+	}
+	panic(fmt.Errorf("BuildStaticVariant called on non-library module: %q", c.BaseModuleName()))
+}
+
+func (c *Module) BuildSharedVariant() bool {
+	if c.linker != nil {
+		if library, ok := c.linker.(libraryInterface); ok {
+			return library.buildShared()
+		}
+	}
+	panic(fmt.Errorf("BuildSharedVariant called on non-library module: %q", c.BaseModuleName()))
+}
+
+func (c *Module) Module() android.Module {
+	return c
 }
 
 func (c *Module) OutputFile() android.OptionalPath {
 	return c.outputFile
 }
 
+var _ LinkableInterface = (*Module)(nil)
+
 func (c *Module) UnstrippedOutputFile() android.Path {
 	if c.linker != nil {
 		return c.linker.unstrippedOutputFilePath()
@@ -432,6 +685,13 @@
 	return nil
 }
 
+func (c *Module) CoverageOutputFile() android.OptionalPath {
+	if c.linker != nil {
+		return c.linker.coverageOutputFilePath()
+	}
+	return android.OptionalPath{}
+}
+
 func (c *Module) RelativeInstallPath() string {
 	if c.installer != nil {
 		return c.installer.relativeInstallPath()
@@ -439,6 +699,10 @@
 	return ""
 }
 
+func (c *Module) VndkVersion() string {
+	return c.Properties.VndkVersion
+}
+
 func (c *Module) Init() android.Module {
 	c.AddProperties(&c.Properties, &c.VendorProperties)
 	if c.compiler != nil {
@@ -490,10 +754,9 @@
 		}
 	})
 	android.InitAndroidArchModule(c, c.hod, c.multilib)
-
-	android.InitDefaultableModule(c)
-
 	android.InitApexModule(c)
+	android.InitSdkAwareModule(c)
+	android.InitDefaultableModule(c)
 
 	return c
 }
@@ -509,47 +772,47 @@
 	return false
 }
 
-func (c *Module) useVndk() bool {
-	return c.Properties.UseVndk
+// Returns true if the module is using VNDK libraries instead of the libraries in /system/lib or /system/lib64.
+// "product" and "vendor" variant modules return true for this function.
+// When BOARD_VNDK_VERSION is set, vendor variants of "vendor_available: true", "vendor: true",
+// "soc_specific: true" and more vendor installed modules are included here.
+// When PRODUCT_PRODUCT_VNDK_VERSION is set, product variants of "vendor_available: true" or
+// "product_specific: true" modules are included here.
+func (c *Module) UseVndk() bool {
+	return c.Properties.VndkVersion != ""
 }
 
 func (c *Module) isCoverageVariant() bool {
 	return c.coverage.Properties.IsCoverageVariant
 }
 
-func (c *Module) isNdk() bool {
+func (c *Module) IsNdk() bool {
 	return inList(c.Name(), ndkMigratedLibs)
 }
 
 func (c *Module) isLlndk(config android.Config) bool {
 	// Returns true for both LLNDK (public) and LLNDK-private libs.
-	return inList(c.Name(), *llndkLibraries(config))
+	return isLlndkLibrary(c.BaseModuleName(), config)
 }
 
 func (c *Module) isLlndkPublic(config android.Config) bool {
 	// Returns true only for LLNDK (public) libs.
-	return c.isLlndk(config) && !c.isVndkPrivate(config)
+	name := c.BaseModuleName()
+	return isLlndkLibrary(name, config) && !isVndkPrivateLibrary(name, config)
 }
 
 func (c *Module) isVndkPrivate(config android.Config) bool {
 	// Returns true for LLNDK-private, VNDK-SP-private, and VNDK-core-private.
-	return inList(c.Name(), *vndkPrivateLibraries(config))
+	return isVndkPrivateLibrary(c.BaseModuleName(), config)
 }
 
-func (c *Module) isVndk() bool {
+func (c *Module) IsVndk() bool {
 	if vndkdep := c.vndkdep; vndkdep != nil {
 		return vndkdep.isVndk()
 	}
 	return false
 }
 
-func (c *Module) vndkVersion() string {
-	if vndkdep := c.vndkdep; vndkdep != nil {
-		return vndkdep.Properties.Vndk.Version
-	}
-	return ""
-}
-
 func (c *Module) isPgoCompile() bool {
 	if pgo := c.pgo; pgo != nil {
 		return pgo.Properties.PgoCompile
@@ -578,8 +841,8 @@
 	return false
 }
 
-func (c *Module) mustUseVendorVariant() bool {
-	return c.isVndkSp() || inList(c.Name(), config.VndkMustUseVendorVariantList)
+func (c *Module) MustUseVendorVariant() bool {
+	return c.isVndkSp() || c.Properties.MustUseVendorVariant
 }
 
 func (c *Module) getVndkExtendsModuleName() string {
@@ -589,17 +852,37 @@
 	return ""
 }
 
-// Returns true only when this module is configured to have core and vendor
+// Returns true only when this module is configured to have core, product and vendor
 // variants.
-func (c *Module) hasVendorVariant() bool {
-	return c.isVndk() || Bool(c.VendorProperties.Vendor_available)
+func (c *Module) HasVendorVariant() bool {
+	return c.IsVndk() || Bool(c.VendorProperties.Vendor_available)
 }
 
-func (c *Module) inRecovery() bool {
-	return c.Properties.InRecovery || c.ModuleBase.InstallInRecovery()
+const (
+	// VendorVariationPrefix is the variant prefix used for /vendor code that compiles
+	// against the VNDK.
+	VendorVariationPrefix = "vendor."
+
+	// ProductVariationPrefix is the variant prefix used for /product code that compiles
+	// against the VNDK.
+	ProductVariationPrefix = "product."
+)
+
+// Returns true if the module is "product" variant. Usually these modules are installed in /product
+func (c *Module) inProduct() bool {
+	return c.Properties.ImageVariationPrefix == ProductVariationPrefix
 }
 
-func (c *Module) onlyInRecovery() bool {
+// Returns true if the module is "vendor" variant. Usually these modules are installed in /vendor
+func (c *Module) inVendor() bool {
+	return c.Properties.ImageVariationPrefix == VendorVariationPrefix
+}
+
+func (c *Module) InRecovery() bool {
+	return c.ModuleBase.InRecovery() || c.ModuleBase.InstallInRecovery()
+}
+
+func (c *Module) OnlyInRecovery() bool {
 	return c.ModuleBase.InstallInRecovery()
 }
 
@@ -627,24 +910,67 @@
 }
 
 func (c *Module) nativeCoverage() bool {
+	// Bug: http://b/137883967 - native-bridge modules do not currently work with coverage
+	if c.Target().NativeBridge == android.NativeBridgeEnabled {
+		return false
+	}
 	return c.linker != nil && c.linker.nativeCoverage()
 }
 
+func (c *Module) ExportedIncludeDirs() android.Paths {
+	if flagsProducer, ok := c.linker.(exportedFlagsProducer); ok {
+		return flagsProducer.exportedDirs()
+	}
+	return nil
+}
+
+func (c *Module) ExportedSystemIncludeDirs() android.Paths {
+	if flagsProducer, ok := c.linker.(exportedFlagsProducer); ok {
+		return flagsProducer.exportedSystemDirs()
+	}
+	return nil
+}
+
+func (c *Module) ExportedFlags() []string {
+	if flagsProducer, ok := c.linker.(exportedFlagsProducer); ok {
+		return flagsProducer.exportedFlags()
+	}
+	return nil
+}
+
+func (c *Module) ExportedDeps() android.Paths {
+	if flagsProducer, ok := c.linker.(exportedFlagsProducer); ok {
+		return flagsProducer.exportedDeps()
+	}
+	return nil
+}
+
+func (c *Module) ExportedGeneratedHeaders() android.Paths {
+	if flagsProducer, ok := c.linker.(exportedFlagsProducer); ok {
+		return flagsProducer.exportedGeneratedHeaders()
+	}
+	return nil
+}
+
 func isBionic(name string) bool {
 	switch name {
-	case "libc", "libm", "libdl", "linker":
+	case "libc", "libm", "libdl", "libdl_android", "linker":
 		return true
 	}
 	return false
 }
 
-func installToBootstrap(name string, config android.Config) bool {
+func InstallToBootstrap(name string, config android.Config) bool {
 	if name == "libclang_rt.hwasan-aarch64-android" {
 		return inList("hwaddress", config.SanitizeDevice())
 	}
 	return isBionic(name)
 }
 
+func (c *Module) XrefCcFiles() android.Paths {
+	return c.kytheFiles
+}
+
 type baseModuleContext struct {
 	android.BaseModuleContext
 	moduleContextImpl
@@ -660,9 +986,14 @@
 	moduleContextImpl
 }
 
+func (ctx *moduleContext) ProductSpecific() bool {
+	return ctx.ModuleContext.ProductSpecific() ||
+		(ctx.mod.HasVendorVariant() && ctx.mod.inProduct() && !ctx.mod.IsVndk())
+}
+
 func (ctx *moduleContext) SocSpecific() bool {
 	return ctx.ModuleContext.SocSpecific() ||
-		(ctx.mod.hasVendorVariant() && ctx.mod.useVndk() && !ctx.mod.isVndk())
+		(ctx.mod.HasVendorVariant() && ctx.mod.inVendor() && !ctx.mod.IsVndk())
 }
 
 type moduleContextImpl struct {
@@ -682,6 +1013,10 @@
 	return ctx.mod.staticBinary()
 }
 
+func (ctx *moduleContextImpl) header() bool {
+	return ctx.mod.header()
+}
+
 func (ctx *moduleContextImpl) useSdk() bool {
 	if ctx.ctx.Device() && !ctx.useVndk() && !ctx.inRecovery() && !ctx.ctx.Fuchsia() {
 		return String(ctx.mod.Properties.Sdk_version) != ""
@@ -692,15 +1027,11 @@
 func (ctx *moduleContextImpl) sdkVersion() string {
 	if ctx.ctx.Device() {
 		if ctx.useVndk() {
-			vndk_ver := ctx.ctx.DeviceConfig().VndkVersion()
-			if vndk_ver == "current" {
-				platform_vndk_ver := ctx.ctx.DeviceConfig().PlatformVndkVersion()
-				if inList(platform_vndk_ver, ctx.ctx.Config().PlatformVersionCombinedCodenames()) {
-					return "current"
-				}
-				return platform_vndk_ver
+			vndkVer := ctx.mod.VndkVersion()
+			if inList(vndkVer, ctx.ctx.Config().PlatformVersionCombinedCodenames()) {
+				return "current"
 			}
-			return vndk_ver
+			return vndkVer
 		}
 		return String(ctx.mod.Properties.Sdk_version)
 	}
@@ -708,11 +1039,11 @@
 }
 
 func (ctx *moduleContextImpl) useVndk() bool {
-	return ctx.mod.useVndk()
+	return ctx.mod.UseVndk()
 }
 
 func (ctx *moduleContextImpl) isNdk() bool {
-	return ctx.mod.isNdk()
+	return ctx.mod.IsNdk()
 }
 
 func (ctx *moduleContextImpl) isLlndk(config android.Config) bool {
@@ -728,7 +1059,7 @@
 }
 
 func (ctx *moduleContextImpl) isVndk() bool {
-	return ctx.mod.isVndk()
+	return ctx.mod.IsVndk()
 }
 
 func (ctx *moduleContextImpl) isPgoCompile() bool {
@@ -748,15 +1079,23 @@
 }
 
 func (ctx *moduleContextImpl) mustUseVendorVariant() bool {
-	return ctx.mod.mustUseVendorVariant()
+	return ctx.mod.MustUseVendorVariant()
+}
+
+func (ctx *moduleContextImpl) inProduct() bool {
+	return ctx.mod.inProduct()
+}
+
+func (ctx *moduleContextImpl) inVendor() bool {
+	return ctx.mod.inVendor()
 }
 
 func (ctx *moduleContextImpl) inRecovery() bool {
-	return ctx.mod.inRecovery()
+	return ctx.mod.InRecovery()
 }
 
 // Check whether ABI dumps should be created for this module.
-func (ctx *moduleContextImpl) shouldCreateVndkSourceAbiDump(config android.Config) bool {
+func (ctx *moduleContextImpl) shouldCreateSourceAbiDump() bool {
 	if ctx.ctx.Config().IsEnvTrue("SKIP_ABI_CHECKS") {
 		return false
 	}
@@ -774,22 +1113,11 @@
 		// Host modules do not need ABI dumps.
 		return false
 	}
-	if !ctx.mod.IsForPlatform() {
-		// APEX variants do not need ABI dumps.
+	if ctx.isStubs() {
+		// Stubs do not need ABI dumps.
 		return false
 	}
-	if ctx.isNdk() {
-		return true
-	}
-	if ctx.isLlndkPublic(config) {
-		return true
-	}
-	if ctx.useVndk() && ctx.isVndk() && !ctx.isVndkPrivate(config) {
-		// Return true if this is VNDK-core, VNDK-SP, or VNDK-Ext and this is not
-		// VNDK-private.
-		return true
-	}
-	return false
+	return true
 }
 
 func (ctx *moduleContextImpl) selectedStl() string {
@@ -811,6 +1139,10 @@
 	return ctx.mod.getVndkExtendsModuleName()
 }
 
+func (ctx *moduleContextImpl) isForPlatform() bool {
+	return ctx.mod.IsForPlatform()
+}
+
 func (ctx *moduleContextImpl) apexName() string {
 	return ctx.mod.ApexName()
 }
@@ -911,32 +1243,102 @@
 	return orderedAllDeps, orderedDeclaredDeps
 }
 
-func orderStaticModuleDeps(module *Module, staticDeps []*Module, sharedDeps []*Module) (results []android.Path) {
+func orderStaticModuleDeps(module LinkableInterface, staticDeps []LinkableInterface, sharedDeps []LinkableInterface) (results []android.Path) {
 	// convert Module to Path
+	var depsInLinkOrder []android.Path
 	allTransitiveDeps := make(map[android.Path][]android.Path, len(staticDeps))
 	staticDepFiles := []android.Path{}
 	for _, dep := range staticDeps {
-		allTransitiveDeps[dep.outputFile.Path()] = dep.depsInLinkOrder
-		staticDepFiles = append(staticDepFiles, dep.outputFile.Path())
+		allTransitiveDeps[dep.OutputFile().Path()] = dep.GetDepsInLinkOrder()
+		staticDepFiles = append(staticDepFiles, dep.OutputFile().Path())
 	}
 	sharedDepFiles := []android.Path{}
 	for _, sharedDep := range sharedDeps {
-		staticAnalogue := sharedDep.staticVariant
-		if staticAnalogue != nil {
-			allTransitiveDeps[staticAnalogue.outputFile.Path()] = staticAnalogue.depsInLinkOrder
-			sharedDepFiles = append(sharedDepFiles, staticAnalogue.outputFile.Path())
+		if sharedDep.HasStaticVariant() {
+			staticAnalogue := sharedDep.GetStaticVariant()
+			allTransitiveDeps[staticAnalogue.OutputFile().Path()] = staticAnalogue.GetDepsInLinkOrder()
+			sharedDepFiles = append(sharedDepFiles, staticAnalogue.OutputFile().Path())
 		}
 	}
 
 	// reorder the dependencies based on transitive dependencies
-	module.depsInLinkOrder, results = orderDeps(staticDepFiles, sharedDepFiles, allTransitiveDeps)
+	depsInLinkOrder, results = orderDeps(staticDepFiles, sharedDepFiles, allTransitiveDeps)
+	module.SetDepsInLinkOrder(depsInLinkOrder)
 
 	return results
 }
 
+func gatherTransitiveStaticDeps(staticDeps []LinkableInterface) []string {
+	var ret []string
+	for _, dep := range staticDeps {
+		ret = append(ret, dep.Module().Name())
+		ret = append(ret, dep.AllStaticDeps()...)
+	}
+	return android.FirstUniqueStrings(ret)
+}
+
+func (c *Module) IsTestPerSrcAllTestsVariation() bool {
+	test, ok := c.linker.(testPerSrc)
+	return ok && test.isAllTestsVariation()
+}
+
+func (c *Module) getNameSuffixWithVndkVersion(ctx android.ModuleContext) string {
+	// Returns the name suffix for product and vendor variants. If the VNDK version is not
+	// "current", it will append the VNDK version to the name suffix.
+	var vndkVersion string
+	var nameSuffix string
+	if c.inProduct() {
+		vndkVersion = ctx.DeviceConfig().ProductVndkVersion()
+		nameSuffix = productSuffix
+	} else {
+		vndkVersion = ctx.DeviceConfig().VndkVersion()
+		nameSuffix = vendorSuffix
+	}
+	if vndkVersion == "current" {
+		vndkVersion = ctx.DeviceConfig().PlatformVndkVersion()
+	}
+	if c.Properties.VndkVersion != vndkVersion {
+		// add version suffix only if the module is using different vndk version than the
+		// version in product or vendor partition.
+		nameSuffix += "." + c.Properties.VndkVersion
+	}
+	return nameSuffix
+}
+
 func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) {
+	// Handle the case of a test module split by `test_per_src` mutator.
+	//
+	// The `test_per_src` mutator adds an extra variation named "", depending on all the other
+	// `test_per_src` variations of the test module. Set `outputFile` to an empty path for this
+	// module and return early, as this module does not produce an output file per se.
+	if c.IsTestPerSrcAllTestsVariation() {
+		c.outputFile = android.OptionalPath{}
+		return
+	}
+
 	c.makeLinkType = c.getMakeLinkType(actx)
 
+	c.Properties.SubName = ""
+
+	if c.Target().NativeBridge == android.NativeBridgeEnabled {
+		c.Properties.SubName += nativeBridgeSuffix
+	}
+
+	_, llndk := c.linker.(*llndkStubDecorator)
+	_, llndkHeader := c.linker.(*llndkHeadersDecorator)
+	if llndk || llndkHeader || (c.UseVndk() && c.HasVendorVariant()) {
+		// .vendor.{version} suffix is added for vendor variant or .product.{version} suffix is
+		// added for product variant only when we have vendor and product variants with core
+		// variant. The suffix is not added for vendor-only or product-only module.
+		c.Properties.SubName += c.getNameSuffixWithVndkVersion(actx)
+	} else if _, ok := c.linker.(*vndkPrebuiltLibraryDecorator); ok {
+		// .vendor suffix is added for backward compatibility with VNDK snapshot whose names with
+		// such suffixes are already hard-coded in prebuilts/vndk/.../Android.bp.
+		c.Properties.SubName += vendorSuffix
+	} else if c.InRecovery() && !c.OnlyInRecovery() {
+		c.Properties.SubName += recoverySuffix
+	}
+
 	ctx := &moduleContext{
 		ModuleContext: actx,
 		moduleContextImpl: moduleContextImpl{
@@ -956,6 +1358,7 @@
 
 	flags := Flags{
 		Toolchain: c.toolchain(ctx),
+		EmitXrefs: ctx.Config().EmitXrefRules(),
 	}
 	if c.compiler != nil {
 		flags = c.compiler.compilerFlags(ctx, flags, deps)
@@ -970,7 +1373,7 @@
 		flags = c.sanitize.flags(ctx, flags)
 	}
 	if c.coverage != nil {
-		flags = c.coverage.flags(ctx, flags)
+		flags, deps = c.coverage.flags(ctx, flags, deps)
 	}
 	if c.lto != nil {
 		flags = c.lto.flags(ctx, flags)
@@ -988,17 +1391,17 @@
 		return
 	}
 
-	flags.CFlags, _ = filterList(flags.CFlags, config.IllegalFlags)
-	flags.CppFlags, _ = filterList(flags.CppFlags, config.IllegalFlags)
-	flags.ConlyFlags, _ = filterList(flags.ConlyFlags, config.IllegalFlags)
+	flags.Local.CFlags, _ = filterList(flags.Local.CFlags, config.IllegalFlags)
+	flags.Local.CppFlags, _ = filterList(flags.Local.CppFlags, config.IllegalFlags)
+	flags.Local.ConlyFlags, _ = filterList(flags.Local.ConlyFlags, config.IllegalFlags)
 
-	flags.GlobalFlags = append(flags.GlobalFlags, deps.Flags...)
+	flags.Local.CommonFlags = append(flags.Local.CommonFlags, deps.Flags...)
 
 	for _, dir := range deps.IncludeDirs {
-		flags.GlobalFlags = append(flags.GlobalFlags, "-I"+dir)
+		flags.Local.CommonFlags = append(flags.Local.CommonFlags, "-I"+dir.String())
 	}
 	for _, dir := range deps.SystemIncludeDirs {
-		flags.GlobalFlags = append(flags.GlobalFlags, "-isystem "+dir)
+		flags.Local.CommonFlags = append(flags.Local.CommonFlags, "-isystem "+dir.String())
 	}
 
 	c.flags = flags
@@ -1006,14 +1409,17 @@
 	if c.sabi != nil {
 		flags = c.sabi.flags(ctx, flags)
 	}
+
+	flags.AssemblerWithCpp = inList("-xassembler-with-cpp", flags.Local.AsFlags)
+
 	// Optimization to reduce size of build.ninja
 	// Replace the long list of flags for each file with a module-local variable
-	ctx.Variable(pctx, "cflags", strings.Join(flags.CFlags, " "))
-	ctx.Variable(pctx, "cppflags", strings.Join(flags.CppFlags, " "))
-	ctx.Variable(pctx, "asflags", strings.Join(flags.AsFlags, " "))
-	flags.CFlags = []string{"$cflags"}
-	flags.CppFlags = []string{"$cppflags"}
-	flags.AsFlags = []string{"$asflags"}
+	ctx.Variable(pctx, "cflags", strings.Join(flags.Local.CFlags, " "))
+	ctx.Variable(pctx, "cppflags", strings.Join(flags.Local.CppFlags, " "))
+	ctx.Variable(pctx, "asflags", strings.Join(flags.Local.AsFlags, " "))
+	flags.Local.CFlags = []string{"$cflags"}
+	flags.Local.CppFlags = []string{"$cppflags"}
+	flags.Local.AsFlags = []string{"$asflags"}
 
 	var objs Objects
 	if c.compiler != nil {
@@ -1021,6 +1427,7 @@
 		if ctx.Failed() {
 			return
 		}
+		c.kytheFiles = objs.kytheFiles
 	}
 
 	if c.linker != nil {
@@ -1038,7 +1445,7 @@
 		// module is marked with 'bootstrap: true').
 		if c.HasStubsVariants() &&
 			android.DirectlyInAnyApex(ctx, ctx.baseModuleName()) &&
-			!c.inRecovery() && !c.useVndk() && !c.static() && !c.isCoverageVariant() &&
+			!c.InRecovery() && !c.UseVndk() && !c.static() && !c.isCoverageVariant() &&
 			c.IsStubs() {
 			c.Properties.HideFromMake = false // unhide
 			// Note: this is still non-installable
@@ -1185,7 +1592,7 @@
 }
 
 // Split name#version into name and version
-func stubsLibNameAndVersion(name string) (string, string) {
+func StubsLibNameAndVersion(name string) (string, string) {
 	if sharp := strings.LastIndex(name, "#"); sharp != -1 && sharp != len(name)-1 {
 		version := name[sharp+1:]
 		libname := name[:sharp]
@@ -1225,21 +1632,20 @@
 		// The caller can then know to add the variantLibs dependencies differently from the
 		// nonvariantLibs
 
-		llndkLibraries := llndkLibraries(actx.Config())
 		vendorPublicLibraries := vendorPublicLibraries(actx.Config())
 		rewriteNdkLibs := func(list []string) (nonvariantLibs []string, variantLibs []string) {
 			variantLibs = []string{}
 			nonvariantLibs = []string{}
 			for _, entry := range list {
 				// strip #version suffix out
-				name, _ := stubsLibNameAndVersion(entry)
+				name, _ := StubsLibNameAndVersion(entry)
 				if ctx.useSdk() && inList(name, ndkPrebuiltSharedLibraries) {
 					if !inList(name, ndkMigratedLibs) {
 						nonvariantLibs = append(nonvariantLibs, name+".ndk."+version)
 					} else {
 						variantLibs = append(variantLibs, name+ndkLibrarySuffix)
 					}
-				} else if ctx.useVndk() && inList(name, *llndkLibraries) {
+				} else if ctx.useVndk() && isLlndkLibrary(name, ctx.Config()) {
 					nonvariantLibs = append(nonvariantLibs, name+llndkLibrarySuffix)
 				} else if (ctx.Platform() || ctx.ProductSpecific()) && inList(name, *vendorPublicLibraries) {
 					vendorPublicLib := name + vendorPublicLibrarySuffix
@@ -1279,10 +1685,8 @@
 			depTag = headerExportDepTag
 		}
 		if buildStubs {
-			actx.AddFarVariationDependencies([]blueprint.Variation{
-				{Mutator: "arch", Variation: ctx.Target().String()},
-				{Mutator: "image", Variation: c.imageVariation()},
-			}, depTag, lib)
+			actx.AddFarVariationDependencies(append(ctx.Target().Variations(), c.ImageVariation()),
+				depTag, lib)
 		} else {
 			actx.AddVariationDependencies(nil, depTag, lib)
 		}
@@ -1307,7 +1711,7 @@
 	}
 
 	for _, lib := range deps.StaticLibs {
-		depTag := staticDepTag
+		depTag := StaticDepTag
 		if inList(lib, deps.ReexportStaticLibHeaders) {
 			depTag = staticExportDepTag
 		}
@@ -1325,27 +1729,27 @@
 		{Mutator: "link", Variation: "static"},
 	}, lateStaticDepTag, deps.LateStaticLibs...)
 
-	addSharedLibDependencies := func(depTag dependencyTag, name string, version string) {
+	addSharedLibDependencies := func(depTag DependencyTag, name string, version string) {
 		var variations []blueprint.Variation
 		variations = append(variations, blueprint.Variation{Mutator: "link", Variation: "shared"})
-		versionVariantAvail := !ctx.useVndk() && !c.inRecovery()
+		versionVariantAvail := !ctx.useVndk() && !c.InRecovery()
 		if version != "" && versionVariantAvail {
 			// Version is explicitly specified. i.e. libFoo#30
 			variations = append(variations, blueprint.Variation{Mutator: "version", Variation: version})
-			depTag.explicitlyVersioned = true
+			depTag.ExplicitlyVersioned = true
 		}
 		actx.AddVariationDependencies(variations, depTag, name)
 
 		// If the version is not specified, add dependency to the latest stubs library.
 		// The stubs library will be used when the depending module is built for APEX and
 		// the dependent module is not in the same APEX.
-		latestVersion := latestStubsVersionFor(actx.Config(), name)
+		latestVersion := LatestStubsVersionFor(actx.Config(), name)
 		if version == "" && latestVersion != "" && versionVariantAvail {
 			actx.AddVariationDependencies([]blueprint.Variation{
 				{Mutator: "link", Variation: "shared"},
 				{Mutator: "version", Variation: latestVersion},
 			}, depTag, name)
-			// Note that depTag.explicitlyVersioned is false in this case.
+			// Note that depTag.ExplicitlyVersioned is false in this case.
 		}
 	}
 
@@ -1353,7 +1757,7 @@
 	var sharedLibNames []string
 
 	for _, lib := range deps.SharedLibs {
-		depTag := sharedDepTag
+		depTag := SharedDepTag
 		if inList(lib, deps.ReexportSharedLibHeaders) {
 			depTag = sharedExportDepTag
 		}
@@ -1362,7 +1766,7 @@
 			lib = impl
 		}
 
-		name, version := stubsLibNameAndVersion(lib)
+		name, version := StubsLibNameAndVersion(lib)
 		sharedLibNames = append(sharedLibNames, name)
 
 		addSharedLibDependencies(depTag, name, version)
@@ -1395,10 +1799,10 @@
 	actx.AddVariationDependencies(nil, objDepTag, deps.ObjFiles...)
 
 	if deps.CrtBegin != "" {
-		actx.AddVariationDependencies(nil, crtBeginDepTag, deps.CrtBegin)
+		actx.AddVariationDependencies(nil, CrtBeginDepTag, deps.CrtBegin)
 	}
 	if deps.CrtEnd != "" {
-		actx.AddVariationDependencies(nil, crtEndDepTag, deps.CrtEnd)
+		actx.AddVariationDependencies(nil, CrtEndDepTag, deps.CrtEnd)
 	}
 	if deps.LinkerFlagsFile != "" {
 		actx.AddDependency(c, linkerFlagsDepTag, deps.LinkerFlagsFile)
@@ -1419,12 +1823,8 @@
 
 	if vndkdep := c.vndkdep; vndkdep != nil {
 		if vndkdep.isVndkExt() {
-			baseModuleMode := vendorMode
-			if actx.DeviceConfig().VndkVersion() == "" {
-				baseModuleMode = coreMode
-			}
 			actx.AddVariationDependencies([]blueprint.Variation{
-				{Mutator: "image", Variation: baseModuleMode},
+				c.ImageVariation(),
 				{Mutator: "link", Variation: "shared"},
 			}, vndkExtDepTag, vndkdep.getVndkExtendsModuleName())
 		}
@@ -1439,52 +1839,58 @@
 
 // Whether a module can link to another module, taking into
 // account NDK linking.
-func checkLinkType(ctx android.ModuleContext, from *Module, to *Module, tag dependencyTag) {
-	if from.Target().Os != android.Android {
+func checkLinkType(ctx android.ModuleContext, from LinkableInterface, to LinkableInterface, tag DependencyTag) {
+	if from.Module().Target().Os != android.Android {
 		// Host code is not restricted
 		return
 	}
-	if from.Properties.UseVndk {
+
+	// VNDK is cc.Module supported only for now.
+	if ccFrom, ok := from.(*Module); ok && from.UseVndk() {
 		// Though vendor code is limited by the vendor mutator,
 		// each vendor-available module needs to check
 		// link-type for VNDK.
-		if from.vndkdep != nil {
-			from.vndkdep.vndkCheckLinkType(ctx, to, tag)
+		if ccTo, ok := to.(*Module); ok {
+			if ccFrom.vndkdep != nil {
+				ccFrom.vndkdep.vndkCheckLinkType(ctx, ccTo, tag)
+			}
+		} else {
+			ctx.ModuleErrorf("Attempting to link VNDK cc.Module with unsupported module type")
 		}
 		return
 	}
-	if String(from.Properties.Sdk_version) == "" {
+	if from.SdkVersion() == "" {
 		// Platform code can link to anything
 		return
 	}
-	if from.inRecovery() {
+	if from.InRecovery() {
 		// Recovery code is not NDK
 		return
 	}
-	if _, ok := to.linker.(*toolchainLibraryDecorator); ok {
+	if to.ToolchainLibrary() {
 		// These are always allowed
 		return
 	}
-	if _, ok := to.linker.(*ndkPrebuiltStlLinker); ok {
+	if to.NdkPrebuiltStl() {
 		// These are allowed, but they don't set sdk_version
 		return
 	}
-	if _, ok := to.linker.(*stubDecorator); ok {
+	if to.StubDecorator() {
 		// These aren't real libraries, but are the stub shared libraries that are included in
 		// the NDK.
 		return
 	}
 
-	if strings.HasPrefix(ctx.ModuleName(), "libclang_rt.") && to.Name() == "libc++" {
+	if strings.HasPrefix(ctx.ModuleName(), "libclang_rt.") && to.Module().Name() == "libc++" {
 		// Bug: http://b/121358700 - Allow libclang_rt.* shared libraries (with sdk_version)
 		// to link to libc++ (non-NDK and without sdk_version).
 		return
 	}
 
-	if String(to.Properties.Sdk_version) == "" {
+	if to.SdkVersion() == "" {
 		// NDK code linking to platform code is never okay.
 		ctx.ModuleErrorf("depends on non-NDK-built library %q",
-			ctx.OtherModuleName(to))
+			ctx.OtherModuleName(to.Module()))
 		return
 	}
 
@@ -1494,36 +1900,36 @@
 	// APIs.
 
 	// Current can link against anything.
-	if String(from.Properties.Sdk_version) != "current" {
+	if from.SdkVersion() != "current" {
 		// Otherwise we need to check.
-		if String(to.Properties.Sdk_version) == "current" {
+		if to.SdkVersion() == "current" {
 			// Current can't be linked against by anything else.
 			ctx.ModuleErrorf("links %q built against newer API version %q",
-				ctx.OtherModuleName(to), "current")
+				ctx.OtherModuleName(to.Module()), "current")
 		} else {
-			fromApi, err := strconv.Atoi(String(from.Properties.Sdk_version))
+			fromApi, err := strconv.Atoi(from.SdkVersion())
 			if err != nil {
 				ctx.PropertyErrorf("sdk_version",
 					"Invalid sdk_version value (must be int or current): %q",
-					String(from.Properties.Sdk_version))
+					from.SdkVersion())
 			}
-			toApi, err := strconv.Atoi(String(to.Properties.Sdk_version))
+			toApi, err := strconv.Atoi(to.SdkVersion())
 			if err != nil {
 				ctx.PropertyErrorf("sdk_version",
 					"Invalid sdk_version value (must be int or current): %q",
-					String(to.Properties.Sdk_version))
+					to.SdkVersion())
 			}
 
 			if toApi > fromApi {
 				ctx.ModuleErrorf("links %q built against newer API version %q",
-					ctx.OtherModuleName(to), String(to.Properties.Sdk_version))
+					ctx.OtherModuleName(to.Module()), to.SdkVersion())
 			}
 		}
 	}
 
 	// Also check that the two STL choices are compatible.
-	fromStl := from.stl.Properties.SelectedStl
-	toStl := to.stl.Properties.SelectedStl
+	fromStl := from.SelectedStl()
+	toStl := to.SelectedStl()
 	if fromStl == "" || toStl == "" {
 		// Libraries that don't use the STL are unrestricted.
 	} else if fromStl == "ndk_system" || toStl == "ndk_system" {
@@ -1532,8 +1938,8 @@
 		// using either libc++ or nothing.
 	} else if getNdkStlFamily(from) != getNdkStlFamily(to) {
 		ctx.ModuleErrorf("uses %q and depends on %q which uses incompatible %q",
-			from.stl.Properties.SelectedStl, ctx.OtherModuleName(to),
-			to.stl.Properties.SelectedStl)
+			from.SelectedStl(), ctx.OtherModuleName(to.Module()),
+			to.SelectedStl())
 	}
 }
 
@@ -1542,7 +1948,6 @@
 // it is subject to be double loaded. Such lib should be explicitly marked as double_loadable: true
 // or as vndk-sp (vndk: { enabled: true, support_system_process: true}).
 func checkDoubleLoadableLibraries(ctx android.TopDownMutatorContext) {
-	llndkLibraries := llndkLibraries(ctx.Config())
 	check := func(child, parent android.Module) bool {
 		to, ok := child.(*Module)
 		if !ok {
@@ -1555,11 +1960,11 @@
 		}
 
 		// if target lib has no vendor variant, keep checking dependency graph
-		if !to.hasVendorVariant() {
+		if !to.HasVendorVariant() {
 			return true
 		}
 
-		if to.isVndkSp() || inList(child.Name(), *llndkLibraries) || Bool(to.VendorProperties.Double_loadable) {
+		if to.isVndkSp() || to.isLlndk(ctx.Config()) || Bool(to.VendorProperties.Double_loadable) {
 			return false
 		}
 
@@ -1574,7 +1979,7 @@
 	}
 	if module, ok := ctx.Module().(*Module); ok {
 		if lib, ok := module.linker.(*libraryDecorator); ok && lib.shared() {
-			if inList(ctx.ModuleName(), *llndkLibraries) || Bool(module.VendorProperties.Double_loadable) {
+			if module.isLlndk(ctx.Config()) || Bool(module.VendorProperties.Double_loadable) {
 				ctx.WalkDeps(check)
 			}
 		}
@@ -1585,10 +1990,9 @@
 func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
 	var depPaths PathDeps
 
-	directStaticDeps := []*Module{}
-	directSharedDeps := []*Module{}
+	directStaticDeps := []LinkableInterface{}
+	directSharedDeps := []LinkableInterface{}
 
-	llndkLibraries := llndkLibraries(ctx.Config())
 	vendorPublicLibraries := vendorPublicLibraries(ctx.Config())
 
 	reexportExporter := func(exporter exportedFlagsProducer) {
@@ -1596,14 +2000,16 @@
 		depPaths.ReexportedSystemDirs = append(depPaths.ReexportedSystemDirs, exporter.exportedSystemDirs()...)
 		depPaths.ReexportedFlags = append(depPaths.ReexportedFlags, exporter.exportedFlags()...)
 		depPaths.ReexportedDeps = append(depPaths.ReexportedDeps, exporter.exportedDeps()...)
+		depPaths.ReexportedGeneratedHeaders = append(depPaths.ReexportedGeneratedHeaders, exporter.exportedGeneratedHeaders()...)
 	}
 
 	ctx.VisitDirectDeps(func(dep android.Module) {
 		depName := ctx.OtherModuleName(dep)
 		depTag := ctx.OtherModuleDependencyTag(dep)
 
-		ccDep, _ := dep.(*Module)
-		if ccDep == nil {
+		ccDep, ok := dep.(LinkableInterface)
+		if !ok {
+
 			// handling for a few module types that aren't cc Module but that are also supported
 			switch depTag {
 			case genSourceDepTag:
@@ -1618,14 +2024,18 @@
 			case genHeaderDepTag, genHeaderExportDepTag:
 				if genRule, ok := dep.(genrule.SourceFileGenerator); ok {
 					depPaths.GeneratedHeaders = append(depPaths.GeneratedHeaders,
+						genRule.GeneratedSourceFiles()...)
+					depPaths.GeneratedDeps = append(depPaths.GeneratedDeps,
 						genRule.GeneratedDeps()...)
-					dirs := genRule.GeneratedHeaderDirs().Strings()
+					dirs := genRule.GeneratedHeaderDirs()
 					depPaths.IncludeDirs = append(depPaths.IncludeDirs, dirs...)
 					if depTag == genHeaderExportDepTag {
 						depPaths.ReexportedDirs = append(depPaths.ReexportedDirs, dirs...)
+						depPaths.ReexportedGeneratedHeaders = append(depPaths.ReexportedGeneratedHeaders,
+							genRule.GeneratedSourceFiles()...)
 						depPaths.ReexportedDeps = append(depPaths.ReexportedDeps, genRule.GeneratedDeps()...)
 						// Add these re-exported flags to help header-abi-dumper to infer the abi exported by a library.
-						c.sabi.Properties.ReexportedIncludes = append(c.sabi.Properties.ReexportedIncludes, dirs...)
+						c.sabi.Properties.ReexportedIncludes = append(c.sabi.Properties.ReexportedIncludes, dirs.Strings()...)
 
 					}
 				} else {
@@ -1661,9 +2071,10 @@
 
 		// re-exporting flags
 		if depTag == reuseObjTag {
-			if l, ok := ccDep.compiler.(libraryInterface); ok {
+			// reusing objects only make sense for cc.Modules.
+			if ccReuseDep, ok := ccDep.(*Module); ok && ccDep.CcLibraryInterface() {
 				c.staticVariant = ccDep
-				objs, exporter := l.reuseObjs()
+				objs, exporter := ccReuseDep.compiler.(libraryInterface).reuseObjs()
 				depPaths.Objs = depPaths.Objs.Append(objs)
 				reexportExporter(exporter)
 				return
@@ -1671,30 +2082,31 @@
 		}
 
 		if depTag == staticVariantTag {
-			if _, ok := ccDep.compiler.(libraryInterface); ok {
+			// staticVariants are a cc.Module specific concept.
+			if _, ok := ccDep.(*Module); ok && ccDep.CcLibraryInterface() {
 				c.staticVariant = ccDep
 				return
 			}
 		}
 
-		// Extract explicitlyVersioned field from the depTag and reset it inside the struct.
-		// Otherwise, sharedDepTag and lateSharedDepTag with explicitlyVersioned set to true
-		// won't be matched to sharedDepTag and lateSharedDepTag.
+		// Extract ExplicitlyVersioned field from the depTag and reset it inside the struct.
+		// Otherwise, SharedDepTag and lateSharedDepTag with ExplicitlyVersioned set to true
+		// won't be matched to SharedDepTag and lateSharedDepTag.
 		explicitlyVersioned := false
-		if t, ok := depTag.(dependencyTag); ok {
-			explicitlyVersioned = t.explicitlyVersioned
-			t.explicitlyVersioned = false
+		if t, ok := depTag.(DependencyTag); ok {
+			explicitlyVersioned = t.ExplicitlyVersioned
+			t.ExplicitlyVersioned = false
 			depTag = t
 		}
 
-		if t, ok := depTag.(dependencyTag); ok && t.library {
+		if t, ok := depTag.(DependencyTag); ok && t.Library {
 			depIsStatic := false
 			switch depTag {
-			case staticDepTag, staticExportDepTag, lateStaticDepTag, wholeStaticDepTag:
+			case StaticDepTag, staticExportDepTag, lateStaticDepTag, wholeStaticDepTag:
 				depIsStatic = true
 			}
-			if dependentLibrary, ok := ccDep.linker.(*libraryDecorator); ok && !depIsStatic {
-				depIsStubs := dependentLibrary.buildStubs()
+			if ccDep.CcLibrary() && !depIsStatic {
+				depIsStubs := ccDep.BuildStubs()
 				depHasStubs := ccDep.HasStubsVariants()
 				depInSameApex := android.DirectlyInApex(c.ApexName(), depName)
 				depInPlatform := !android.DirectlyInAnyApex(ctx, depName)
@@ -1711,7 +2123,7 @@
 					// If not building for APEX, use stubs only when it is from
 					// an APEX (and not from platform)
 					useThisDep = (depInPlatform != depIsStubs)
-					if c.inRecovery() || c.bootstrap() {
+					if c.InRecovery() || c.bootstrap() {
 						// However, for recovery or bootstrap modules,
 						// always link to non-stub variant
 						useThisDep = !depIsStubs
@@ -1727,85 +2139,97 @@
 				}
 			}
 
-			if i, ok := ccDep.linker.(exportedFlagsProducer); ok {
-				depPaths.IncludeDirs = append(depPaths.IncludeDirs, i.exportedDirs()...)
-				depPaths.SystemIncludeDirs = append(depPaths.SystemIncludeDirs, i.exportedSystemDirs()...)
-				depPaths.GeneratedHeaders = append(depPaths.GeneratedHeaders, i.exportedDeps()...)
-				depPaths.Flags = append(depPaths.Flags, i.exportedFlags()...)
+			depPaths.IncludeDirs = append(depPaths.IncludeDirs, ccDep.IncludeDirs()...)
 
-				if t.reexportFlags {
-					reexportExporter(i)
-					// Add these re-exported flags to help header-abi-dumper to infer the abi exported by a library.
-					// Re-exported shared library headers must be included as well since they can help us with type information
-					// about template instantiations (instantiated from their headers).
-					// -isystem headers are not included since for bionic libraries, abi-filtering is taken care of by version
-					// scripts.
-					c.sabi.Properties.ReexportedIncludes = append(
-						c.sabi.Properties.ReexportedIncludes, i.exportedDirs()...)
+			// Exporting flags only makes sense for cc.Modules
+			if _, ok := ccDep.(*Module); ok {
+				if i, ok := ccDep.(*Module).linker.(exportedFlagsProducer); ok {
+					depPaths.SystemIncludeDirs = append(depPaths.SystemIncludeDirs, i.exportedSystemDirs()...)
+					depPaths.GeneratedHeaders = append(depPaths.GeneratedHeaders, i.exportedGeneratedHeaders()...)
+					depPaths.GeneratedDeps = append(depPaths.GeneratedDeps, i.exportedDeps()...)
+					depPaths.Flags = append(depPaths.Flags, i.exportedFlags()...)
+
+					if t.ReexportFlags {
+						reexportExporter(i)
+						// Add these re-exported flags to help header-abi-dumper to infer the abi exported by a library.
+						// Re-exported shared library headers must be included as well since they can help us with type information
+						// about template instantiations (instantiated from their headers).
+						// -isystem headers are not included since for bionic libraries, abi-filtering is taken care of by version
+						// scripts.
+						c.sabi.Properties.ReexportedIncludes = append(
+							c.sabi.Properties.ReexportedIncludes, i.exportedDirs().Strings()...)
+					}
 				}
 			}
-
 			checkLinkType(ctx, c, ccDep, t)
 		}
 
 		var ptr *android.Paths
 		var depPtr *android.Paths
 
-		linkFile := ccDep.outputFile
+		linkFile := ccDep.OutputFile()
 		depFile := android.OptionalPath{}
 
 		switch depTag {
-		case ndkStubDepTag, sharedDepTag, sharedExportDepTag:
+		case ndkStubDepTag, SharedDepTag, sharedExportDepTag:
 			ptr = &depPaths.SharedLibs
 			depPtr = &depPaths.SharedLibsDeps
-			depFile = ccDep.linker.(libraryInterface).toc()
+			depFile = ccDep.Toc()
 			directSharedDeps = append(directSharedDeps, ccDep)
+
 		case earlySharedDepTag:
 			ptr = &depPaths.EarlySharedLibs
 			depPtr = &depPaths.EarlySharedLibsDeps
-			depFile = ccDep.linker.(libraryInterface).toc()
+			depFile = ccDep.Toc()
 			directSharedDeps = append(directSharedDeps, ccDep)
 		case lateSharedDepTag, ndkLateStubDepTag:
 			ptr = &depPaths.LateSharedLibs
 			depPtr = &depPaths.LateSharedLibsDeps
-			depFile = ccDep.linker.(libraryInterface).toc()
-		case staticDepTag, staticExportDepTag:
+			depFile = ccDep.Toc()
+		case StaticDepTag, staticExportDepTag:
 			ptr = nil
 			directStaticDeps = append(directStaticDeps, ccDep)
 		case lateStaticDepTag:
 			ptr = &depPaths.LateStaticLibs
 		case wholeStaticDepTag:
 			ptr = &depPaths.WholeStaticLibs
-			staticLib, ok := ccDep.linker.(libraryInterface)
-			if !ok || !staticLib.static() {
+			if !ccDep.CcLibraryInterface() || !ccDep.Static() {
 				ctx.ModuleErrorf("module %q not a static library", depName)
 				return
 			}
 
-			if missingDeps := staticLib.getWholeStaticMissingDeps(); missingDeps != nil {
-				postfix := " (required by " + ctx.OtherModuleName(dep) + ")"
-				for i := range missingDeps {
-					missingDeps[i] += postfix
+			// Because the static library objects are included, this only makes sense
+			// in the context of proper cc.Modules.
+			if ccWholeStaticLib, ok := ccDep.(*Module); ok {
+				staticLib := ccWholeStaticLib.linker.(libraryInterface)
+				if missingDeps := staticLib.getWholeStaticMissingDeps(); missingDeps != nil {
+					postfix := " (required by " + ctx.OtherModuleName(dep) + ")"
+					for i := range missingDeps {
+						missingDeps[i] += postfix
+					}
+					ctx.AddMissingDependencies(missingDeps)
 				}
-				ctx.AddMissingDependencies(missingDeps)
+				depPaths.WholeStaticLibObjs = depPaths.WholeStaticLibObjs.Append(staticLib.objs())
+			} else {
+				ctx.ModuleErrorf(
+					"non-cc.Modules cannot be included as whole static libraries.", depName)
+				return
 			}
-			depPaths.WholeStaticLibObjs = depPaths.WholeStaticLibObjs.Append(staticLib.objs())
 		case headerDepTag:
 			// Nothing
 		case objDepTag:
 			depPaths.Objs.objFiles = append(depPaths.Objs.objFiles, linkFile.Path())
-		case crtBeginDepTag:
+		case CrtBeginDepTag:
 			depPaths.CrtBegin = linkFile
-		case crtEndDepTag:
+		case CrtEndDepTag:
 			depPaths.CrtEnd = linkFile
 		case dynamicLinkerDepTag:
 			depPaths.DynamicLinker = linkFile
 		}
 
 		switch depTag {
-		case staticDepTag, staticExportDepTag, lateStaticDepTag:
-			staticLib, ok := ccDep.linker.(libraryInterface)
-			if !ok || !staticLib.static() {
+		case StaticDepTag, staticExportDepTag, lateStaticDepTag:
+			if !ccDep.CcLibraryInterface() || !ccDep.Static() {
 				ctx.ModuleErrorf("module %q not a static library", depName)
 				return
 			}
@@ -1813,16 +2237,23 @@
 			// When combining coverage files for shared libraries and executables, coverage files
 			// in static libraries act as if they were whole static libraries. The same goes for
 			// source based Abi dump files.
-			depPaths.StaticLibObjs.coverageFiles = append(depPaths.StaticLibObjs.coverageFiles,
-				staticLib.objs().coverageFiles...)
-			depPaths.StaticLibObjs.sAbiDumpFiles = append(depPaths.StaticLibObjs.sAbiDumpFiles,
-				staticLib.objs().sAbiDumpFiles...)
-
+			// This should only be done for cc.Modules
+			if c, ok := ccDep.(*Module); ok {
+				staticLib := c.linker.(libraryInterface)
+				depPaths.StaticLibObjs.coverageFiles = append(depPaths.StaticLibObjs.coverageFiles,
+					staticLib.objs().coverageFiles...)
+				depPaths.StaticLibObjs.sAbiDumpFiles = append(depPaths.StaticLibObjs.sAbiDumpFiles,
+					staticLib.objs().sAbiDumpFiles...)
+			}
 		}
 
 		if ptr != nil {
 			if !linkFile.Valid() {
-				ctx.ModuleErrorf("module %q missing output file", depName)
+				if !ctx.Config().AllowMissingDependencies() {
+					ctx.ModuleErrorf("module %q missing output file", depName)
+				} else {
+					ctx.AddMissingDependencies([]string{depName})
+				}
 				return
 			}
 			*ptr = append(*ptr, linkFile.Path())
@@ -1840,23 +2271,23 @@
 			libName := strings.TrimSuffix(depName, llndkLibrarySuffix)
 			libName = strings.TrimSuffix(libName, vendorPublicLibrarySuffix)
 			libName = strings.TrimPrefix(libName, "prebuilt_")
-			isLLndk := inList(libName, *llndkLibraries)
+			isLLndk := isLlndkLibrary(libName, ctx.Config())
 			isVendorPublicLib := inList(libName, *vendorPublicLibraries)
-			bothVendorAndCoreVariantsExist := ccDep.hasVendorVariant() || isLLndk
+			bothVendorAndCoreVariantsExist := ccDep.HasVendorVariant() || isLLndk
 
-			if ctx.DeviceConfig().VndkUseCoreVariant() && ccDep.isVndk() && !ccDep.mustUseVendorVariant() {
+			if ctx.DeviceConfig().VndkUseCoreVariant() && ccDep.IsVndk() && !ccDep.MustUseVendorVariant() && !c.InRecovery() {
 				// The vendor module is a no-vendor-variant VNDK library.  Depend on the
 				// core module instead.
 				return libName
-			} else if c.useVndk() && bothVendorAndCoreVariantsExist {
+			} else if c.UseVndk() && bothVendorAndCoreVariantsExist {
 				// The vendor module in Make will have been renamed to not conflict with the core
 				// module, so update the dependency name here accordingly.
-				return libName + vendorSuffix
+				return libName + c.getNameSuffixWithVndkVersion(ctx)
 			} else if (ctx.Platform() || ctx.ProductSpecific()) && isVendorPublicLib {
 				return libName + vendorPublicLibrarySuffix
-			} else if ccDep.inRecovery() && !ccDep.onlyInRecovery() {
+			} else if ccDep.InRecovery() && !ccDep.OnlyInRecovery() {
 				return libName + recoverySuffix
-			} else if ccDep.Target().NativeBridge == android.NativeBridgeEnabled {
+			} else if ccDep.Module().Target().NativeBridge == android.NativeBridgeEnabled {
 				return libName + nativeBridgeSuffix
 			} else {
 				return libName
@@ -1865,9 +2296,9 @@
 
 		// Export the shared libs to Make.
 		switch depTag {
-		case sharedDepTag, sharedExportDepTag, lateSharedDepTag, earlySharedDepTag:
-			if dependentLibrary, ok := ccDep.linker.(*libraryDecorator); ok {
-				if dependentLibrary.buildStubs() && android.InAnyApex(depName) {
+		case SharedDepTag, sharedExportDepTag, lateSharedDepTag, earlySharedDepTag:
+			if ccDep.CcLibrary() {
+				if ccDep.BuildStubs() && android.InAnyApex(depName) {
 					// Add the dependency to the APEX(es) providing the library so that
 					// m <module> can trigger building the APEXes as well.
 					for _, an := range android.GetApexesForModule(depName) {
@@ -1882,11 +2313,10 @@
 			c.Properties.AndroidMkSharedLibs = append(
 				c.Properties.AndroidMkSharedLibs, makeLibName(depName))
 		case ndkStubDepTag, ndkLateStubDepTag:
-			ndkStub := ccDep.linker.(*stubDecorator)
 			c.Properties.AndroidMkSharedLibs = append(
 				c.Properties.AndroidMkSharedLibs,
-				depName+"."+ndkStub.properties.ApiLevel)
-		case staticDepTag, staticExportDepTag, lateStaticDepTag:
+				depName+"."+ccDep.ApiLevel())
+		case StaticDepTag, staticExportDepTag, lateStaticDepTag:
 			c.Properties.AndroidMkStaticLibs = append(
 				c.Properties.AndroidMkStaticLibs, makeLibName(depName))
 		case runtimeDepTag:
@@ -1903,18 +2333,22 @@
 
 	// Dedup exported flags from dependencies
 	depPaths.Flags = android.FirstUniqueStrings(depPaths.Flags)
-	depPaths.IncludeDirs = android.FirstUniqueStrings(depPaths.IncludeDirs)
-	depPaths.SystemIncludeDirs = android.FirstUniqueStrings(depPaths.SystemIncludeDirs)
+	depPaths.IncludeDirs = android.FirstUniquePaths(depPaths.IncludeDirs)
+	depPaths.SystemIncludeDirs = android.FirstUniquePaths(depPaths.SystemIncludeDirs)
 	depPaths.GeneratedHeaders = android.FirstUniquePaths(depPaths.GeneratedHeaders)
-	depPaths.ReexportedDirs = android.FirstUniqueStrings(depPaths.ReexportedDirs)
-	depPaths.ReexportedSystemDirs = android.FirstUniqueStrings(depPaths.ReexportedSystemDirs)
+	depPaths.GeneratedDeps = android.FirstUniquePaths(depPaths.GeneratedDeps)
+	depPaths.ReexportedDirs = android.FirstUniquePaths(depPaths.ReexportedDirs)
+	depPaths.ReexportedSystemDirs = android.FirstUniquePaths(depPaths.ReexportedSystemDirs)
 	depPaths.ReexportedFlags = android.FirstUniqueStrings(depPaths.ReexportedFlags)
 	depPaths.ReexportedDeps = android.FirstUniquePaths(depPaths.ReexportedDeps)
+	depPaths.ReexportedGeneratedHeaders = android.FirstUniquePaths(depPaths.ReexportedGeneratedHeaders)
 
 	if c.sabi != nil {
 		c.sabi.Properties.ReexportedIncludes = android.FirstUniqueStrings(c.sabi.Properties.ReexportedIncludes)
 	}
 
+	c.allStaticDeps = gatherTransitiveStaticDeps(directStaticDeps)
+
 	return depPaths
 }
 
@@ -1936,7 +2370,7 @@
 }
 
 func (c *Module) InstallInRecovery() bool {
-	return c.inRecovery()
+	return c.InRecovery()
 }
 
 func (c *Module) HostToolPath() android.OptionalPath {
@@ -1980,30 +2414,41 @@
 	return false
 }
 
+func (c *Module) header() bool {
+	if h, ok := c.linker.(interface {
+		header() bool
+	}); ok {
+		return h.header()
+	}
+	return false
+}
+
 func (c *Module) getMakeLinkType(actx android.ModuleContext) string {
-	name := actx.ModuleName()
-	if c.useVndk() {
+	if c.UseVndk() {
 		if lib, ok := c.linker.(*llndkStubDecorator); ok {
 			if Bool(lib.Properties.Vendor_available) {
 				return "native:vndk"
 			}
 			return "native:vndk_private"
 		}
-		if c.isVndk() && !c.isVndkExt() {
+		if c.IsVndk() && !c.isVndkExt() {
 			if Bool(c.VendorProperties.Vendor_available) {
 				return "native:vndk"
 			}
 			return "native:vndk_private"
 		}
+		if c.inProduct() {
+			return "native:product"
+		}
 		return "native:vendor"
-	} else if c.inRecovery() {
+	} else if c.InRecovery() {
 		return "native:recovery"
 	} else if c.Target().Os == android.Android && String(c.Properties.Sdk_version) != "" {
 		return "native:ndk:none:none"
 		// TODO(b/114741097): use the correct ndk stl once build errors have been fixed
 		//family, link := getNdkStlFamilyAndLinkType(c)
 		//return fmt.Sprintf("native:ndk:%s:%s", family, link)
-	} else if inList(name, *vndkUsingCoreVariantLibraries(actx.Config())) {
+	} else if actx.DeviceConfig().VndkUseCoreVariant() && !c.MustUseVendorVariant() {
 		return "native:platform_vndk"
 	} else {
 		return "native:platform"
@@ -2011,36 +2456,54 @@
 }
 
 // Overrides ApexModule.IsInstallabeToApex()
-// Only shared libraries are installable to APEX.
+// Only shared/runtime libraries and "test_per_src" tests are installable to APEX.
 func (c *Module) IsInstallableToApex() bool {
 	if shared, ok := c.linker.(interface {
 		shared() bool
 	}); ok {
-		return shared.shared()
+		// Stub libs and prebuilt libs in a versioned SDK are not
+		// installable to APEX even though they are shared libs.
+		return shared.shared() && !c.IsStubs() && c.ContainingSdk().Unversioned()
+	} else if _, ok := c.linker.(testPerSrc); ok {
+		return true
 	}
 	return false
 }
 
+func (c *Module) AvailableFor(what string) bool {
+	if linker, ok := c.linker.(interface {
+		availableFor(string) bool
+	}); ok {
+		return c.ApexModuleBase.AvailableFor(what) || linker.availableFor(what)
+	} else {
+		return c.ApexModuleBase.AvailableFor(what)
+	}
+}
+
 func (c *Module) installable() bool {
-	return c.installer != nil && !c.Properties.PreventInstall && c.IsForPlatform() && c.outputFile.Valid()
+	ret := c.installer != nil && !c.Properties.PreventInstall && c.outputFile.Valid()
+
+	// The platform variant doesn't need further condition. Apex variants however might not
+	// be installable because it will likely to be included in the APEX and won't appear
+	// in the system partition.
+	if c.IsForPlatform() {
+		return ret
+	}
+
+	// Special case for modules that are configured to be installed to /data, which includes
+	// test modules. For these modules, both APEX and non-APEX variants are considered as
+	// installable. This is because even the APEX variants won't be included in the APEX, but
+	// will anyway be installed to /data/*.
+	// See b/146995717
+	if c.InstallInData() {
+		return ret
+	}
+
+	return false
 }
 
-func (c *Module) imageVariation() string {
-	variation := "core"
-	if c.useVndk() {
-		variation = "vendor"
-	} else if c.inRecovery() {
-		variation = "recovery"
-	}
-	return variation
-}
-
-func (c *Module) IDEInfo(dpInfo *android.IdeInfo) {
-	outputFiles, err := c.OutputFiles("")
-	if err != nil {
-		panic(err)
-	}
-	dpInfo.Srcs = append(dpInfo.Srcs, outputFiles.Strings()...)
+func (c *Module) AllStaticDeps() []string {
+	return c.allStaticDeps
 }
 
 func (c *Module) AndroidMkWriteAdditionalDependenciesForSourceAbiDiff(w io.Writer) {
@@ -2051,6 +2514,16 @@
 	}
 }
 
+func (c *Module) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
+	if depTag, ok := ctx.OtherModuleDependencyTag(dep).(DependencyTag); ok {
+		if cc, ok := dep.(*Module); ok && cc.IsStubs() && depTag.Shared {
+			// dynamic dep to a stubs lib crosses APEX boundary
+			return false
+		}
+	}
+	return true
+}
+
 //
 // Defaults
 //
@@ -2078,11 +2551,15 @@
 		&VendorProperties{},
 		&BaseCompilerProperties{},
 		&BaseLinkerProperties{},
+		&ObjectLinkerProperties{},
 		&LibraryProperties{},
+		&StaticProperties{},
+		&SharedProperties{},
 		&FlagExporterProperties{},
 		&BinaryLinkerProperties{},
 		&TestProperties{},
 		&TestBinaryProperties{},
+		&FuzzProperties{},
 		&StlProperties{},
 		&SanitizeProperties{},
 		&StripProperties{},
@@ -2098,23 +2575,10 @@
 	)
 
 	android.InitDefaultsModule(module)
-	android.InitApexModule(module)
 
 	return module
 }
 
-const (
-	// coreMode is the variant used for framework-private libraries, or
-	// SDK libraries. (which framework-private libraries can use)
-	coreMode = "core"
-
-	// vendorMode is the variant used for /vendor code that compiles
-	// against the VNDK.
-	vendorMode = "vendor"
-
-	recoveryMode = "recovery"
-)
-
 func squashVendorSrcs(m *Module) {
 	if lib, ok := m.compiler.(*libraryDecorator); ok {
 		lib.baseCompiler.Properties.Srcs = append(lib.baseCompiler.Properties.Srcs,
@@ -2135,63 +2599,9 @@
 	}
 }
 
-func ImageMutator(mctx android.BottomUpMutatorContext) {
-	if mctx.Os() != android.Android {
-		return
-	}
+var _ android.ImageInterface = (*Module)(nil)
 
-	if g, ok := mctx.Module().(*genrule.Module); ok {
-		if props, ok := g.Extra.(*GenruleExtraProperties); ok {
-			var coreVariantNeeded bool = false
-			var vendorVariantNeeded bool = false
-			var recoveryVariantNeeded bool = false
-			if mctx.DeviceConfig().VndkVersion() == "" {
-				coreVariantNeeded = true
-			} else if Bool(props.Vendor_available) {
-				coreVariantNeeded = true
-				vendorVariantNeeded = true
-			} else if mctx.SocSpecific() || mctx.DeviceSpecific() {
-				vendorVariantNeeded = true
-			} else {
-				coreVariantNeeded = true
-			}
-			if Bool(props.Recovery_available) {
-				recoveryVariantNeeded = true
-			}
-
-			if recoveryVariantNeeded {
-				primaryArch := mctx.Config().DevicePrimaryArchType()
-				moduleArch := g.Target().Arch.ArchType
-				if moduleArch != primaryArch {
-					recoveryVariantNeeded = false
-				}
-			}
-
-			var variants []string
-			if coreVariantNeeded {
-				variants = append(variants, coreMode)
-			}
-			if vendorVariantNeeded {
-				variants = append(variants, vendorMode)
-			}
-			if recoveryVariantNeeded {
-				variants = append(variants, recoveryMode)
-			}
-			mod := mctx.CreateVariations(variants...)
-			for i, v := range variants {
-				if v == recoveryMode {
-					m := mod[i].(*genrule.Module)
-					m.Extra.(*GenruleExtraProperties).InRecovery = true
-				}
-			}
-		}
-	}
-
-	m, ok := mctx.Module().(*Module)
-	if !ok {
-		return
-	}
-
+func (m *Module) ImageMutatorBegin(mctx android.BaseModuleContext) {
 	// Sanity check
 	vendorSpecific := mctx.SocSpecific() || mctx.DeviceSpecific()
 	productSpecific := mctx.ProductSpecific()
@@ -2199,7 +2609,6 @@
 	if m.VendorProperties.Vendor_available != nil && vendorSpecific {
 		mctx.PropertyErrorf("vendor_available",
 			"doesn't make sense at the same time as `vendor: true`, `proprietary: true`, or `device_specific:true`")
-		return
 	}
 
 	if vndkdep := m.vndkdep; vndkdep != nil {
@@ -2207,69 +2616,99 @@
 			if productSpecific {
 				mctx.PropertyErrorf("product_specific",
 					"product_specific must not be true when `vndk: {enabled: true}`")
-				return
 			}
 			if vendorSpecific {
 				if !vndkdep.isVndkExt() {
 					mctx.PropertyErrorf("vndk",
 						"must set `extends: \"...\"` to vndk extension")
-					return
 				}
 			} else {
 				if vndkdep.isVndkExt() {
 					mctx.PropertyErrorf("vndk",
 						"must set `vendor: true` to set `extends: %q`",
 						m.getVndkExtendsModuleName())
-					return
 				}
 				if m.VendorProperties.Vendor_available == nil {
 					mctx.PropertyErrorf("vndk",
 						"vendor_available must be set to either true or false when `vndk: {enabled: true}`")
-					return
 				}
 			}
 		} else {
 			if vndkdep.isVndkSp() {
 				mctx.PropertyErrorf("vndk",
 					"must set `enabled: true` to set `support_system_process: true`")
-				return
 			}
 			if vndkdep.isVndkExt() {
 				mctx.PropertyErrorf("vndk",
 					"must set `enabled: true` to set `extends: %q`",
 					m.getVndkExtendsModuleName())
-				return
 			}
 		}
 	}
 
 	var coreVariantNeeded bool = false
-	var vendorVariantNeeded bool = false
 	var recoveryVariantNeeded bool = false
 
-	if mctx.DeviceConfig().VndkVersion() == "" {
+	var vendorVariants []string
+	var productVariants []string
+
+	platformVndkVersion := mctx.DeviceConfig().PlatformVndkVersion()
+	boardVndkVersion := mctx.DeviceConfig().VndkVersion()
+	productVndkVersion := mctx.DeviceConfig().ProductVndkVersion()
+	if boardVndkVersion == "current" {
+		boardVndkVersion = platformVndkVersion
+	}
+	if productVndkVersion == "current" {
+		productVndkVersion = platformVndkVersion
+	}
+
+	if boardVndkVersion == "" {
 		// If the device isn't compiling against the VNDK, we always
 		// use the core mode.
 		coreVariantNeeded = true
 	} else if _, ok := m.linker.(*llndkStubDecorator); ok {
-		// LL-NDK stubs only exist in the vendor variant, since the
-		// real libraries will be used in the core variant.
-		vendorVariantNeeded = true
+		// LL-NDK stubs only exist in the vendor and product variants,
+		// since the real libraries will be used in the core variant.
+		vendorVariants = append(vendorVariants,
+			platformVndkVersion,
+			boardVndkVersion,
+		)
+		productVariants = append(productVariants,
+			platformVndkVersion,
+			productVndkVersion,
+		)
 	} else if _, ok := m.linker.(*llndkHeadersDecorator); ok {
 		// ... and LL-NDK headers as well
-		vendorVariantNeeded = true
-	} else if _, ok := m.linker.(*vndkPrebuiltLibraryDecorator); ok {
+		vendorVariants = append(vendorVariants,
+			platformVndkVersion,
+			boardVndkVersion,
+		)
+		productVariants = append(productVariants,
+			platformVndkVersion,
+			productVndkVersion,
+		)
+	} else if lib, ok := m.linker.(*vndkPrebuiltLibraryDecorator); ok {
 		// Make vendor variants only for the versions in BOARD_VNDK_VERSION and
 		// PRODUCT_EXTRA_VNDK_VERSIONS.
-		vendorVariantNeeded = true
-	} else if m.hasVendorVariant() && !vendorSpecific {
-		// This will be available in both /system and /vendor
-		// or a /system directory that is available to vendor.
+		vendorVariants = append(vendorVariants, lib.version())
+	} else if m.HasVendorVariant() && !vendorSpecific {
+		// This will be available in /system, /vendor and /product
+		// or a /system directory that is available to vendor and product.
 		coreVariantNeeded = true
-		vendorVariantNeeded = 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() {
+			vendorVariants = append(vendorVariants, boardVndkVersion)
+			productVariants = append(productVariants, productVndkVersion)
+		}
 	} else if vendorSpecific && String(m.Properties.Sdk_version) == "" {
 		// This will be available in /vendor (or /odm) only
-		vendorVariantNeeded = true
+		vendorVariants = append(vendorVariants, boardVndkVersion)
 	} else {
 		// This is either in /system (or similar: /data), or is a
 		// modules built with the NDK. Modules built with the NDK
@@ -2277,6 +2716,19 @@
 		coreVariantNeeded = true
 	}
 
+	if boardVndkVersion != "" && productVndkVersion != "" {
+		if coreVariantNeeded && productSpecific && String(m.Properties.Sdk_version) == "" {
+			// The module has "product_specific: true" that does not create core variant.
+			coreVariantNeeded = false
+			productVariants = append(productVariants, productVndkVersion)
+		}
+	} else {
+		// Unless PRODUCT_PRODUCT_VNDK_VERSION is set, product partition has no
+		// restriction to use system libs.
+		// No product variants defined in this case.
+		productVariants = []string{}
+	}
+
 	if Bool(m.Properties.Recovery_available) {
 		recoveryVariantNeeded = true
 	}
@@ -2286,36 +2738,43 @@
 		coreVariantNeeded = false
 	}
 
-	if recoveryVariantNeeded {
-		primaryArch := mctx.Config().DevicePrimaryArchType()
-		moduleArch := m.Target().Arch.ArchType
-		if moduleArch != primaryArch {
-			recoveryVariantNeeded = false
-		}
+	for _, variant := range android.FirstUniqueStrings(vendorVariants) {
+		m.Properties.ExtraVariants = append(m.Properties.ExtraVariants, VendorVariationPrefix+variant)
 	}
 
-	var variants []string
-	if coreVariantNeeded {
-		variants = append(variants, coreMode)
+	for _, variant := range android.FirstUniqueStrings(productVariants) {
+		m.Properties.ExtraVariants = append(m.Properties.ExtraVariants, ProductVariationPrefix+variant)
 	}
-	if vendorVariantNeeded {
-		variants = append(variants, vendorMode)
-	}
-	if recoveryVariantNeeded {
-		variants = append(variants, recoveryMode)
-	}
-	mod := mctx.CreateVariations(variants...)
-	for i, v := range variants {
-		if v == vendorMode {
-			m := mod[i].(*Module)
-			m.Properties.UseVndk = true
-			squashVendorSrcs(m)
-		} else if v == recoveryMode {
-			m := mod[i].(*Module)
-			m.Properties.InRecovery = true
-			m.MakeAsPlatform()
-			squashRecoverySrcs(m)
-		}
+
+	m.Properties.RecoveryVariantNeeded = recoveryVariantNeeded
+	m.Properties.CoreVariantNeeded = coreVariantNeeded
+}
+
+func (c *Module) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
+	return c.Properties.CoreVariantNeeded
+}
+
+func (c *Module) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
+	return c.Properties.RecoveryVariantNeeded
+}
+
+func (c *Module) ExtraImageVariations(ctx android.BaseModuleContext) []string {
+	return c.Properties.ExtraVariants
+}
+
+func (c *Module) SetImageVariation(ctx android.BaseModuleContext, variant string, module android.Module) {
+	m := module.(*Module)
+	if variant == android.RecoveryVariation {
+		m.MakeAsPlatform()
+		squashRecoverySrcs(m)
+	} else if strings.HasPrefix(variant, VendorVariationPrefix) {
+		m.Properties.ImageVariationPrefix = VendorVariationPrefix
+		m.Properties.VndkVersion = strings.TrimPrefix(variant, VendorVariationPrefix)
+		squashVendorSrcs(m)
+	} else if strings.HasPrefix(variant, ProductVariationPrefix) {
+		m.Properties.ImageVariationPrefix = ProductVariationPrefix
+		m.Properties.VndkVersion = strings.TrimPrefix(variant, ProductVariationPrefix)
+		squashVendorSrcs(m)
 	}
 }
 
@@ -2326,6 +2785,31 @@
 	return ctx.Config().PlatformSdkVersion()
 }
 
+func kytheExtractAllFactory() android.Singleton {
+	return &kytheExtractAllSingleton{}
+}
+
+type kytheExtractAllSingleton struct {
+}
+
+func (ks *kytheExtractAllSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+	var xrefTargets android.Paths
+	ctx.VisitAllModules(func(module android.Module) {
+		if ccModule, ok := module.(xref); ok {
+			xrefTargets = append(xrefTargets, ccModule.XrefCcFiles()...)
+		}
+	})
+	// TODO(asmundak): Perhaps emit a rule to output a warning if there were no xrefTargets
+	if len(xrefTargets) > 0 {
+		ctx.Build(pctx, android.BuildParams{
+			Rule:   blueprint.Phony,
+			Output: android.PathForPhony(ctx, "xref_cxx"),
+			Inputs: xrefTargets,
+			//Default: true,
+		})
+	}
+}
+
 var Bool = proptools.Bool
 var BoolDefault = proptools.BoolDefault
 var BoolPtr = proptools.BoolPtr
diff --git a/cc/cc_test.go b/cc/cc_test.go
index ca34185..4d02f4f 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -15,8 +15,6 @@
 package cc
 
 import (
-	"android/soong/android"
-
 	"fmt"
 	"io/ioutil"
 	"os"
@@ -25,6 +23,8 @@
 	"sort"
 	"strings"
 	"testing"
+
+	"android/soong/android"
 )
 
 var buildDir string
@@ -52,14 +52,10 @@
 	os.Exit(run())
 }
 
-func testCcWithConfig(t *testing.T, bp string, config android.Config) *android.TestContext {
-	return testCcWithConfigForOs(t, bp, config, android.Android)
-}
-
-func testCcWithConfigForOs(t *testing.T, bp string, config android.Config, os android.OsType) *android.TestContext {
+func testCcWithConfig(t *testing.T, config android.Config) *android.TestContext {
 	t.Helper()
-	ctx := CreateTestContext(bp, nil, os)
-	ctx.Register()
+	ctx := CreateTestContext()
+	ctx.Register(config)
 
 	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
 	android.FailIfErrored(t, errs)
@@ -71,29 +67,26 @@
 
 func testCc(t *testing.T, bp string) *android.TestContext {
 	t.Helper()
-	config := android.TestArchConfig(buildDir, nil)
+	config := TestConfig(buildDir, android.Android, nil, bp, nil)
 	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
 	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
 
-	return testCcWithConfig(t, bp, config)
+	return testCcWithConfig(t, config)
 }
 
 func testCcNoVndk(t *testing.T, bp string) *android.TestContext {
 	t.Helper()
-	config := android.TestArchConfig(buildDir, nil)
+	config := TestConfig(buildDir, android.Android, nil, bp, nil)
 	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
 
-	return testCcWithConfig(t, bp, config)
+	return testCcWithConfig(t, config)
 }
 
-func testCcError(t *testing.T, pattern string, bp string) {
+func testCcErrorWithConfig(t *testing.T, pattern string, config android.Config) {
 	t.Helper()
-	config := android.TestArchConfig(buildDir, nil)
-	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
-	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
 
-	ctx := CreateTestContext(bp, nil, android.Android)
-	ctx.Register()
+	ctx := CreateTestContext()
+	ctx.Register(config)
 
 	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
 	if len(errs) > 0 {
@@ -110,10 +103,28 @@
 	t.Fatalf("missing expected error %q (0 errors are returned)", pattern)
 }
 
+func testCcError(t *testing.T, pattern string, bp string) {
+	config := TestConfig(buildDir, android.Android, nil, bp, nil)
+	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
+	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+	testCcErrorWithConfig(t, pattern, config)
+	return
+}
+
+func testCcErrorProductVndk(t *testing.T, pattern string, bp string) {
+	config := TestConfig(buildDir, android.Android, nil, bp, nil)
+	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
+	config.TestProductVariables.ProductVndkVersion = StringPtr("current")
+	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+	testCcErrorWithConfig(t, pattern, config)
+	return
+}
+
 const (
-	coreVariant     = "android_arm64_armv8-a_core_shared"
-	vendorVariant   = "android_arm64_armv8-a_vendor_shared"
-	recoveryVariant = "android_arm64_armv8-a_recovery_shared"
+	coreVariant     = "android_arm64_armv8-a_shared"
+	vendorVariant   = "android_vendor.VER_arm64_armv8-a_shared"
+	productVariant  = "android_product.VER_arm64_armv8-a_shared"
+	recoveryVariant = "android_recovery_arm64_armv8-a_shared"
 )
 
 func TestFuchsiaDeps(t *testing.T) {
@@ -130,8 +141,8 @@
 			},
 		}`
 
-	config := android.TestArchConfigFuchsia(buildDir, nil)
-	ctx := testCcWithConfigForOs(t, bp, config, android.Fuchsia)
+	config := TestConfig(buildDir, android.Fuchsia, nil, bp, nil)
+	ctx := testCcWithConfig(t, config)
 
 	rt := false
 	fb := false
@@ -167,8 +178,8 @@
 			},
 		}`
 
-	config := android.TestArchConfigFuchsia(buildDir, nil)
-	ctx := testCcWithConfigForOs(t, bp, config, android.Fuchsia)
+	config := TestConfig(buildDir, android.Fuchsia, nil, bp, nil)
+	ctx := testCcWithConfig(t, config)
 	ld := ctx.ModuleForTests("libTest", "fuchsia_arm64_shared").Rule("ld")
 	var objs []string
 	for _, o := range ld.Inputs {
@@ -212,7 +223,7 @@
 	t.Helper()
 
 	mod := ctx.ModuleForTests(name, vendorVariant).Module().(*Module)
-	if !mod.hasVendorVariant() {
+	if !mod.HasVendorVariant() {
 		t.Errorf("%q must have vendor variant", name)
 	}
 
@@ -229,8 +240,8 @@
 	if mod.vndkdep == nil {
 		t.Fatalf("%q must have `vndkdep`", name)
 	}
-	if !mod.isVndk() {
-		t.Errorf("%q isVndk() must equal to true", name)
+	if !mod.IsVndk() {
+		t.Errorf("%q IsVndk() must equal to true", name)
 	}
 	if mod.isVndkSp() != isVndkSp {
 		t.Errorf("%q isVndkSp() must equal to %t", name, isVndkSp)
@@ -247,28 +258,56 @@
 	}
 }
 
-func checkVndkSnapshot(t *testing.T, ctx *android.TestContext, name, subDir, variant string) {
+func checkVndkSnapshot(t *testing.T, ctx *android.TestContext, moduleName, snapshotFilename, subDir, variant string) {
 	vndkSnapshot := ctx.SingletonForTests("vndk-snapshot")
 
-	snapshotPath := filepath.Join(subDir, name+".so")
-	mod := ctx.ModuleForTests(name, variant).Module().(*Module)
-	if !mod.outputFile.Valid() {
-		t.Errorf("%q must have output\n", name)
+	mod, ok := ctx.ModuleForTests(moduleName, variant).Module().(android.OutputFileProducer)
+	if !ok {
+		t.Errorf("%q must have output\n", moduleName)
 		return
 	}
+	outputFiles, err := mod.OutputFiles("")
+	if err != nil || len(outputFiles) != 1 {
+		t.Errorf("%q must have single output\n", moduleName)
+		return
+	}
+	snapshotPath := filepath.Join(subDir, snapshotFilename)
 
 	out := vndkSnapshot.Output(snapshotPath)
-	if out.Input != mod.outputFile.Path() {
-		t.Errorf("The input of VNDK snapshot must be %q, but %q", out.Input.String(), mod.outputFile.String())
+	if out.Input.String() != outputFiles[0].String() {
+		t.Errorf("The input of VNDK snapshot must be %q, but %q", out.Input.String(), outputFiles[0])
 	}
 }
 
-func TestVndk(t *testing.T) {
-	config := android.TestArchConfig(buildDir, nil)
-	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
-	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+func checkWriteFileOutput(t *testing.T, params android.TestingBuildParams, expected []string) {
+	t.Helper()
+	assertString(t, params.Rule.String(), android.WriteFile.String())
+	actual := strings.FieldsFunc(strings.ReplaceAll(params.Args["content"], "\\n", "\n"), func(r rune) bool { return r == '\n' })
+	assertArrayString(t, actual, expected)
+}
 
-	ctx := testCcWithConfig(t, `
+func checkVndkOutput(t *testing.T, ctx *android.TestContext, output string, expected []string) {
+	t.Helper()
+	vndkSnapshot := ctx.SingletonForTests("vndk-snapshot")
+	checkWriteFileOutput(t, vndkSnapshot.Output(output), expected)
+}
+
+func checkVndkLibrariesOutput(t *testing.T, ctx *android.TestContext, module string, expected []string) {
+	t.Helper()
+	vndkLibraries := ctx.ModuleForTests(module, "")
+
+	var output string
+	if module != "vndkcorevariant.libraries.txt" {
+		output = insertVndkVersion(module, "VER")
+	} else {
+		output = module
+	}
+
+	checkWriteFileOutput(t, vndkLibraries.Output(output), expected)
+}
+
+func TestVndk(t *testing.T) {
+	bp := `
 		cc_library {
 			name: "libvndk",
 			vendor_available: true,
@@ -285,6 +324,7 @@
 				enabled: true,
 			},
 			nocrt: true,
+			stem: "libvndk-private",
 		}
 
 		cc_library {
@@ -295,6 +335,7 @@
 				support_system_process: true,
 			},
 			nocrt: true,
+			suffix: "-x",
 		}
 
 		cc_library {
@@ -305,8 +346,34 @@
 				support_system_process: true,
 			},
 			nocrt: true,
+			target: {
+				vendor: {
+					suffix: "-x",
+				},
+			},
 		}
-	`, config)
+		vndk_libraries_txt {
+			name: "llndk.libraries.txt",
+		}
+		vndk_libraries_txt {
+			name: "vndkcore.libraries.txt",
+		}
+		vndk_libraries_txt {
+			name: "vndksp.libraries.txt",
+		}
+		vndk_libraries_txt {
+			name: "vndkprivate.libraries.txt",
+		}
+		vndk_libraries_txt {
+			name: "vndkcorevariant.libraries.txt",
+		}
+	`
+
+	config := TestConfig(buildDir, android.Android, nil, bp, nil)
+	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
+	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+
+	ctx := testCcWithConfig(t, config)
 
 	checkVndkModule(t, ctx, "libvndk", "vndk-VER", false, "")
 	checkVndkModule(t, ctx, "libvndk_private", "vndk-VER", false, "")
@@ -328,13 +395,124 @@
 	vndkCoreLib2ndPath := filepath.Join(vndkLib2ndPath, "shared", "vndk-core")
 	vndkSpLib2ndPath := filepath.Join(vndkLib2ndPath, "shared", "vndk-sp")
 
-	variant := "android_arm64_armv8-a_vendor_shared"
-	variant2nd := "android_arm_armv7-a-neon_vendor_shared"
+	variant := "android_vendor.VER_arm64_armv8-a_shared"
+	variant2nd := "android_vendor.VER_arm_armv7-a-neon_shared"
 
-	checkVndkSnapshot(t, ctx, "libvndk", vndkCoreLibPath, variant)
-	checkVndkSnapshot(t, ctx, "libvndk", vndkCoreLib2ndPath, variant2nd)
-	checkVndkSnapshot(t, ctx, "libvndk_sp", vndkSpLibPath, variant)
-	checkVndkSnapshot(t, ctx, "libvndk_sp", vndkSpLib2ndPath, variant2nd)
+	checkVndkSnapshot(t, ctx, "libvndk", "libvndk.so", vndkCoreLibPath, variant)
+	checkVndkSnapshot(t, ctx, "libvndk", "libvndk.so", vndkCoreLib2ndPath, variant2nd)
+	checkVndkSnapshot(t, ctx, "libvndk_sp", "libvndk_sp-x.so", vndkSpLibPath, variant)
+	checkVndkSnapshot(t, ctx, "libvndk_sp", "libvndk_sp-x.so", vndkSpLib2ndPath, variant2nd)
+
+	snapshotConfigsPath := filepath.Join(snapshotVariantPath, "configs")
+	checkVndkSnapshot(t, ctx, "llndk.libraries.txt", "llndk.libraries.txt", snapshotConfigsPath, "")
+	checkVndkSnapshot(t, ctx, "vndkcore.libraries.txt", "vndkcore.libraries.txt", snapshotConfigsPath, "")
+	checkVndkSnapshot(t, ctx, "vndksp.libraries.txt", "vndksp.libraries.txt", snapshotConfigsPath, "")
+	checkVndkSnapshot(t, ctx, "vndkprivate.libraries.txt", "vndkprivate.libraries.txt", snapshotConfigsPath, "")
+
+	checkVndkOutput(t, ctx, "vndk/vndk.libraries.txt", []string{
+		"LLNDK: libc.so",
+		"LLNDK: libdl.so",
+		"LLNDK: libft2.so",
+		"LLNDK: libm.so",
+		"VNDK-SP: libc++.so",
+		"VNDK-SP: libvndk_sp-x.so",
+		"VNDK-SP: libvndk_sp_private-x.so",
+		"VNDK-core: libvndk-private.so",
+		"VNDK-core: libvndk.so",
+		"VNDK-private: libft2.so",
+		"VNDK-private: libvndk-private.so",
+		"VNDK-private: libvndk_sp_private-x.so",
+	})
+	checkVndkLibrariesOutput(t, ctx, "llndk.libraries.txt", []string{"libc.so", "libdl.so", "libft2.so", "libm.so"})
+	checkVndkLibrariesOutput(t, ctx, "vndkcore.libraries.txt", []string{"libvndk-private.so", "libvndk.so"})
+	checkVndkLibrariesOutput(t, ctx, "vndkprivate.libraries.txt", []string{"libft2.so", "libvndk-private.so", "libvndk_sp_private-x.so"})
+	checkVndkLibrariesOutput(t, ctx, "vndksp.libraries.txt", []string{"libc++.so", "libvndk_sp-x.so", "libvndk_sp_private-x.so"})
+	checkVndkLibrariesOutput(t, ctx, "vndkcorevariant.libraries.txt", nil)
+}
+
+func TestVndkLibrariesTxtAndroidMk(t *testing.T) {
+	bp := `
+		vndk_libraries_txt {
+			name: "llndk.libraries.txt",
+		}`
+	config := TestConfig(buildDir, android.Android, nil, bp, nil)
+	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
+	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+	ctx := testCcWithConfig(t, config)
+
+	module := ctx.ModuleForTests("llndk.libraries.txt", "")
+	entries := android.AndroidMkEntriesForTest(t, config, "", module.Module())[0]
+	assertArrayString(t, entries.EntryMap["LOCAL_MODULE_STEM"], []string{"llndk.libraries.VER.txt"})
+}
+
+func TestVndkUsingCoreVariant(t *testing.T) {
+	bp := `
+		cc_library {
+			name: "libvndk",
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+			},
+			nocrt: true,
+		}
+
+		cc_library {
+			name: "libvndk_sp",
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+				support_system_process: true,
+			},
+			nocrt: true,
+		}
+
+		cc_library {
+			name: "libvndk2",
+			vendor_available: false,
+			vndk: {
+				enabled: true,
+			},
+			nocrt: true,
+		}
+
+		vndk_libraries_txt {
+			name: "vndkcorevariant.libraries.txt",
+		}
+	`
+
+	config := TestConfig(buildDir, android.Android, nil, bp, nil)
+	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
+	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+	config.TestProductVariables.VndkUseCoreVariant = BoolPtr(true)
+
+	setVndkMustUseVendorVariantListForTest(config, []string{"libvndk"})
+
+	ctx := testCcWithConfig(t, config)
+
+	checkVndkLibrariesOutput(t, ctx, "vndkcorevariant.libraries.txt", []string{"libc++.so", "libvndk2.so", "libvndk_sp.so"})
+}
+
+func TestVndkWhenVndkVersionIsNotSet(t *testing.T) {
+	ctx := testCcNoVndk(t, `
+		cc_library {
+			name: "libvndk",
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+			},
+			nocrt: true,
+		}
+	`)
+
+	checkVndkOutput(t, ctx, "vndk/vndk.libraries.txt", []string{
+		"LLNDK: libc.so",
+		"LLNDK: libdl.so",
+		"LLNDK: libft2.so",
+		"LLNDK: libm.so",
+		"VNDK-SP: libc++.so",
+		"VNDK-core: libvndk.so",
+		"VNDK-private: libft2.so",
+	})
 }
 
 func TestVndkDepError(t *testing.T) {
@@ -767,6 +945,19 @@
 			},
 			nocrt: true,
 		}
+		cc_library {
+			name: "libvndk2",
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+			},
+			target: {
+				vendor: {
+					suffix: "-suffix",
+				},
+			},
+			nocrt: true,
+		}
 
 		cc_library {
 			name: "libvndk_ext",
@@ -777,9 +968,22 @@
 			},
 			nocrt: true,
 		}
+
+		cc_library {
+			name: "libvndk2_ext",
+			vendor: true,
+			vndk: {
+				enabled: true,
+				extends: "libvndk2",
+			},
+			nocrt: true,
+		}
 	`)
 
 	checkVndkModule(t, ctx, "libvndk_ext", "vndk", false, "libvndk")
+
+	mod := ctx.ModuleForTests("libvndk2_ext", vendorVariant).Module().(*Module)
+	assertString(t, mod.outputFile.Path().Base(), "libvndk2-suffix.so")
 }
 
 func TestVndkExtWithoutBoardVndkVersion(t *testing.T) {
@@ -1263,85 +1467,243 @@
 	`)
 }
 
+func TestEnforceProductVndkVersion(t *testing.T) {
+	bp := `
+		cc_library {
+			name: "libllndk",
+		}
+		llndk_library {
+			name: "libllndk",
+			symbol_file: "",
+		}
+		cc_library {
+			name: "libvndk",
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+			},
+			nocrt: true,
+		}
+		cc_library {
+			name: "libvndk_sp",
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+				support_system_process: true,
+			},
+			nocrt: true,
+		}
+		cc_library {
+			name: "libva",
+			vendor_available: true,
+			nocrt: true,
+		}
+		cc_library {
+			name: "libproduct_va",
+			product_specific: true,
+			vendor_available: true,
+			nocrt: true,
+		}
+		cc_library {
+			name: "libprod",
+			product_specific: true,
+			shared_libs: [
+				"libllndk",
+				"libvndk",
+				"libvndk_sp",
+				"libva",
+				"libproduct_va",
+			],
+			nocrt: true,
+		}
+		cc_library {
+			name: "libvendor",
+			vendor: true,
+			shared_libs: [
+				"libllndk",
+				"libvndk",
+				"libvndk_sp",
+				"libva",
+				"libproduct_va",
+			],
+			nocrt: true,
+		}
+	`
+
+	config := TestConfig(buildDir, android.Android, nil, bp, nil)
+	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
+	config.TestProductVariables.ProductVndkVersion = StringPtr("current")
+	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+
+	ctx := testCcWithConfig(t, config)
+
+	checkVndkModule(t, ctx, "libvndk", "vndk-VER", false, "")
+	checkVndkModule(t, ctx, "libvndk_sp", "vndk-sp-VER", true, "")
+}
+
+func TestEnforceProductVndkVersionErrors(t *testing.T) {
+	testCcErrorProductVndk(t, "dependency \".*\" of \".*\" missing variant:\n.*image:product.VER", `
+		cc_library {
+			name: "libprod",
+			product_specific: true,
+			shared_libs: [
+				"libvendor",
+			],
+			nocrt: true,
+		}
+		cc_library {
+			name: "libvendor",
+			vendor: true,
+			nocrt: true,
+		}
+	`)
+	testCcErrorProductVndk(t, "dependency \".*\" of \".*\" missing variant:\n.*image:product.VER", `
+		cc_library {
+			name: "libprod",
+			product_specific: true,
+			shared_libs: [
+				"libsystem",
+			],
+			nocrt: true,
+		}
+		cc_library {
+			name: "libsystem",
+			nocrt: true,
+		}
+	`)
+	testCcErrorProductVndk(t, "Vendor module that is not VNDK should not link to \".*\" which is marked as `vendor_available: false`", `
+		cc_library {
+			name: "libprod",
+			product_specific: true,
+			shared_libs: [
+				"libvndk_private",
+			],
+			nocrt: true,
+		}
+		cc_library {
+			name: "libvndk_private",
+			vendor_available: false,
+			vndk: {
+				enabled: true,
+			},
+			nocrt: true,
+		}
+	`)
+	testCcErrorProductVndk(t, "dependency \".*\" of \".*\" missing variant:\n.*image:product.VER", `
+		cc_library {
+			name: "libprod",
+			product_specific: true,
+			shared_libs: [
+				"libsystem_ext",
+			],
+			nocrt: true,
+		}
+		cc_library {
+			name: "libsystem_ext",
+			system_ext_specific: true,
+			nocrt: true,
+		}
+	`)
+	testCcErrorProductVndk(t, "dependency \".*\" of \".*\" missing variant:\n.*image:", `
+		cc_library {
+			name: "libsystem",
+			shared_libs: [
+				"libproduct_va",
+			],
+			nocrt: true,
+		}
+		cc_library {
+			name: "libproduct_va",
+			product_specific: true,
+			vendor_available: true,
+			nocrt: true,
+		}
+	`)
+}
+
 func TestMakeLinkType(t *testing.T) {
-	config := android.TestArchConfig(buildDir, nil)
+	bp := `
+		cc_library {
+			name: "libvndk",
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+			},
+		}
+		cc_library {
+			name: "libvndksp",
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+				support_system_process: true,
+			},
+		}
+		cc_library {
+			name: "libvndkprivate",
+			vendor_available: false,
+			vndk: {
+				enabled: true,
+			},
+		}
+		cc_library {
+			name: "libvendor",
+			vendor: true,
+		}
+		cc_library {
+			name: "libvndkext",
+			vendor: true,
+			vndk: {
+				enabled: true,
+				extends: "libvndk",
+			},
+		}
+		vndk_prebuilt_shared {
+			name: "prevndk",
+			version: "27",
+			target_arch: "arm",
+			binder32bit: true,
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+			},
+			arch: {
+				arm: {
+					srcs: ["liba.so"],
+				},
+			},
+		}
+		cc_library {
+			name: "libllndk",
+		}
+		llndk_library {
+			name: "libllndk",
+			symbol_file: "",
+		}
+		cc_library {
+			name: "libllndkprivate",
+		}
+		llndk_library {
+			name: "libllndkprivate",
+			vendor_available: false,
+			symbol_file: "",
+		}`
+
+	config := TestConfig(buildDir, android.Android, nil, bp, nil)
 	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
 	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
 	// native:vndk
-	ctx := testCcWithConfig(t, `
-	cc_library {
-		name: "libvndk",
-		vendor_available: true,
-		vndk: {
-			enabled: true,
-		},
-	}
-	cc_library {
-		name: "libvndksp",
-		vendor_available: true,
-		vndk: {
-			enabled: true,
-			support_system_process: true,
-		},
-	}
-	cc_library {
-		name: "libvndkprivate",
-		vendor_available: false,
-		vndk: {
-			enabled: true,
-		},
-	}
-	cc_library {
-		name: "libvendor",
-		vendor: true,
-	}
-	cc_library {
-		name: "libvndkext",
-		vendor: true,
-		vndk: {
-			enabled: true,
-			extends: "libvndk",
-		},
-	}
-	vndk_prebuilt_shared {
-		name: "prevndk",
-		version: "27",
-		target_arch: "arm",
-		binder32bit: true,
-		vendor_available: true,
-		vndk: {
-			enabled: true,
-		},
-		arch: {
-			arm: {
-				srcs: ["liba.so"],
-			},
-		},
-	}
-	cc_library {
-		name: "libllndk",
-	}
-	llndk_library {
-		name: "libllndk",
-		symbol_file: "",
-	}
-	cc_library {
-		name: "libllndkprivate",
-	}
-	llndk_library {
-		name: "libllndkprivate",
-		vendor_available: false,
-		symbol_file: "",
-	}`, config)
+	ctx := testCcWithConfig(t, config)
 
-	assertArrayString(t, *vndkCoreLibraries(config),
+	assertMapKeys(t, vndkCoreLibraries(config),
 		[]string{"libvndk", "libvndkprivate"})
-	assertArrayString(t, *vndkSpLibraries(config),
+	assertMapKeys(t, vndkSpLibraries(config),
 		[]string{"libc++", "libvndksp"})
-	assertArrayString(t, *llndkLibraries(config),
-		[]string{"libc", "libdl", "libllndk", "libllndkprivate", "libm"})
-	assertArrayString(t, *vndkPrivateLibraries(config),
-		[]string{"libllndkprivate", "libvndkprivate"})
+	assertMapKeys(t, llndkLibraries(config),
+		[]string{"libc", "libdl", "libft2", "libllndk", "libllndkprivate", "libm"})
+	assertMapKeys(t, vndkPrivateLibraries(config),
+		[]string{"libft2", "libllndkprivate", "libvndkprivate"})
+
+	vendorVariant27 := "android_vendor.27_arm64_armv8-a_shared"
 
 	tests := []struct {
 		variant  string
@@ -1353,8 +1715,8 @@
 		{vendorVariant, "libvndkprivate", "native:vndk_private"},
 		{vendorVariant, "libvendor", "native:vendor"},
 		{vendorVariant, "libvndkext", "native:vendor"},
-		{vendorVariant, "prevndk.vndk.27.arm.binder32", "native:vndk"},
 		{vendorVariant, "libllndk.llndk", "native:vndk"},
+		{vendorVariant27, "prevndk.vndk.27.arm.binder32", "native:vndk"},
 		{coreVariant, "libvndk", "native:platform"},
 		{coreVariant, "libvndkprivate", "native:platform"},
 		{coreVariant, "libllndk", "native:platform"},
@@ -1722,7 +2084,7 @@
 
 	`)
 
-	variant := "android_arm64_armv8-a_core_static"
+	variant := "android_arm64_armv8-a_static"
 	moduleA := ctx.ModuleForTests("a", variant).Module().(*Module)
 	actual := moduleA.depsInLinkOrder
 	expected := getOutputPaths(ctx, variant, []string{"c", "b", "d"})
@@ -1756,7 +2118,7 @@
 
 	`)
 
-	variant := "android_arm64_armv8-a_core_static"
+	variant := "android_arm64_armv8-a_static"
 	moduleA := ctx.ModuleForTests("a", variant).Module().(*Module)
 	actual := moduleA.depsInLinkOrder
 	expected := getOutputPaths(ctx, variant, []string{"c", "b"})
@@ -1792,7 +2154,7 @@
 	`)
 
 	// _static variant is used since _shared reuses *.o from the static variant
-	cc := ctx.ModuleForTests("libvendor", "android_arm_armv7-a-neon_vendor_static").Rule("cc")
+	cc := ctx.ModuleForTests("libvendor", "android_vendor.VER_arm_armv7-a-neon_static").Rule("cc")
 	cflags := cc.Args["cFlags"]
 	if !strings.Contains(cflags, "-Imy_include") {
 		t.Errorf("cflags for libvendor must contain -Imy_include, but was %#v.", cflags)
@@ -1868,7 +2230,7 @@
 	ctx := testCc(t, runtimeLibAndroidBp)
 
 	// runtime_libs for core variants use the module names without suffixes.
-	variant := "android_arm64_armv8-a_core_shared"
+	variant := "android_arm64_armv8-a_shared"
 
 	module := ctx.ModuleForTests("libvendor_available2", variant).Module().(*Module)
 	checkRuntimeLibs(t, []string{"libvendor_available1"}, module)
@@ -1878,7 +2240,7 @@
 
 	// runtime_libs for vendor variants have '.vendor' suffixes if the modules have both core
 	// and vendor variants.
-	variant = "android_arm64_armv8-a_vendor_shared"
+	variant = "android_vendor.VER_arm64_armv8-a_shared"
 
 	module = ctx.ModuleForTests("libvendor_available2", variant).Module().(*Module)
 	checkRuntimeLibs(t, []string{"libvendor_available1.vendor"}, module)
@@ -1890,11 +2252,11 @@
 func TestExcludeRuntimeLibs(t *testing.T) {
 	ctx := testCc(t, runtimeLibAndroidBp)
 
-	variant := "android_arm64_armv8-a_core_shared"
+	variant := "android_arm64_armv8-a_shared"
 	module := ctx.ModuleForTests("libvendor_available3", variant).Module().(*Module)
 	checkRuntimeLibs(t, []string{"libvendor_available1"}, module)
 
-	variant = "android_arm64_armv8-a_vendor_shared"
+	variant = "android_vendor.VER_arm64_armv8-a_shared"
 	module = ctx.ModuleForTests("libvendor_available3", variant).Module().(*Module)
 	checkRuntimeLibs(t, nil, module)
 }
@@ -1904,7 +2266,7 @@
 
 	// If DeviceVndkVersion is not defined, then runtime_libs are copied as-is.
 
-	variant := "android_arm64_armv8-a_core_shared"
+	variant := "android_arm64_armv8-a_shared"
 
 	module := ctx.ModuleForTests("libvendor_available2", variant).Module().(*Module)
 	checkRuntimeLibs(t, []string{"libvendor_available1"}, module)
@@ -1939,15 +2301,15 @@
 	ctx := testCc(t, staticLibAndroidBp)
 
 	// Check the shared version of lib2.
-	variant := "android_arm64_armv8-a_core_shared"
+	variant := "android_arm64_armv8-a_shared"
 	module := ctx.ModuleForTests("lib2", variant).Module().(*Module)
-	checkStaticLibs(t, []string{"lib1", "libclang_rt.builtins-aarch64-android", "libatomic", "libgcc_stripped"}, module)
+	checkStaticLibs(t, []string{"lib1", "libc++demangle", "libclang_rt.builtins-aarch64-android", "libatomic", "libgcc_stripped"}, module)
 
 	// Check the static version of lib2.
-	variant = "android_arm64_armv8-a_core_static"
+	variant = "android_arm64_armv8-a_static"
 	module = ctx.ModuleForTests("lib2", variant).Module().(*Module)
 	// libc++_static is linked additionally.
-	checkStaticLibs(t, []string{"lib1", "libc++_static", "libclang_rt.builtins-aarch64-android", "libatomic", "libgcc_stripped"}, module)
+	checkStaticLibs(t, []string{"lib1", "libc++_static", "libc++demangle", "libclang_rt.builtins-aarch64-android", "libatomic", "libgcc_stripped"}, module)
 }
 
 var compilerFlagsTestCases = []struct {
@@ -2072,28 +2434,29 @@
 	}
 	`)
 
-	variant := "android_arm64_armv8-a_core_shared"
+	coreVariant := "android_arm64_armv8-a_shared"
+	vendorVariant := "android_vendor.VER_arm64_armv8-a_shared"
 
 	// test if header search paths are correctly added
 	// _static variant is used since _shared reuses *.o from the static variant
-	cc := ctx.ModuleForTests("libsystem", strings.Replace(variant, "_shared", "_static", 1)).Rule("cc")
+	cc := ctx.ModuleForTests("libsystem", strings.Replace(coreVariant, "_shared", "_static", 1)).Rule("cc")
 	cflags := cc.Args["cFlags"]
 	if !strings.Contains(cflags, "-Imy_include") {
 		t.Errorf("cflags for libsystem must contain -Imy_include, but was %#v.", cflags)
 	}
 
 	// test if libsystem is linked to the stub
-	ld := ctx.ModuleForTests("libsystem", variant).Rule("ld")
+	ld := ctx.ModuleForTests("libsystem", coreVariant).Rule("ld")
 	libflags := ld.Args["libFlags"]
-	stubPaths := getOutputPaths(ctx, variant, []string{"libvendorpublic" + vendorPublicLibrarySuffix})
+	stubPaths := getOutputPaths(ctx, coreVariant, []string{"libvendorpublic" + vendorPublicLibrarySuffix})
 	if !strings.Contains(libflags, stubPaths[0].String()) {
 		t.Errorf("libflags for libsystem must contain %#v, but was %#v", stubPaths[0], libflags)
 	}
 
 	// test if libvendor is linked to the real shared lib
-	ld = ctx.ModuleForTests("libvendor", strings.Replace(variant, "_core", "_vendor", 1)).Rule("ld")
+	ld = ctx.ModuleForTests("libvendor", vendorVariant).Rule("ld")
 	libflags = ld.Args["libFlags"]
-	stubPaths = getOutputPaths(ctx, strings.Replace(variant, "_core", "_vendor", 1), []string{"libvendorpublic"})
+	stubPaths = getOutputPaths(ctx, vendorVariant, []string{"libvendorpublic"})
 	if !strings.Contains(libflags, stubPaths[0].String()) {
 		t.Errorf("libflags for libvendor must contain %#v, but was %#v", stubPaths[0], libflags)
 	}
@@ -2119,7 +2482,7 @@
 	`)
 
 	variants := ctx.ModuleVariantsForTests("librecovery")
-	const arm64 = "android_arm64_armv8-a_recovery_shared"
+	const arm64 = "android_recovery_arm64_armv8-a_shared"
 	if len(variants) != 1 || !android.InList(arm64, variants) {
 		t.Errorf("variants of librecovery must be \"%s\" only, but was %#v", arm64, variants)
 	}
@@ -2154,14 +2517,14 @@
 
 	variants := ctx.ModuleVariantsForTests("libFoo")
 	expectedVariants := []string{
-		"android_arm64_armv8-a_core_shared",
-		"android_arm64_armv8-a_core_shared_1",
-		"android_arm64_armv8-a_core_shared_2",
-		"android_arm64_armv8-a_core_shared_3",
-		"android_arm_armv7-a-neon_core_shared",
-		"android_arm_armv7-a-neon_core_shared_1",
-		"android_arm_armv7-a-neon_core_shared_2",
-		"android_arm_armv7-a-neon_core_shared_3",
+		"android_arm64_armv8-a_shared",
+		"android_arm64_armv8-a_shared_1",
+		"android_arm64_armv8-a_shared_2",
+		"android_arm64_armv8-a_shared_3",
+		"android_arm_armv7-a-neon_shared",
+		"android_arm_armv7-a-neon_shared_1",
+		"android_arm_armv7-a-neon_shared_2",
+		"android_arm_armv7-a-neon_shared_3",
 	}
 	variantsMismatch := false
 	if len(variants) != len(expectedVariants) {
@@ -2184,14 +2547,14 @@
 		}
 	}
 
-	libBarLinkRule := ctx.ModuleForTests("libBar", "android_arm64_armv8-a_core_shared").Rule("ld")
+	libBarLinkRule := ctx.ModuleForTests("libBar", "android_arm64_armv8-a_shared").Rule("ld")
 	libFlags := libBarLinkRule.Args["libFlags"]
-	libFoo1StubPath := "libFoo/android_arm64_armv8-a_core_shared_1/libFoo.so"
+	libFoo1StubPath := "libFoo/android_arm64_armv8-a_shared_1/libFoo.so"
 	if !strings.Contains(libFlags, libFoo1StubPath) {
 		t.Errorf("%q is not found in %q", libFoo1StubPath, libFlags)
 	}
 
-	libBarCompileRule := ctx.ModuleForTests("libBar", "android_arm64_armv8-a_core_shared").Rule("cc")
+	libBarCompileRule := ctx.ModuleForTests("libBar", "android_arm64_armv8-a_shared").Rule("cc")
 	cFlags := libBarCompileRule.Args["cFlags"]
 	libFoo1VersioningMacro := "-D__LIBFOO_API__=1"
 	if !strings.Contains(cFlags, libFoo1VersioningMacro) {
@@ -2203,14 +2566,14 @@
 	ctx := testCc(t, `
 		cc_binary {
 			name: "static_test",
-			srcs: ["foo.c"],
+			srcs: ["foo.c", "baz.o"],
 			static_executable: true,
 		}`)
 
-	variant := "android_arm64_armv8-a_core"
+	variant := "android_arm64_armv8-a"
 	binModuleRule := ctx.ModuleForTests("static_test", variant).Rule("ld")
 	libFlags := binModuleRule.Args["libFlags"]
-	systemStaticLibs := []string{"libc.a", "libm.a", "libdl.a"}
+	systemStaticLibs := []string{"libc.a", "libm.a"}
 	for _, lib := range systemStaticLibs {
 		if !strings.Contains(libFlags, lib) {
 			t.Errorf("Static lib %q was not found in %q", lib, libFlags)
@@ -2250,9 +2613,9 @@
 			},
 		}`)
 
-	mybin := ctx.ModuleForTests("mybin", "android_arm64_armv8-a_core").Module().(*Module)
+	mybin := ctx.ModuleForTests("mybin", "android_arm64_armv8-a").Module().(*Module)
 	actual := mybin.depsInLinkOrder
-	expected := getOutputPaths(ctx, "android_arm64_armv8-a_core_static", []string{"libB", "libC"})
+	expected := getOutputPaths(ctx, "android_arm64_armv8-a_static", []string{"libB", "libC"})
 
 	if !reflect.DeepEqual(actual, expected) {
 		t.Errorf("staticDeps orderings were not propagated correctly"+
@@ -2264,6 +2627,40 @@
 	}
 }
 
+func TestErrorsIfAModuleDependsOnDisabled(t *testing.T) {
+	testCcError(t, `module "libA" .* depends on disabled module "libB"`, `
+		cc_library {
+			name: "libA",
+			srcs: ["foo.c"],
+			shared_libs: ["libB"],
+			stl: "none",
+		}
+
+		cc_library {
+			name: "libB",
+			srcs: ["foo.c"],
+			enabled: false,
+			stl: "none",
+		}
+	`)
+}
+
+// Simple smoke test for the cc_fuzz target that ensures the rule compiles
+// correctly.
+func TestFuzzTarget(t *testing.T) {
+	ctx := testCc(t, `
+		cc_fuzz {
+			name: "fuzz_smoke_test",
+			srcs: ["foo.c"],
+		}`)
+
+	variant := "android_arm64_armv8-a_fuzzer"
+	ctx.ModuleForTests("fuzz_smoke_test", variant).Rule("cc")
+}
+
+func TestAidl(t *testing.T) {
+}
+
 func assertString(t *testing.T, got, expected string) {
 	t.Helper()
 	if got != expected {
@@ -2285,3 +2682,72 @@
 		}
 	}
 }
+
+func assertMapKeys(t *testing.T, m map[string]string, expected []string) {
+	t.Helper()
+	assertArrayString(t, android.SortedStringKeys(m), expected)
+}
+
+func TestDefaults(t *testing.T) {
+	ctx := testCc(t, `
+		cc_defaults {
+			name: "defaults",
+			srcs: ["foo.c"],
+			static: {
+				srcs: ["bar.c"],
+			},
+			shared: {
+				srcs: ["baz.c"],
+			},
+		}
+
+		cc_library_static {
+			name: "libstatic",
+			defaults: ["defaults"],
+		}
+
+		cc_library_shared {
+			name: "libshared",
+			defaults: ["defaults"],
+		}
+
+		cc_library {
+			name: "libboth",
+			defaults: ["defaults"],
+		}
+
+		cc_binary {
+			name: "binary",
+			defaults: ["defaults"],
+		}`)
+
+	pathsToBase := func(paths android.Paths) []string {
+		var ret []string
+		for _, p := range paths {
+			ret = append(ret, p.Base())
+		}
+		return ret
+	}
+
+	shared := ctx.ModuleForTests("libshared", "android_arm64_armv8-a_shared").Rule("ld")
+	if g, w := pathsToBase(shared.Inputs), []string{"foo.o", "baz.o"}; !reflect.DeepEqual(w, g) {
+		t.Errorf("libshared ld rule wanted %q, got %q", w, g)
+	}
+	bothShared := ctx.ModuleForTests("libboth", "android_arm64_armv8-a_shared").Rule("ld")
+	if g, w := pathsToBase(bothShared.Inputs), []string{"foo.o", "baz.o"}; !reflect.DeepEqual(w, g) {
+		t.Errorf("libboth ld rule wanted %q, got %q", w, g)
+	}
+	binary := ctx.ModuleForTests("binary", "android_arm64_armv8-a").Rule("ld")
+	if g, w := pathsToBase(binary.Inputs), []string{"foo.o"}; !reflect.DeepEqual(w, g) {
+		t.Errorf("binary ld rule wanted %q, got %q", w, g)
+	}
+
+	static := ctx.ModuleForTests("libstatic", "android_arm64_armv8-a_static").Rule("ar")
+	if g, w := pathsToBase(static.Inputs), []string{"foo.o", "bar.o"}; !reflect.DeepEqual(w, g) {
+		t.Errorf("libstatic ar rule wanted %q, got %q", w, g)
+	}
+	bothStatic := ctx.ModuleForTests("libboth", "android_arm64_armv8-a_static").Rule("ar")
+	if g, w := pathsToBase(bothStatic.Inputs), []string{"foo.o", "bar.o"}; !reflect.DeepEqual(w, g) {
+		t.Errorf("libboth ar rule wanted %q, got %q", w, g)
+	}
+}
diff --git a/cc/ccdeps.go b/cc/ccdeps.go
new file mode 100644
index 0000000..9b89110
--- /dev/null
+++ b/cc/ccdeps.go
@@ -0,0 +1,252 @@
+// 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 cc
+
+import (
+	"encoding/json"
+	"fmt"
+	"os"
+	"path"
+	"sort"
+	"strings"
+
+	"android/soong/android"
+)
+
+// This singleton collects cc modules' source and flags into to a json file.
+// It does so for generating CMakeLists.txt project files needed data when
+// either make, mm, mma, mmm or mmma is called.
+// The info file is generated in $OUT/module_bp_cc_depend.json.
+
+func init() {
+	android.RegisterSingletonType("ccdeps_generator", ccDepsGeneratorSingleton)
+}
+
+func ccDepsGeneratorSingleton() android.Singleton {
+	return &ccdepsGeneratorSingleton{}
+}
+
+type ccdepsGeneratorSingleton struct {
+}
+
+const (
+	// Environment variables used to control the behavior of this singleton.
+	envVariableCollectCCDeps = "SOONG_COLLECT_CC_DEPS"
+	ccdepsJsonFileName       = "module_bp_cc_deps.json"
+	cClang                   = "clang"
+	cppClang                 = "clang++"
+)
+
+type ccIdeInfo struct {
+	Path                 []string     `json:"path,omitempty"`
+	Srcs                 []string     `json:"srcs,omitempty"`
+	Global_Common_Flags  ccParameters `json:"global_common_flags,omitempty"`
+	Local_Common_Flags   ccParameters `json:"local_common_flags,omitempty"`
+	Global_C_flags       ccParameters `json:"global_c_flags,omitempty"`
+	Local_C_flags        ccParameters `json:"local_c_flags,omitempty"`
+	Global_C_only_flags  ccParameters `json:"global_c_only_flags,omitempty"`
+	Local_C_only_flags   ccParameters `json:"local_c_only_flags,omitempty"`
+	Global_Cpp_flags     ccParameters `json:"global_cpp_flags,omitempty"`
+	Local_Cpp_flags      ccParameters `json:"local_cpp_flags,omitempty"`
+	System_include_flags ccParameters `json:"system_include_flags,omitempty"`
+	Module_name          string       `json:"module_name,omitempty"`
+}
+
+type ccParameters struct {
+	HeaderSearchPath       []string          `json:"header_search_path,omitempty"`
+	SystemHeaderSearchPath []string          `json:"system_search_path,omitempty"`
+	FlagParameters         []string          `json:"flag,omitempty"`
+	SysRoot                string            `json:"system_root,omitempty"`
+	RelativeFilePathFlags  map[string]string `json:"relative_file_path,omitempty"`
+}
+
+type ccMapIdeInfos map[string]ccIdeInfo
+
+type ccDeps struct {
+	C_clang   string        `json:"clang,omitempty"`
+	Cpp_clang string        `json:"clang++,omitempty"`
+	Modules   ccMapIdeInfos `json:"modules,omitempty"`
+}
+
+func (c *ccdepsGeneratorSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+	if !ctx.Config().IsEnvTrue(envVariableCollectCCDeps) {
+		return
+	}
+
+	moduleDeps := ccDeps{}
+	moduleInfos := map[string]ccIdeInfo{}
+
+	// Track which projects have already had CMakeLists.txt generated to keep the first
+	// variant for each project.
+	seenProjects := map[string]bool{}
+
+	pathToCC, _ := evalVariable(ctx, "${config.ClangBin}/")
+	moduleDeps.C_clang = fmt.Sprintf("%s%s", buildCMakePath(pathToCC), cClang)
+	moduleDeps.Cpp_clang = fmt.Sprintf("%s%s", buildCMakePath(pathToCC), cppClang)
+
+	ctx.VisitAllModules(func(module android.Module) {
+		if ccModule, ok := module.(*Module); ok {
+			if compiledModule, ok := ccModule.compiler.(CompiledInterface); ok {
+				generateCLionProjectData(ctx, compiledModule, ccModule, seenProjects, moduleInfos)
+			}
+		}
+	})
+
+	moduleDeps.Modules = moduleInfos
+
+	ccfpath := android.PathForOutput(ctx, ccdepsJsonFileName).String()
+	err := createJsonFile(moduleDeps, ccfpath)
+	if err != nil {
+		ctx.Errorf(err.Error())
+	}
+}
+
+func parseCompilerCCParameters(ctx android.SingletonContext, params []string) ccParameters {
+	compilerParams := ccParameters{}
+
+	cparams := []string{}
+	for _, param := range params {
+		param, _ = evalVariable(ctx, param)
+		cparams = append(cparams, param)
+	}
+
+	// Soong does not guarantee that each flag will be in an individual string. e.g: The
+	// input received could be:
+	// params = {"-isystem", "path/to/system"}
+	// or it could be
+	// params = {"-isystem path/to/system"}
+	// To normalize the input, we split all strings with the "space" character and consolidate
+	// all tokens into a flattened parameters list
+	cparams = normalizeParameters(cparams)
+
+	for i := 0; i < len(cparams); i++ {
+		param := cparams[i]
+		if param == "" {
+			continue
+		}
+
+		switch categorizeParameter(param) {
+		case headerSearchPath:
+			compilerParams.HeaderSearchPath =
+				append(compilerParams.HeaderSearchPath, strings.TrimPrefix(param, "-I"))
+		case systemHeaderSearchPath:
+			if i < len(params)-1 {
+				compilerParams.SystemHeaderSearchPath = append(compilerParams.SystemHeaderSearchPath, cparams[i+1])
+			}
+			i = i + 1
+		case flag:
+			c := cleanupParameter(param)
+			compilerParams.FlagParameters = append(compilerParams.FlagParameters, c)
+		case systemRoot:
+			if i < len(cparams)-1 {
+				compilerParams.SysRoot = cparams[i+1]
+			}
+			i = i + 1
+		case relativeFilePathFlag:
+			flagComponents := strings.Split(param, "=")
+			if len(flagComponents) == 2 {
+				if compilerParams.RelativeFilePathFlags == nil {
+					compilerParams.RelativeFilePathFlags = map[string]string{}
+				}
+				compilerParams.RelativeFilePathFlags[flagComponents[0]] = flagComponents[1]
+			}
+		}
+	}
+	return compilerParams
+}
+
+func generateCLionProjectData(ctx android.SingletonContext, compiledModule CompiledInterface,
+	ccModule *Module, seenProjects map[string]bool, moduleInfos map[string]ccIdeInfo) {
+	srcs := compiledModule.Srcs()
+	if len(srcs) == 0 {
+		return
+	}
+
+	// Only keep the DeviceArch variant module.
+	if ctx.DeviceConfig().DeviceArch() != ccModule.ModuleBase.Arch().ArchType.Name {
+		return
+	}
+
+	clionProjectLocation := getCMakeListsForModule(ccModule, ctx)
+	if seenProjects[clionProjectLocation] {
+		return
+	}
+
+	seenProjects[clionProjectLocation] = true
+
+	name := ccModule.ModuleBase.Name()
+	dpInfo := moduleInfos[name]
+
+	dpInfo.Path = append(dpInfo.Path, path.Dir(ctx.BlueprintFile(ccModule)))
+	dpInfo.Srcs = append(dpInfo.Srcs, srcs.Strings()...)
+	dpInfo.Path = android.FirstUniqueStrings(dpInfo.Path)
+	dpInfo.Srcs = android.FirstUniqueStrings(dpInfo.Srcs)
+
+	dpInfo.Global_Common_Flags = parseCompilerCCParameters(ctx, ccModule.flags.Global.CommonFlags)
+	dpInfo.Local_Common_Flags = parseCompilerCCParameters(ctx, ccModule.flags.Local.CommonFlags)
+	dpInfo.Global_C_flags = parseCompilerCCParameters(ctx, ccModule.flags.Global.CFlags)
+	dpInfo.Local_C_flags = parseCompilerCCParameters(ctx, ccModule.flags.Local.CFlags)
+	dpInfo.Global_C_only_flags = parseCompilerCCParameters(ctx, ccModule.flags.Global.ConlyFlags)
+	dpInfo.Local_C_only_flags = parseCompilerCCParameters(ctx, ccModule.flags.Local.ConlyFlags)
+	dpInfo.Global_Cpp_flags = parseCompilerCCParameters(ctx, ccModule.flags.Global.CppFlags)
+	dpInfo.Local_Cpp_flags = parseCompilerCCParameters(ctx, ccModule.flags.Local.CppFlags)
+	dpInfo.System_include_flags = parseCompilerCCParameters(ctx, ccModule.flags.SystemIncludeFlags)
+
+	dpInfo.Module_name = name
+
+	moduleInfos[name] = dpInfo
+}
+
+type Deal struct {
+	Name    string
+	ideInfo ccIdeInfo
+}
+
+type Deals []Deal
+
+// Ensure it satisfies sort.Interface
+func (d Deals) Len() int           { return len(d) }
+func (d Deals) Less(i, j int) bool { return d[i].Name < d[j].Name }
+func (d Deals) Swap(i, j int)      { d[i], d[j] = d[j], d[i] }
+
+func sortMap(moduleInfos map[string]ccIdeInfo) map[string]ccIdeInfo {
+	var deals Deals
+	for k, v := range moduleInfos {
+		deals = append(deals, Deal{k, v})
+	}
+
+	sort.Sort(deals)
+
+	m := map[string]ccIdeInfo{}
+	for _, d := range deals {
+		m[d.Name] = d.ideInfo
+	}
+	return m
+}
+
+func createJsonFile(moduleDeps ccDeps, ccfpath string) error {
+	file, err := os.Create(ccfpath)
+	if err != nil {
+		return fmt.Errorf("Failed to create file: %s, relative: %v", ccdepsJsonFileName, err)
+	}
+	defer file.Close()
+	moduleDeps.Modules = sortMap(moduleDeps.Modules)
+	buf, err := json.MarshalIndent(moduleDeps, "", "\t")
+	if err != nil {
+		return fmt.Errorf("Write file failed: %s, relative: %v", ccdepsJsonFileName, err)
+	}
+	fmt.Fprintf(file, string(buf))
+	return nil
+}
diff --git a/cc/cflag_artifacts.go b/cc/cflag_artifacts.go
new file mode 100644
index 0000000..b61f2a8
--- /dev/null
+++ b/cc/cflag_artifacts.go
@@ -0,0 +1,188 @@
+package cc
+
+import (
+	"fmt"
+	"sort"
+	"strings"
+
+	"github.com/google/blueprint/proptools"
+
+	"android/soong/android"
+)
+
+func init() {
+	android.RegisterSingletonType("cflag_artifacts_text", cflagArtifactsTextFactory)
+}
+
+var (
+	TrackedCFlags = []string{
+		"-Wall",
+		"-Werror",
+		"-Wextra",
+		"-Wthread-safety",
+		"-O3",
+	}
+
+	TrackedCFlagsDir = []string{
+		"device/google/",
+		"vendor/google/",
+	}
+)
+
+const FileBP = 50
+
+// Stores output files.
+type cflagArtifactsText struct {
+	interOutputs map[string]android.WritablePaths
+	outputs      android.WritablePaths
+}
+
+// allowedDir verifies if the directory/project is part of the TrackedCFlagsDir
+// filter.
+func allowedDir(subdir string) bool {
+	subdir += "/"
+	for _, prefix := range TrackedCFlagsDir {
+		if strings.HasPrefix(subdir, prefix) {
+			return true
+		}
+	}
+	return false
+}
+
+func (s *cflagArtifactsText) genFlagFilename(flag string) string {
+	return fmt.Sprintf("module_cflags%s.txt", flag)
+}
+
+// incrementFile is used to generate an output path object with the passed in flag
+// and part number.
+// e.g. FLAG + part # -> out/soong/cflags/module_cflags-FLAG.txt.0
+func (s *cflagArtifactsText) incrementFile(ctx android.SingletonContext,
+	flag string, part int) (string, android.OutputPath) {
+
+	filename := fmt.Sprintf("%s.%d", s.genFlagFilename(flag), part)
+	filepath := android.PathForOutput(ctx, "cflags", filename)
+	s.interOutputs[flag] = append(s.interOutputs[flag], filepath)
+	return filename, filepath
+}
+
+// GenCFlagArtifactParts is used to generate the build rules which produce the
+// intermediary files for each desired C Flag artifact
+// e.g. module_cflags-FLAG.txt.0, module_cflags-FLAG.txt.1, ...
+func (s *cflagArtifactsText) GenCFlagArtifactParts(ctx android.SingletonContext,
+	flag string, using bool, modules []string, part int) int {
+
+	cleanedName := strings.Replace(flag, "=", "_", -1)
+	filename, filepath := s.incrementFile(ctx, cleanedName, part)
+	rule := android.NewRuleBuilder()
+	rule.Command().Textf("rm -f %s", filepath.String())
+
+	if using {
+		rule.Command().
+			Textf("echo '# Modules using %s'", flag).
+			FlagWithOutput(">> ", filepath)
+	} else {
+		rule.Command().
+			Textf("echo '# Modules not using %s'", flag).
+			FlagWithOutput(">> ", filepath)
+	}
+
+	length := len(modules)
+
+	if length == 0 {
+		rule.Build(pctx, ctx, filename, "gen "+filename)
+		part++
+	}
+
+	// Following loop splits the module list for each tracked C Flag into
+	// chunks of length FileBP (file breakpoint) and generates a partial artifact
+	// (intermediary file) build rule for each split.
+	moduleShards := android.ShardStrings(modules, FileBP)
+	for index, shard := range moduleShards {
+		rule.Command().
+			Textf("for m in %s; do echo $m",
+				strings.Join(proptools.ShellEscapeList(shard), " ")).
+			FlagWithOutput(">> ", filepath).
+			Text("; done")
+		rule.Build(pctx, ctx, filename, "gen "+filename)
+
+		if index+1 != len(moduleShards) {
+			filename, filepath = s.incrementFile(ctx, cleanedName, part+index+1)
+			rule = android.NewRuleBuilder()
+			rule.Command().Textf("rm -f %s", filepath.String())
+		}
+	}
+
+	return part + len(moduleShards)
+}
+
+// GenCFlagArtifacts is used to generate build rules which combine the
+// intermediary files of a specific tracked flag into a single C Flag artifact
+// for each tracked flag.
+// e.g. module_cflags-FLAG.txt.0 + module_cflags-FLAG.txt.1 = module_cflags-FLAG.txt
+func (s *cflagArtifactsText) GenCFlagArtifacts(ctx android.SingletonContext) {
+	// Scans through s.interOutputs and creates a build rule for each tracked C
+	// Flag that concatenates the associated intermediary file into a single
+	// artifact.
+	for _, flag := range TrackedCFlags {
+		// Generate build rule to combine related intermediary files into a
+		// C Flag artifact
+		rule := android.NewRuleBuilder()
+		filename := s.genFlagFilename(flag)
+		outputpath := android.PathForOutput(ctx, "cflags", filename)
+		rule.Command().
+			Text("cat").
+			Inputs(s.interOutputs[flag].Paths()).
+			FlagWithOutput("> ", outputpath)
+		rule.Build(pctx, ctx, filename, "gen "+filename)
+		s.outputs = append(s.outputs, outputpath)
+	}
+}
+
+func (s *cflagArtifactsText) GenerateBuildActions(ctx android.SingletonContext) {
+	modulesWithCFlag := make(map[string][]string)
+
+	// Scan through all modules, selecting the ones that are part of the filter,
+	// and then storing into a map which tracks whether or not tracked C flag is
+	// used or not.
+	ctx.VisitAllModules(func(module android.Module) {
+		if ccModule, ok := module.(*Module); ok {
+			if allowedDir(ctx.ModuleDir(ccModule)) {
+				cflags := ccModule.flags.Local.CFlags
+				cppflags := ccModule.flags.Local.CppFlags
+				module := fmt.Sprintf("%s:%s (%s)",
+					ctx.BlueprintFile(ccModule),
+					ctx.ModuleName(ccModule),
+					ctx.ModuleSubDir(ccModule))
+				for _, flag := range TrackedCFlags {
+					if inList(flag, cflags) || inList(flag, cppflags) {
+						modulesWithCFlag[flag] = append(modulesWithCFlag[flag], module)
+					} else {
+						modulesWithCFlag["!"+flag] = append(modulesWithCFlag["!"+flag], module)
+					}
+				}
+			}
+		}
+	})
+
+	// Traversing map and setting up rules to produce intermediary files which
+	// contain parts of each expected C Flag artifact.
+	for _, flag := range TrackedCFlags {
+		sort.Strings(modulesWithCFlag[flag])
+		part := s.GenCFlagArtifactParts(ctx, flag, true, modulesWithCFlag[flag], 0)
+		sort.Strings(modulesWithCFlag["!"+flag])
+		s.GenCFlagArtifactParts(ctx, flag, false, modulesWithCFlag["!"+flag], part)
+	}
+
+	// Combine intermediary files into a single C Flag artifact.
+	s.GenCFlagArtifacts(ctx)
+}
+
+func cflagArtifactsTextFactory() android.Singleton {
+	return &cflagArtifactsText{
+		interOutputs: make(map[string]android.WritablePaths),
+	}
+}
+
+func (s *cflagArtifactsText) MakeVars(ctx android.MakeVarsContext) {
+	ctx.Strict("SOONG_MODULES_CFLAG_ARTIFACTS", strings.Join(s.outputs.Strings(), " "))
+}
diff --git a/cc/check.go b/cc/check.go
index 4e9e160..46328e9 100644
--- a/cc/check.go
+++ b/cc/check.go
@@ -38,6 +38,11 @@
 			ctx.PropertyErrorf(prop, "Illegal flag `%s`", flag)
 		} else if flag == "--coverage" {
 			ctx.PropertyErrorf(prop, "Bad flag: `%s`, use native_coverage instead", flag)
+		} else if flag == "-Weverything" {
+			if !ctx.Config().IsEnvTrue("ANDROID_TEMPORARILY_ALLOW_WEVERYTHING") {
+				ctx.PropertyErrorf(prop, "-Weverything is not allowed in Android.bp files.  "+
+					"Build with `m ANDROID_TEMPORARILY_ALLOW_WEVERYTHING=true` to experiment locally with -Weverything.")
+			}
 		} else if strings.Contains(flag, " ") {
 			args := strings.Split(flag, " ")
 			if args[0] == "-include" {
diff --git a/cc/cmakelists.go b/cc/cmakelists.go
index 7b4f89b..f7d9081 100644
--- a/cc/cmakelists.go
+++ b/cc/cmakelists.go
@@ -76,7 +76,7 @@
 	// Link all handmade CMakeLists.txt aggregate from
 	//     BASE/development/ide/clion to
 	// BASE/out/development/ide/clion.
-	dir := filepath.Join(getAndroidSrcRootDirectory(ctx), cLionAggregateProjectsDirectory)
+	dir := filepath.Join(android.AbsSrcDirForExistingUseCases(), cLionAggregateProjectsDirectory)
 	filepath.Walk(dir, linkAggregateCMakeListsFiles)
 
 	return
@@ -147,7 +147,7 @@
 	f.WriteString("# Tools > CMake > Change Project Root  \n\n")
 	f.WriteString(fmt.Sprintf("cmake_minimum_required(VERSION %s)\n", minimumCMakeVersionSupported))
 	f.WriteString(fmt.Sprintf("project(%s)\n", ccModule.ModuleBase.Name()))
-	f.WriteString(fmt.Sprintf("set(ANDROID_ROOT %s)\n\n", getAndroidSrcRootDirectory(ctx)))
+	f.WriteString(fmt.Sprintf("set(ANDROID_ROOT %s)\n\n", android.AbsSrcDirForExistingUseCases()))
 
 	pathToCC, _ := evalVariable(ctx, "${config.ClangBin}/")
 	f.WriteString(fmt.Sprintf("set(CMAKE_C_COMPILER \"%s%s\")\n", buildCMakePath(pathToCC), "clang"))
@@ -162,25 +162,41 @@
 	f.WriteString(")\n")
 
 	// Add all header search path and compiler parameters (-D, -W, -f, -XXXX)
-	f.WriteString("\n# GLOBAL FLAGS:\n")
-	globalParameters := parseCompilerParameters(ccModule.flags.GlobalFlags, ctx, f)
-	translateToCMake(globalParameters, f, true, true)
+	f.WriteString("\n# GLOBAL ALL FLAGS:\n")
+	globalAllParameters := parseCompilerParameters(ccModule.flags.Global.CommonFlags, ctx, f)
+	translateToCMake(globalAllParameters, f, true, true)
 
-	f.WriteString("\n# CFLAGS:\n")
-	cParameters := parseCompilerParameters(ccModule.flags.CFlags, ctx, f)
-	translateToCMake(cParameters, f, true, true)
+	f.WriteString("\n# LOCAL ALL FLAGS:\n")
+	localAllParameters := parseCompilerParameters(ccModule.flags.Local.CommonFlags, ctx, f)
+	translateToCMake(localAllParameters, f, true, true)
 
-	f.WriteString("\n# C ONLY FLAGS:\n")
-	cOnlyParameters := parseCompilerParameters(ccModule.flags.ConlyFlags, ctx, f)
-	translateToCMake(cOnlyParameters, f, true, false)
+	f.WriteString("\n# GLOBAL CFLAGS:\n")
+	globalCParameters := parseCompilerParameters(ccModule.flags.Global.CFlags, ctx, f)
+	translateToCMake(globalCParameters, f, true, true)
 
-	f.WriteString("\n# CPP FLAGS:\n")
-	cppParameters := parseCompilerParameters(ccModule.flags.CppFlags, ctx, f)
-	translateToCMake(cppParameters, f, false, true)
+	f.WriteString("\n# LOCAL CFLAGS:\n")
+	localCParameters := parseCompilerParameters(ccModule.flags.Local.CFlags, ctx, f)
+	translateToCMake(localCParameters, f, true, true)
 
-	f.WriteString("\n# SYSTEM INCLUDE FLAGS:\n")
-	includeParameters := parseCompilerParameters(ccModule.flags.SystemIncludeFlags, ctx, f)
-	translateToCMake(includeParameters, f, true, true)
+	f.WriteString("\n# GLOBAL C ONLY FLAGS:\n")
+	globalConlyParameters := parseCompilerParameters(ccModule.flags.Global.ConlyFlags, ctx, f)
+	translateToCMake(globalConlyParameters, f, true, false)
+
+	f.WriteString("\n# LOCAL C ONLY FLAGS:\n")
+	localConlyParameters := parseCompilerParameters(ccModule.flags.Local.ConlyFlags, ctx, f)
+	translateToCMake(localConlyParameters, f, true, false)
+
+	f.WriteString("\n# GLOBAL CPP FLAGS:\n")
+	globalCppParameters := parseCompilerParameters(ccModule.flags.Global.CppFlags, ctx, f)
+	translateToCMake(globalCppParameters, f, false, true)
+
+	f.WriteString("\n# LOCAL CPP FLAGS:\n")
+	localCppParameters := parseCompilerParameters(ccModule.flags.Local.CppFlags, ctx, f)
+	translateToCMake(localCppParameters, f, false, true)
+
+	f.WriteString("\n# GLOBAL SYSTEM INCLUDE FLAGS:\n")
+	globalIncludeParameters := parseCompilerParameters(ccModule.flags.SystemIncludeFlags, ctx, f)
+	translateToCMake(globalIncludeParameters, f, true, true)
 
 	// Add project executable.
 	f.WriteString(fmt.Sprintf("\nadd_executable(%s ${SOURCE_FILES})\n",
@@ -306,6 +322,20 @@
 	return flag
 }
 
+// Flattens a list of strings potentially containing space characters into a list of string containing no
+// spaces.
+func normalizeParameters(params []string) []string {
+	var flatParams []string
+	for _, s := range params {
+		s = strings.Trim(s, " ")
+		if len(s) == 0 {
+			continue
+		}
+		flatParams = append(flatParams, strings.Split(s, " ")...)
+	}
+	return flatParams
+}
+
 func parseCompilerParameters(params []string, ctx android.SingletonContext, f *os.File) compilerParameters {
 	var compilerParameters = makeCompilerParameters()
 
@@ -313,6 +343,15 @@
 		f.WriteString(fmt.Sprintf("# Raw param [%d] = '%s'\n", i, str))
 	}
 
+	// Soong does not guarantee that each flag will be in an individual string. e.g: The
+	// input received could be:
+	// params = {"-isystem", "path/to/system"}
+	// or it could be
+	// params = {"-isystem path/to/system"}
+	// To normalize the input, we split all strings with the "space" character and consolidate
+	// all tokens into a flattened parameters list
+	params = normalizeParameters(params)
+
 	for i := 0; i < len(params); i++ {
 		param := params[i]
 		if param == "" {
@@ -426,7 +465,7 @@
 }
 
 func getCMakeListsForModule(module *Module, ctx android.SingletonContext) string {
-	return filepath.Join(getAndroidSrcRootDirectory(ctx),
+	return filepath.Join(android.AbsSrcDirForExistingUseCases(),
 		cLionOutputProjectsDirectory,
 		path.Dir(ctx.BlueprintFile(module)),
 		module.ModuleBase.Name()+"-"+
@@ -434,8 +473,3 @@
 			module.ModuleBase.Os().Name,
 		cMakeListsFilename)
 }
-
-func getAndroidSrcRootDirectory(ctx android.SingletonContext) string {
-	srcPath, _ := filepath.Abs(android.PathForSource(ctx).String())
-	return srcPath
-}
diff --git a/cc/compdb.go b/cc/compdb.go
index 1102651..ea12443 100644
--- a/cc/compdb.go
+++ b/cc/compdb.go
@@ -27,7 +27,7 @@
 // This singleton generates a compile_commands.json file. It does so for each
 // blueprint Android.bp resulting in a cc.Module when either make, mm, mma, mmm
 // or mmma is called. It will only create a single compile_commands.json file
-// at out/development/ide/compdb/compile_commands.json. It will also symlink it
+// at ${OUT_DIR}/soong/development/ide/compdb/compile_commands.json. It will also symlink it
 // to ${SOONG_LINK_COMPDB_TO} if set. In general this should be created by running
 // make SOONG_GEN_COMPDB=1 nothing to get all targets.
 
@@ -43,7 +43,7 @@
 
 const (
 	compdbFilename                = "compile_commands.json"
-	compdbOutputProjectsDirectory = "out/development/ide/compdb"
+	compdbOutputProjectsDirectory = "development/ide/compdb"
 
 	// Environment variables used to modify behavior of this singleton.
 	envVariableGenerateCompdb          = "SOONG_GEN_COMPDB"
@@ -78,12 +78,12 @@
 	})
 
 	// Create the output file.
-	dir := filepath.Join(getCompdbAndroidSrcRootDirectory(ctx), compdbOutputProjectsDirectory)
-	os.MkdirAll(dir, 0777)
-	compDBFile := filepath.Join(dir, compdbFilename)
-	f, err := os.Create(compdbFilename)
+	dir := android.PathForOutput(ctx, compdbOutputProjectsDirectory)
+	os.MkdirAll(filepath.Join(android.AbsSrcDirForExistingUseCases(), dir.String()), 0777)
+	compDBFile := dir.Join(ctx, compdbFilename)
+	f, err := os.Create(filepath.Join(android.AbsSrcDirForExistingUseCases(), compDBFile.String()))
 	if err != nil {
-		log.Fatalf("Could not create file %s: %s", filepath.Join(dir, compdbFilename), err)
+		log.Fatalf("Could not create file %s: %s", compDBFile, err)
 	}
 	defer f.Close()
 
@@ -103,10 +103,10 @@
 	}
 	f.Write(dat)
 
-	finalLinkPath := filepath.Join(ctx.Config().Getenv(envVariableCompdbLink), compdbFilename)
-	if finalLinkPath != "" {
+	if finalLinkDir := ctx.Config().Getenv(envVariableCompdbLink); finalLinkDir != "" {
+		finalLinkPath := filepath.Join(finalLinkDir, compdbFilename)
 		os.Remove(finalLinkPath)
-		if err := os.Symlink(compDBFile, finalLinkPath); err != nil {
+		if err := os.Symlink(compDBFile.String(), finalLinkPath); err != nil {
 			log.Fatalf("Unable to symlink %s to %s: %s", compDBFile, finalLinkPath, err)
 		}
 	}
@@ -141,7 +141,7 @@
 		isAsm = false
 		isCpp = false
 		clangPath = ccPath
-	case ".cpp", ".cc", ".mm":
+	case ".cpp", ".cc", ".cxx", ".mm":
 		isAsm = false
 		isCpp = true
 		clangPath = cxxPath
@@ -152,12 +152,16 @@
 		clangPath = ccPath
 	}
 	args = append(args, clangPath)
-	args = append(args, expandAllVars(ctx, ccModule.flags.GlobalFlags)...)
-	args = append(args, expandAllVars(ctx, ccModule.flags.CFlags)...)
+	args = append(args, expandAllVars(ctx, ccModule.flags.Global.CommonFlags)...)
+	args = append(args, expandAllVars(ctx, ccModule.flags.Local.CommonFlags)...)
+	args = append(args, expandAllVars(ctx, ccModule.flags.Global.CFlags)...)
+	args = append(args, expandAllVars(ctx, ccModule.flags.Local.CFlags)...)
 	if isCpp {
-		args = append(args, expandAllVars(ctx, ccModule.flags.CppFlags)...)
+		args = append(args, expandAllVars(ctx, ccModule.flags.Global.CppFlags)...)
+		args = append(args, expandAllVars(ctx, ccModule.flags.Local.CppFlags)...)
 	} else if !isAsm {
-		args = append(args, expandAllVars(ctx, ccModule.flags.ConlyFlags)...)
+		args = append(args, expandAllVars(ctx, ccModule.flags.Global.ConlyFlags)...)
+		args = append(args, expandAllVars(ctx, ccModule.flags.Local.ConlyFlags)...)
 	}
 	args = append(args, expandAllVars(ctx, ccModule.flags.SystemIncludeFlags)...)
 	args = append(args, src.String())
@@ -170,18 +174,17 @@
 		return
 	}
 
-	rootDir := getCompdbAndroidSrcRootDirectory(ctx)
-	pathToCC, err := ctx.Eval(pctx, rootDir+"/${config.ClangBin}/")
+	pathToCC, err := ctx.Eval(pctx, "${config.ClangBin}")
 	ccPath := "/bin/false"
 	cxxPath := "/bin/false"
 	if err == nil {
-		ccPath = pathToCC + "clang"
-		cxxPath = pathToCC + "clang++"
+		ccPath = filepath.Join(pathToCC, "clang")
+		cxxPath = filepath.Join(pathToCC, "clang++")
 	}
 	for _, src := range srcs {
 		if _, ok := builds[src.String()]; !ok {
 			builds[src.String()] = compDbEntry{
-				Directory: rootDir,
+				Directory: android.AbsSrcDirForExistingUseCases(),
 				Arguments: getArguments(src, ctx, ccModule, ccPath, cxxPath),
 				File:      src.String(),
 			}
@@ -196,8 +199,3 @@
 	}
 	return []string{""}, err
 }
-
-func getCompdbAndroidSrcRootDirectory(ctx android.SingletonContext) string {
-	srcPath, _ := filepath.Abs(android.PathForSource(ctx).String())
-	return srcPath
-}
diff --git a/cc/compiler.go b/cc/compiler.go
index fd6184b..1ced451 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -17,6 +17,8 @@
 import (
 	"fmt"
 	"path/filepath"
+	"regexp"
+	"strconv"
 	"strings"
 
 	"github.com/google/blueprint/proptools"
@@ -25,6 +27,10 @@
 	"android/soong/cc/config"
 )
 
+var (
+	allowedManualInterfacePaths = []string{"vendor/", "hardware/"}
+)
+
 // This file contains the basic C/C++/assembly to .o compliation steps
 
 type BaseCompilerProperties struct {
@@ -251,6 +257,7 @@
 // per-target values, module type values, and per-module Blueprints properties
 func (compiler *baseCompiler) compilerFlags(ctx ModuleContext, flags Flags, deps PathDeps) Flags {
 	tc := ctx.toolchain()
+	modulePath := android.PathForModuleSrc(ctx).String()
 
 	compiler.srcsBeforeGen = android.PathsForModuleSrcExcludes(ctx, compiler.Properties.Srcs, compiler.Properties.Exclude_srcs)
 	compiler.srcsBeforeGen = append(compiler.srcsBeforeGen, deps.GeneratedSources...)
@@ -264,11 +271,11 @@
 
 	esc := proptools.NinjaAndShellEscapeList
 
-	flags.CFlags = append(flags.CFlags, esc(compiler.Properties.Cflags)...)
-	flags.CppFlags = append(flags.CppFlags, esc(compiler.Properties.Cppflags)...)
-	flags.ConlyFlags = append(flags.ConlyFlags, esc(compiler.Properties.Conlyflags)...)
-	flags.AsFlags = append(flags.AsFlags, esc(compiler.Properties.Asflags)...)
-	flags.YasmFlags = append(flags.YasmFlags, esc(compiler.Properties.Asflags)...)
+	flags.Local.CFlags = append(flags.Local.CFlags, esc(compiler.Properties.Cflags)...)
+	flags.Local.CppFlags = append(flags.Local.CppFlags, esc(compiler.Properties.Cppflags)...)
+	flags.Local.ConlyFlags = append(flags.Local.ConlyFlags, esc(compiler.Properties.Conlyflags)...)
+	flags.Local.AsFlags = append(flags.Local.AsFlags, esc(compiler.Properties.Asflags)...)
+	flags.Local.YasmFlags = append(flags.Local.YasmFlags, esc(compiler.Properties.Asflags)...)
 
 	flags.Yacc = compiler.Properties.Yacc
 
@@ -276,20 +283,20 @@
 	localIncludeDirs := android.PathsForModuleSrc(ctx, compiler.Properties.Local_include_dirs)
 	if len(localIncludeDirs) > 0 {
 		f := includeDirsToFlags(localIncludeDirs)
-		flags.GlobalFlags = append(flags.GlobalFlags, f)
-		flags.YasmFlags = append(flags.YasmFlags, f)
+		flags.Local.CommonFlags = append(flags.Local.CommonFlags, f)
+		flags.Local.YasmFlags = append(flags.Local.YasmFlags, f)
 	}
 	rootIncludeDirs := android.PathsForSource(ctx, compiler.Properties.Include_dirs)
 	if len(rootIncludeDirs) > 0 {
 		f := includeDirsToFlags(rootIncludeDirs)
-		flags.GlobalFlags = append(flags.GlobalFlags, f)
-		flags.YasmFlags = append(flags.YasmFlags, f)
+		flags.Local.CommonFlags = append(flags.Local.CommonFlags, f)
+		flags.Local.YasmFlags = append(flags.Local.YasmFlags, f)
 	}
 
 	if compiler.Properties.Include_build_directory == nil ||
 		*compiler.Properties.Include_build_directory {
-		flags.GlobalFlags = append(flags.GlobalFlags, "-I"+android.PathForModuleSrc(ctx).String())
-		flags.YasmFlags = append(flags.YasmFlags, "-I"+android.PathForModuleSrc(ctx).String())
+		flags.Local.CommonFlags = append(flags.Local.CommonFlags, "-I"+modulePath)
+		flags.Local.YasmFlags = append(flags.Local.YasmFlags, "-I"+modulePath)
 	}
 
 	if !(ctx.useSdk() || ctx.useVndk()) || ctx.Host() {
@@ -308,35 +315,20 @@
 		flags.SystemIncludeFlags = append(flags.SystemIncludeFlags,
 			"-isystem "+getCurrentIncludePath(ctx).String(),
 			"-isystem "+getCurrentIncludePath(ctx).Join(ctx, config.NDKTriple(tc)).String())
-
-		// TODO: Migrate to API suffixed triple?
-		// Traditionally this has come from android/api-level.h, but with the
-		// libc headers unified it must be set by the build system since we
-		// don't have per-API level copies of that header now.
-		version := ctx.sdkVersion()
-		if version == "current" {
-			version = "__ANDROID_API_FUTURE__"
-		}
-		flags.GlobalFlags = append(flags.GlobalFlags,
-			"-D__ANDROID_API__="+version)
 	}
 
 	if ctx.useVndk() {
-		// sdkVersion() returns VNDK version for vendor modules.
-		version := ctx.sdkVersion()
-		if version == "current" {
-			version = "__ANDROID_API_FUTURE__"
-		}
-		flags.GlobalFlags = append(flags.GlobalFlags,
-			"-D__ANDROID_API__="+version, "-D__ANDROID_VNDK__")
+		flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_VNDK__")
 	}
 
 	if ctx.inRecovery() {
-		flags.GlobalFlags = append(flags.GlobalFlags, "-D__ANDROID_RECOVERY__")
+		flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_RECOVERY__")
 	}
 
 	if ctx.apexName() != "" {
-		flags.GlobalFlags = append(flags.GlobalFlags, "-D__ANDROID_APEX__="+ctx.apexName())
+		flags.Global.CommonFlags = append(flags.Global.CommonFlags,
+			"-D__ANDROID_APEX__",
+			"-D__ANDROID_APEX_"+makeDefineString(ctx.apexName())+"__")
 	}
 
 	instructionSet := String(compiler.Properties.Instruction_set)
@@ -351,60 +343,69 @@
 	CheckBadCompilerFlags(ctx, "release.cflags", compiler.Properties.Release.Cflags)
 
 	// TODO: debug
-	flags.CFlags = append(flags.CFlags, esc(compiler.Properties.Release.Cflags)...)
+	flags.Local.CFlags = append(flags.Local.CFlags, esc(compiler.Properties.Release.Cflags)...)
 
 	CheckBadCompilerFlags(ctx, "clang_cflags", compiler.Properties.Clang_cflags)
 	CheckBadCompilerFlags(ctx, "clang_asflags", compiler.Properties.Clang_asflags)
 
-	flags.CFlags = config.ClangFilterUnknownCflags(flags.CFlags)
-	flags.CFlags = append(flags.CFlags, esc(compiler.Properties.Clang_cflags)...)
-	flags.AsFlags = append(flags.AsFlags, esc(compiler.Properties.Clang_asflags)...)
-	flags.CppFlags = config.ClangFilterUnknownCflags(flags.CppFlags)
-	flags.ConlyFlags = config.ClangFilterUnknownCflags(flags.ConlyFlags)
-	flags.LdFlags = config.ClangFilterUnknownCflags(flags.LdFlags)
+	flags.Local.CFlags = config.ClangFilterUnknownCflags(flags.Local.CFlags)
+	flags.Local.CFlags = append(flags.Local.CFlags, esc(compiler.Properties.Clang_cflags)...)
+	flags.Local.AsFlags = append(flags.Local.AsFlags, esc(compiler.Properties.Clang_asflags)...)
+	flags.Local.CppFlags = config.ClangFilterUnknownCflags(flags.Local.CppFlags)
+	flags.Local.ConlyFlags = config.ClangFilterUnknownCflags(flags.Local.ConlyFlags)
+	flags.Local.LdFlags = config.ClangFilterUnknownCflags(flags.Local.LdFlags)
 
 	target := "-target " + tc.ClangTriple()
+	if ctx.Os().Class == android.Device {
+		version := ctx.sdkVersion()
+		if version == "" || version == "current" {
+			target += strconv.Itoa(android.FutureApiLevel)
+		} else {
+			target += version
+		}
+	}
+
 	gccPrefix := "-B" + config.ToolPath(tc)
 
-	flags.CFlags = append(flags.CFlags, target, gccPrefix)
-	flags.AsFlags = append(flags.AsFlags, target, gccPrefix)
-	flags.LdFlags = append(flags.LdFlags, target, gccPrefix)
+	flags.Global.CFlags = append(flags.Global.CFlags, target, gccPrefix)
+	flags.Global.AsFlags = append(flags.Global.AsFlags, target, gccPrefix)
+	flags.Global.LdFlags = append(flags.Global.LdFlags, target, gccPrefix)
 
 	hod := "Host"
 	if ctx.Os().Class == android.Device {
 		hod = "Device"
 	}
 
-	flags.GlobalFlags = append(flags.GlobalFlags, instructionSetFlags)
-	flags.ConlyFlags = append([]string{"${config.CommonGlobalConlyflags}"}, flags.ConlyFlags...)
-	flags.CppFlags = append([]string{fmt.Sprintf("${config.%sGlobalCppflags}", hod)}, flags.CppFlags...)
+	flags.Global.CommonFlags = append(flags.Global.CommonFlags, instructionSetFlags)
+	flags.Global.ConlyFlags = append([]string{"${config.CommonGlobalConlyflags}"}, flags.Global.ConlyFlags...)
+	flags.Global.CppFlags = append([]string{fmt.Sprintf("${config.%sGlobalCppflags}", hod)}, flags.Global.CppFlags...)
 
-	flags.AsFlags = append(flags.AsFlags, tc.ClangAsflags())
-	flags.CppFlags = append([]string{"${config.CommonClangGlobalCppflags}"}, flags.CppFlags...)
-	flags.GlobalFlags = append(flags.GlobalFlags,
+	flags.Global.AsFlags = append(flags.Global.AsFlags, tc.ClangAsflags())
+	flags.Global.CppFlags = append([]string{"${config.CommonClangGlobalCppflags}"}, flags.Global.CppFlags...)
+	flags.Global.CommonFlags = append(flags.Global.CommonFlags,
 		tc.ClangCflags(),
 		"${config.CommonClangGlobalCflags}",
 		fmt.Sprintf("${config.%sClangGlobalCflags}", hod))
 
-	if strings.HasPrefix(android.PathForModuleSrc(ctx).String(), "external/") {
-		flags.GlobalFlags = append([]string{"${config.ClangExternalCflags}"}, flags.GlobalFlags...)
+	if isThirdParty(modulePath) {
+		flags.Global.CommonFlags = append([]string{"${config.ClangExternalCflags}"}, flags.Global.CommonFlags...)
 	}
 
 	if tc.Bionic() {
 		if Bool(compiler.Properties.Rtti) {
-			flags.CppFlags = append(flags.CppFlags, "-frtti")
+			flags.Local.CppFlags = append(flags.Local.CppFlags, "-frtti")
 		} else {
-			flags.CppFlags = append(flags.CppFlags, "-fno-rtti")
+			flags.Local.CppFlags = append(flags.Local.CppFlags, "-fno-rtti")
 		}
 	}
 
-	flags.AsFlags = append(flags.AsFlags, "-D__ASSEMBLY__")
+	flags.Global.AsFlags = append(flags.Global.AsFlags, "-D__ASSEMBLY__")
 
-	flags.CppFlags = append(flags.CppFlags, tc.ClangCppflags())
+	flags.Global.CppFlags = append(flags.Global.CppFlags, tc.ClangCppflags())
 
-	flags.YasmFlags = append(flags.YasmFlags, tc.YasmFlags())
+	flags.Global.YasmFlags = append(flags.Global.YasmFlags, tc.YasmFlags())
 
-	flags.GlobalFlags = append(flags.GlobalFlags, tc.ToolchainClangCflags())
+	flags.Global.CommonFlags = append(flags.Global.CommonFlags, tc.ToolchainClangCflags())
 
 	cStd := config.CStdVersion
 	if String(compiler.Properties.C_std) == "experimental" {
@@ -426,15 +427,15 @@
 		cppStd = gnuToCReplacer.Replace(cppStd)
 	}
 
-	flags.ConlyFlags = append([]string{"-std=" + cStd}, flags.ConlyFlags...)
-	flags.CppFlags = append([]string{"-std=" + cppStd}, flags.CppFlags...)
+	flags.Local.ConlyFlags = append([]string{"-std=" + cStd}, flags.Local.ConlyFlags...)
+	flags.Local.CppFlags = append([]string{"-std=" + cppStd}, flags.Local.CppFlags...)
 
 	if ctx.useVndk() {
-		flags.CFlags = append(flags.CFlags, esc(compiler.Properties.Target.Vendor.Cflags)...)
+		flags.Local.CFlags = append(flags.Local.CFlags, esc(compiler.Properties.Target.Vendor.Cflags)...)
 	}
 
 	if ctx.inRecovery() {
-		flags.CFlags = append(flags.CFlags, esc(compiler.Properties.Target.Recovery.Cflags)...)
+		flags.Local.CFlags = append(flags.Local.CFlags, esc(compiler.Properties.Target.Recovery.Cflags)...)
 	}
 
 	// We can enforce some rules more strictly in the code we own. strict
@@ -443,14 +444,14 @@
 	// vendor/device specific things), we could extend this to be a ternary
 	// value.
 	strict := true
-	if strings.HasPrefix(android.PathForModuleSrc(ctx).String(), "external/") {
+	if strings.HasPrefix(modulePath, "external/") {
 		strict = false
 	}
 
 	// Can be used to make some annotations stricter for code we can fix
 	// (such as when we mark functions as deprecated).
 	if strict {
-		flags.CFlags = append(flags.CFlags, "-DANDROID_STRICT")
+		flags.Global.CFlags = append(flags.Global.CFlags, "-DANDROID_STRICT")
 	}
 
 	if compiler.hasSrcExt(".proto") {
@@ -458,12 +459,12 @@
 	}
 
 	if compiler.hasSrcExt(".y") || compiler.hasSrcExt(".yy") {
-		flags.GlobalFlags = append(flags.GlobalFlags,
+		flags.Local.CommonFlags = append(flags.Local.CommonFlags,
 			"-I"+android.PathForModuleGen(ctx, "yacc", ctx.ModuleDir()).String())
 	}
 
 	if compiler.hasSrcExt(".mc") {
-		flags.GlobalFlags = append(flags.GlobalFlags,
+		flags.Local.CommonFlags = append(flags.Local.CommonFlags,
 			"-I"+android.PathForModuleGen(ctx, "windmc", ctx.ModuleDir()).String())
 	}
 
@@ -481,35 +482,41 @@
 			flags.aidlFlags = append(flags.aidlFlags, "-t")
 		}
 
-		flags.GlobalFlags = append(flags.GlobalFlags,
+		flags.Local.CommonFlags = append(flags.Local.CommonFlags,
 			"-I"+android.PathForModuleGen(ctx, "aidl").String())
 	}
 
-	if compiler.hasSrcExt(".rs") || compiler.hasSrcExt(".fs") {
+	if compiler.hasSrcExt(".rscript") || compiler.hasSrcExt(".fs") {
 		flags = rsFlags(ctx, flags, &compiler.Properties)
 	}
 
 	if compiler.hasSrcExt(".sysprop") {
-		flags.GlobalFlags = append(flags.GlobalFlags,
+		flags.Local.CommonFlags = append(flags.Local.CommonFlags,
 			"-I"+android.PathForModuleGen(ctx, "sysprop", "include").String())
 	}
 
 	if len(compiler.Properties.Srcs) > 0 {
 		module := ctx.ModuleDir() + "/Android.bp:" + ctx.ModuleName()
-		if inList("-Wno-error", flags.CFlags) || inList("-Wno-error", flags.CppFlags) {
+		if inList("-Wno-error", flags.Local.CFlags) || inList("-Wno-error", flags.Local.CppFlags) {
 			addToModuleList(ctx, modulesUsingWnoErrorKey, module)
-		} else if !inList("-Werror", flags.CFlags) && !inList("-Werror", flags.CppFlags) {
+		} else if !inList("-Werror", flags.Local.CFlags) && !inList("-Werror", flags.Local.CppFlags) {
 			if warningsAreAllowed(ctx.ModuleDir()) {
 				addToModuleList(ctx, modulesAddedWallKey, module)
-				flags.CFlags = append([]string{"-Wall"}, flags.CFlags...)
+				flags.Local.CFlags = append([]string{"-Wall"}, flags.Local.CFlags...)
 			} else {
-				flags.CFlags = append([]string{"-Wall", "-Werror"}, flags.CFlags...)
+				flags.Local.CFlags = append([]string{"-Wall", "-Werror"}, flags.Local.CFlags...)
 			}
 		}
 	}
 
 	if Bool(compiler.Properties.Openmp) {
-		flags.CFlags = append(flags.CFlags, "-fopenmp")
+		flags.Local.CFlags = append(flags.Local.CFlags, "-fopenmp")
+	}
+
+	// Exclude directories from manual binder interface whitelisting.
+	//TODO(b/145621474): Move this check into IInterface.h when clang-tidy no longer uses absolute paths.
+	if android.PrefixInList(ctx.ModuleDir(), allowedManualInterfacePaths) {
+		flags.Local.CFlags = append(flags.Local.CFlags, "-DDO_NOT_CHECK_MANUAL_BINDER_INTERFACES")
 	}
 
 	return flags
@@ -535,6 +542,12 @@
 	return false
 }
 
+// makeDefineString transforms a name of an APEX module into a value to be used as value for C define
+// For example, com.android.foo => COM_ANDROID_FOO
+func makeDefineString(name string) string {
+	return strings.ReplaceAll(strings.ToUpper(name), ".", "_")
+}
+
 var gnuToCReplacer = strings.NewReplacer("gnu", "c")
 
 func ndkPathDeps(ctx ModuleContext) android.Paths {
@@ -547,7 +560,7 @@
 }
 
 func (compiler *baseCompiler) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects {
-	pathDeps := deps.GeneratedHeaders
+	pathDeps := deps.GeneratedDeps
 	pathDeps = append(pathDeps, ndkPathDeps(ctx)...)
 
 	buildFlags := flagsToBuilderFlags(flags)
@@ -579,3 +592,28 @@
 
 	return TransformSourceToObj(ctx, subdir, srcFiles, flags, pathDeps, cFlagsDeps)
 }
+
+var thirdPartyDirPrefixExceptions = []*regexp.Regexp{
+	regexp.MustCompile("^vendor/[^/]*google[^/]*/"),
+	regexp.MustCompile("^hardware/google/"),
+	regexp.MustCompile("^hardware/interfaces/"),
+	regexp.MustCompile("^hardware/libhardware[^/]*/"),
+	regexp.MustCompile("^hardware/ril/"),
+}
+
+func isThirdParty(path string) bool {
+	thirdPartyDirPrefixes := []string{"external/", "vendor/", "hardware/"}
+
+	for _, prefix := range thirdPartyDirPrefixes {
+		if strings.HasPrefix(path, prefix) {
+			for _, prefix := range thirdPartyDirPrefixExceptions {
+				if prefix.MatchString(path) {
+					return false
+				}
+			}
+			break
+		}
+	}
+
+	return true
+}
diff --git a/cc/compiler_test.go b/cc/compiler_test.go
new file mode 100644
index 0000000..c301388
--- /dev/null
+++ b/cc/compiler_test.go
@@ -0,0 +1,43 @@
+// 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 cc
+
+import (
+	"testing"
+)
+
+func TestIsThirdParty(t *testing.T) {
+	shouldFail := []string{
+		"external/foo/",
+		"vendor/bar/",
+		"hardware/underwater_jaguar/",
+	}
+	shouldPass := []string{
+		"vendor/google/cts/",
+		"hardware/google/pixel",
+		"hardware/interfaces/camera",
+		"hardware/ril/supa_ril",
+	}
+	for _, path := range shouldFail {
+		if !isThirdParty(path) {
+			t.Errorf("Expected %s to be considered third party", path)
+		}
+	}
+	for _, path := range shouldPass {
+		if isThirdParty(path) {
+			t.Errorf("Expected %s to *not* be considered third party", path)
+		}
+	}
+}
diff --git a/cc/config/arm_device.go b/cc/config/arm_device.go
index cd7c410..d37e486 100644
--- a/cc/config/arm_device.go
+++ b/cc/config/arm_device.go
@@ -236,6 +236,7 @@
 		"cortex-a72":     "${config.ArmClangCortexA53Cflags}",
 		"cortex-a73":     "${config.ArmClangCortexA53Cflags}",
 		"cortex-a75":     "${config.ArmClangCortexA55Cflags}",
+		"cortex-a76":     "${config.ArmClangCortexA55Cflags}",
 		"krait":          "${config.ArmClangKraitCflags}",
 		"kryo":           "${config.ArmClangKryoCflags}",
 		"kryo385":        "${config.ArmClangCortexA53Cflags}",
diff --git a/cc/config/clang.go b/cc/config/clang.go
index 47b60e7..8618d09 100644
--- a/cc/config/clang.go
+++ b/cc/config/clang.go
@@ -101,10 +101,6 @@
 		// not emit the table by default on Android since NDK still uses GNU binutils.
 		"-faddrsig",
 
-		// Make implicit fallthrough an error in the future.
-		"-Wimplicit-fallthrough",
-		"-Wno-error=implicit-fallthrough",
-
 		// Help catch common 32/64-bit errors.
 		"-Werror=int-conversion",
 
@@ -116,10 +112,6 @@
 		// TODO: can we remove this now?
 		"-Wno-reserved-id-macro",
 
-		// Disable overly aggressive warning for format strings.
-		// Bug: 20148343
-		"-Wno-format-pedantic",
-
 		// Workaround for ccache with clang.
 		// See http://petereisentraut.blogspot.com/2011/05/ccache-and-clang.html.
 		"-Wno-unused-command-line-argument",
@@ -140,13 +132,18 @@
 		// Disable -Winconsistent-missing-override until we can clean up the existing
 		// codebase for it.
 		"-Wno-inconsistent-missing-override",
+
+		// Warnings from clang-10
+		// Nested and array designated initialization is nice to have.
+		"-Wno-c99-designator",
 	}, " "))
 
 	pctx.StaticVariable("ClangExtraCppflags", strings.Join([]string{
+		// -Wimplicit-fallthrough is not enabled by -Wall.
+		"-Wimplicit-fallthrough",
+
 		// Enable clang's thread-safety annotations in libcxx.
-		// Turn off -Wthread-safety-negative, to avoid breaking projects that use -Weverything.
 		"-D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS",
-		"-Wno-thread-safety-negative",
 
 		// libc++'s math.h has an #include_next outside of system_headers.
 		"-Wno-gnu-include-next",
@@ -168,25 +165,17 @@
 		// new warnings are fixed.
 		"-Wno-tautological-constant-compare",
 		"-Wno-tautological-type-limit-compare",
-		"-Wno-tautological-unsigned-enum-zero-compare",
-		"-Wno-tautological-unsigned-zero-compare",
-
-		// http://b/72330874 Disable -Wenum-compare until the instances detected by this new
-		// warning are fixed.
-		"-Wno-enum-compare",
-		"-Wno-enum-compare-switch",
-
-		// Disable c++98-specific warning since Android is not concerned with C++98
-		// compatibility.
-		"-Wno-c++98-compat-extra-semi",
-
-		// Disable this warning because we don't care about behavior with older compilers.
-		"-Wno-return-std-move-in-c++11",
+		// http://b/145210666
+		"-Wno-reorder-init-list",
+		// http://b/145211066
+		"-Wno-implicit-int-float-conversion",
 	}, " "))
 
-	// Extra cflags for projects under external/ directory
+	// Extra cflags for external third-party projects to disable warnings that
+	// are infeasible to fix in all the external projects and their upstream repos.
 	pctx.StaticVariable("ClangExtraExternalCflags", strings.Join([]string{
-		// TODO(yikong): Move -Wno flags here
+		"-Wno-enum-compare",
+		"-Wno-enum-compare-switch",
 
 		// http://b/72331524 Allow null pointer arithmetic until the instances detected by
 		// this new warning are fixed.
@@ -195,6 +184,13 @@
 		// Bug: http://b/29823425 Disable -Wnull-dereference until the
 		// new instances detected by this warning are fixed.
 		"-Wno-null-dereference",
+
+		// http://b/145211477
+		"-Wno-pointer-compare",
+		// http://b/145211022
+		"-Wno-xor-used-as-pow",
+		// http://b/145211022
+		"-Wno-final-dtor-non-final-class",
 	}, " "))
 }
 
diff --git a/cc/config/global.go b/cc/config/global.go
index a27246e..bae5555 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -46,6 +46,10 @@
 		"-g",
 
 		"-fno-strict-aliasing",
+
+		"-Werror=date-time",
+		"-Werror=pragma-pack",
+		"-Werror=pragma-pack-suspicious-include",
 	}
 
 	commonGlobalConlyflags = []string{}
@@ -67,7 +71,6 @@
 		"-Werror=non-virtual-dtor",
 		"-Werror=address",
 		"-Werror=sequence-point",
-		"-Werror=date-time",
 		"-Werror=format-security",
 	}
 
@@ -107,6 +110,7 @@
 	noOverrideGlobalCflags = []string{
 		"-Werror=int-to-pointer-cast",
 		"-Werror=pointer-to-int-cast",
+		"-Werror=fortify-source",
 	}
 
 	IllegalFlags = []string{
@@ -122,8 +126,8 @@
 
 	// prebuilts/clang default settings.
 	ClangDefaultBase         = "prebuilts/clang/host"
-	ClangDefaultVersion      = "clang-r353983d"
-	ClangDefaultShortVersion = "9.0.4"
+	ClangDefaultVersion      = "clang-r370808"
+	ClangDefaultShortVersion = "10.0.1"
 
 	// Directories with warnings from Android.bp files.
 	WarningAllowedProjects = []string{
@@ -150,8 +154,22 @@
 	pctx.StaticVariable("HostGlobalLdflags", strings.Join(hostGlobalLdflags, " "))
 	pctx.StaticVariable("HostGlobalLldflags", strings.Join(hostGlobalLldflags, " "))
 
-	pctx.StaticVariable("CommonClangGlobalCflags",
-		strings.Join(append(ClangFilterUnknownCflags(commonGlobalCflags), "${ClangExtraCflags}"), " "))
+	pctx.VariableFunc("CommonClangGlobalCflags", func(ctx android.PackageVarContext) string {
+		flags := ClangFilterUnknownCflags(commonGlobalCflags)
+		flags = append(flags, "${ClangExtraCflags}")
+
+		// http://b/131390872
+		// Automatically initialize any uninitialized stack variables.
+		// Prefer zero-init if both options are set.
+		if ctx.Config().IsEnvTrue("AUTO_ZERO_INITIALIZE") {
+			flags = append(flags, "-ftrivial-auto-var-init=zero -enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang")
+		} else if ctx.Config().IsEnvTrue("AUTO_PATTERN_INITIALIZE") {
+			flags = append(flags, "-ftrivial-auto-var-init=pattern")
+		}
+
+		return strings.Join(flags, " ")
+	})
+
 	pctx.VariableFunc("DeviceClangGlobalCflags", func(ctx android.PackageVarContext) string {
 		if ctx.Config().Fuchsia() {
 			return strings.Join(ClangFilterUnknownCflags(deviceGlobalCflags), " ")
diff --git a/cc/config/toolchain.go b/cc/config/toolchain.go
index d5e9d01..db9092d 100644
--- a/cc/config/toolchain.go
+++ b/cc/config/toolchain.go
@@ -181,6 +181,9 @@
 	if arch == "" {
 		return ""
 	}
+	if !t.Bionic() {
+		return "libclang_rt." + library + "-" + arch
+	}
 	return "libclang_rt." + library + "-" + arch + "-android"
 }
 
@@ -224,6 +227,10 @@
 	return LibclangRuntimeLibrary(t, "scudo_minimal")
 }
 
+func LibFuzzerRuntimeLibrary(t Toolchain) string {
+	return LibclangRuntimeLibrary(t, "fuzzer")
+}
+
 func ToolPath(t Toolchain) string {
 	if p := t.ToolPath(); p != "" {
 		return p
diff --git a/cc/config/vndk.go b/cc/config/vndk.go
index e754ad5..9feb5a3 100644
--- a/cc/config/vndk.go
+++ b/cc/config/vndk.go
@@ -65,6 +65,7 @@
 	"android.hardware.neuralnetworks@1.0",
 	"android.hardware.neuralnetworks@1.1",
 	"android.hardware.neuralnetworks@1.2",
+	"android.hardware.neuralnetworks@1.3",
 	"android.hardware.nfc@1.0",
 	"android.hardware.nfc@1.1",
 	"android.hardware.nfc@1.2",
@@ -83,6 +84,7 @@
 	"android.hardware.thermal@1.0",
 	"android.hardware.tv.cec@1.0",
 	"android.hardware.tv.input@1.0",
+	"android.hardware.vibrator-ndk_platform",
 	"android.hardware.vibrator@1.0",
 	"android.hardware.vibrator@1.1",
 	"android.hardware.vibrator@1.2",
@@ -120,6 +122,8 @@
 	"libmedia_omx",
 	"libmemtrack",
 	"libnetutils",
+	"libprotobuf-cpp-full",
+	"libprotobuf-cpp-lite",
 	"libpuresoftkeymasterdevice",
 	"libradio_metadata",
 	"libselinux",
diff --git a/cc/config/x86_64_device.go b/cc/config/x86_64_device.go
index 0f0420f..bcfae5d 100644
--- a/cc/config/x86_64_device.go
+++ b/cc/config/x86_64_device.go
@@ -63,14 +63,20 @@
 	}
 
 	x86_64ArchFeatureCflags = map[string][]string{
-		"ssse3":  []string{"-DUSE_SSSE3", "-mssse3"},
+		"ssse3":  []string{"-mssse3"},
 		"sse4":   []string{"-msse4"},
 		"sse4_1": []string{"-msse4.1"},
 		"sse4_2": []string{"-msse4.2"},
+
+		// Not all cases there is performance gain by enabling -mavx -mavx2
+		// flags so these flags are not enabled by default.
+		// if there is performance gain in individual library components,
+		// the compiler flags can be set in corresponding bp files.
+		// "avx":    []string{"-mavx"},
+		// "avx2":   []string{"-mavx2"},
+		// "avx512": []string{"-mavx512"}
+
 		"popcnt": []string{"-mpopcnt"},
-		"avx":    []string{"-mavx"},
-		"avx2":   []string{"-mavx2"},
-		"avx512": []string{"-mavx512"},
 		"aes_ni": []string{"-maes"},
 	}
 )
diff --git a/cc/config/x86_64_fuchsia_device.go b/cc/config/x86_64_fuchsia_device.go
index 79af00c..0f2013b 100644
--- a/cc/config/x86_64_fuchsia_device.go
+++ b/cc/config/x86_64_fuchsia_device.go
@@ -92,7 +92,7 @@
 }
 
 func (t *toolchainFuchsiaX8664) ToolchainClangCflags() string {
-	return "-DUSE_SSSE3 -mssse3"
+	return "-mssse3"
 }
 
 var toolchainFuchsiaSingleton Toolchain = &toolchainFuchsiaX8664{}
diff --git a/cc/config/x86_darwin_host.go b/cc/config/x86_darwin_host.go
index 1026370..63b9d48 100644
--- a/cc/config/x86_darwin_host.go
+++ b/cc/config/x86_darwin_host.go
@@ -63,6 +63,7 @@
 		"10.12",
 		"10.13",
 		"10.14",
+		"10.15",
 	}
 
 	darwinAvailableLibraries = append(
diff --git a/cc/config/x86_device.go b/cc/config/x86_device.go
index 500014e..64392dc 100644
--- a/cc/config/x86_device.go
+++ b/cc/config/x86_device.go
@@ -82,12 +82,19 @@
 	}
 
 	x86ArchFeatureCflags = map[string][]string{
-		"ssse3":  []string{"-DUSE_SSSE3", "-mssse3"},
+		"ssse3":  []string{"-mssse3"},
 		"sse4":   []string{"-msse4"},
 		"sse4_1": []string{"-msse4.1"},
 		"sse4_2": []string{"-msse4.2"},
-		"avx":    []string{"-mavx"},
-		"avx2":   []string{"-mavx2"},
+
+		// Not all cases there is performance gain by enabling -mavx -mavx2
+		// flags so these flags are not enabled by default.
+		// if there is performance gain in individual library components,
+		// the compiler flags can be set in corresponding bp files.
+		// "avx":    []string{"-mavx"},
+		// "avx2":   []string{"-mavx2"},
+		// "avx512": []string{"-mavx512"}
+
 		"aes_ni": []string{"-maes"},
 	}
 )
diff --git a/cc/config/x86_linux_host.go b/cc/config/x86_linux_host.go
index f072f34..f08a379 100644
--- a/cc/config/x86_linux_host.go
+++ b/cc/config/x86_linux_host.go
@@ -233,6 +233,14 @@
 	return "${config.LinuxX8664YasmFlags}"
 }
 
+func (toolchainLinuxX86) LibclangRuntimeLibraryArch() string {
+	return "i686"
+}
+
+func (toolchainLinuxX8664) LibclangRuntimeLibraryArch() string {
+	return "x86_64"
+}
+
 func (t *toolchainLinux) AvailableLibraries() []string {
 	return linuxAvailableLibraries
 }
diff --git a/cc/config/x86_windows_host.go b/cc/config/x86_windows_host.go
index 0f500b6..43e8c85 100644
--- a/cc/config/x86_windows_host.go
+++ b/cc/config/x86_windows_host.go
@@ -73,6 +73,7 @@
 		"-m32",
 		"-Wl,--large-address-aware",
 		"-L${WindowsGccRoot}/${WindowsGccTriple}/lib32",
+		"-static-libgcc",
 	}
 	windowsX86ClangLdflags = append(ClangFilterUnknownCflags(windowsX86Ldflags), []string{
 		"-B${WindowsGccRoot}/${WindowsGccTriple}/bin",
@@ -86,6 +87,7 @@
 		"-m64",
 		"-L${WindowsGccRoot}/${WindowsGccTriple}/lib64",
 		"-Wl,--high-entropy-va",
+		"-static-libgcc",
 	}
 	windowsX8664ClangLdflags = append(ClangFilterUnknownCflags(windowsX8664Ldflags), []string{
 		"-B${WindowsGccRoot}/${WindowsGccTriple}/bin",
diff --git a/cc/coverage.go b/cc/coverage.go
index 9dc7f06..b6451ee 100644
--- a/cc/coverage.go
+++ b/cc/coverage.go
@@ -17,6 +17,8 @@
 import (
 	"strconv"
 
+	"github.com/google/blueprint"
+
 	"android/soong/android"
 )
 
@@ -41,40 +43,38 @@
 	return []interface{}{&cov.Properties}
 }
 
-func (cov *coverage) deps(ctx BaseModuleContext, deps Deps) Deps {
-	if cov.Properties.NeedCoverageBuild {
-		// Link libprofile-extras/libprofile-extras_ndk when coverage
-		// variant is required.  This is a no-op unless coverage is
-		// actually enabled during linking, when
-		// '-uinit_profile_extras' is added (in flags()) to force the
-		// setup code in libprofile-extras be linked into the
-		// binary/library.
-		//
-		// We cannot narrow it further to only the 'cov' variant since
-		// the mutator hasn't run (and we don't have the 'cov' variant
-		// yet).
-		if !ctx.useSdk() {
-			deps.LateStaticLibs = append(deps.LateStaticLibs, "libprofile-extras")
-		} else {
-			deps.LateStaticLibs = append(deps.LateStaticLibs, "libprofile-extras_ndk")
-		}
+func getProfileLibraryName(ctx ModuleContextIntf) string {
+	// This function should only ever be called for a cc.Module, so the
+	// following statement should always succeed.
+	if ctx.useSdk() {
+		return "libprofile-extras_ndk"
+	} else {
+		return "libprofile-extras"
+	}
+}
+
+func (cov *coverage) deps(ctx DepsContext, deps Deps) Deps {
+	if cov.Properties.NeedCoverageVariant {
+		ctx.AddVariationDependencies([]blueprint.Variation{
+			{Mutator: "link", Variation: "static"},
+		}, coverageDepTag, getProfileLibraryName(ctx))
 	}
 	return deps
 }
 
-func (cov *coverage) flags(ctx ModuleContext, flags Flags) Flags {
+func (cov *coverage) flags(ctx ModuleContext, flags Flags, deps PathDeps) (Flags, PathDeps) {
 	if !ctx.DeviceConfig().NativeCoverageEnabled() {
-		return flags
+		return flags, deps
 	}
 
 	if cov.Properties.CoverageEnabled {
 		flags.Coverage = true
-		flags.GlobalFlags = append(flags.GlobalFlags, "--coverage", "-O0")
+		flags.Local.CommonFlags = append(flags.Local.CommonFlags, "--coverage", "-O0")
 		cov.linkCoverage = true
 
 		// Override -Wframe-larger-than and non-default optimization
 		// flags that the module may use.
-		flags.CFlags = append(flags.CFlags, "-Wno-frame-larger-than=", "-O0")
+		flags.Local.CFlags = append(flags.Local.CFlags, "-Wno-frame-larger-than=", "-O0")
 	}
 
 	// Even if we don't have coverage enabled, if any of our object files were compiled
@@ -112,13 +112,15 @@
 	}
 
 	if cov.linkCoverage {
-		flags.LdFlags = append(flags.LdFlags, "--coverage")
+		flags.Local.LdFlags = append(flags.Local.LdFlags, "--coverage")
 
-		// Force linking of constructor/setup code in libprofile-extras
-		flags.LdFlags = append(flags.LdFlags, "-uinit_profile_extras")
+		coverage := ctx.GetDirectDepWithTag(getProfileLibraryName(ctx), coverageDepTag).(*Module)
+		deps.WholeStaticLibs = append(deps.WholeStaticLibs, coverage.OutputFile().Path())
+
+		flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--wrap,getenv")
 	}
 
-	return flags
+	return flags, deps
 }
 
 func (cov *coverage) begin(ctx BaseModuleContext) {
@@ -138,7 +140,6 @@
 	} else {
 		// Check if Native_coverage is set to false.  This property defaults to true.
 		needCoverageVariant = BoolDefault(cov.Properties.Native_coverage, true)
-
 		if sdk_version := ctx.sdkVersion(); ctx.useSdk() && sdk_version != "current" {
 			// Native coverage is not supported for SDK versions < 23
 			if fromApi, err := strconv.Atoi(sdk_version); err == nil && fromApi < 23 {
@@ -156,6 +157,15 @@
 	cov.Properties.NeedCoverageVariant = needCoverageVariant
 }
 
+// Coverage is an interface for non-CC modules to implement to be mutated for coverage
+type Coverage interface {
+	android.Module
+	IsNativeCoverageNeeded(ctx android.BaseModuleContext) bool
+	PreventInstall()
+	HideFromMake()
+	MarkAsCoverageVariant(bool)
+}
+
 func coverageMutator(mctx android.BottomUpMutatorContext) {
 	if c, ok := mctx.Module().(*Module); ok && c.coverage != nil {
 		needCoverageVariant := c.coverage.Properties.NeedCoverageVariant
@@ -175,5 +185,15 @@
 			m[1].(*Module).coverage.Properties.CoverageEnabled = needCoverageBuild
 			m[1].(*Module).coverage.Properties.IsCoverageVariant = true
 		}
+	} else if cov, ok := mctx.Module().(Coverage); ok && cov.IsNativeCoverageNeeded(mctx) {
+		// APEX modules fall here
+
+		// Note: variant "" is also created because an APEX can be depended on by another
+		// module which are split into "" and "cov" variants. e.g. when cc_test refers
+		// to an APEX via 'data' property.
+		m := mctx.CreateVariations("", "cov")
+		m[0].(Coverage).MarkAsCoverageVariant(true)
+		m[0].(Coverage).PreventInstall()
+		m[0].(Coverage).HideFromMake()
 	}
 }
diff --git a/cc/fuzz.go b/cc/fuzz.go
new file mode 100644
index 0000000..8b84be8
--- /dev/null
+++ b/cc/fuzz.go
@@ -0,0 +1,516 @@
+// 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 cc
+
+import (
+	"encoding/json"
+	"path/filepath"
+	"sort"
+	"strings"
+
+	"android/soong/android"
+	"android/soong/cc/config"
+)
+
+type FuzzConfig struct {
+	// Email address of people to CC on bugs or contact about this fuzz target.
+	Cc []string `json:"cc,omitempty"`
+	// Specify whether to enable continuous fuzzing on devices. Defaults to true.
+	Fuzz_on_haiku_device *bool `json:"fuzz_on_haiku_device,omitempty"`
+	// Specify whether to enable continuous fuzzing on host. Defaults to true.
+	Fuzz_on_haiku_host *bool `json:"fuzz_on_haiku_host,omitempty"`
+	// Component in Google's bug tracking system that bugs should be filed to.
+	Componentid *int64 `json:"componentid,omitempty"`
+	// Hotlists in Google's bug tracking system that bugs should be marked with.
+	Hotlists []string `json:"hotlists,omitempty"`
+}
+
+func (f *FuzzConfig) String() string {
+	b, err := json.Marshal(f)
+	if err != nil {
+		panic(err)
+	}
+
+	return string(b)
+}
+
+type FuzzProperties struct {
+	// Optional list of seed files to be installed to the fuzz target's output
+	// directory.
+	Corpus []string `android:"path"`
+	// Optional list of data files to be installed to the fuzz target's output
+	// directory. Directory structure relative to the module is preserved.
+	Data []string `android:"path"`
+	// Optional dictionary to be installed to the fuzz target's output directory.
+	Dictionary *string `android:"path"`
+	// Config for running the target on fuzzing infrastructure.
+	Fuzz_config *FuzzConfig
+}
+
+func init() {
+	android.RegisterModuleType("cc_fuzz", FuzzFactory)
+	android.RegisterSingletonType("cc_fuzz_packaging", fuzzPackagingFactory)
+}
+
+// cc_fuzz creates a host/device fuzzer binary. Host binaries can be found at
+// $ANDROID_HOST_OUT/fuzz/, and device binaries can be found at /data/fuzz on
+// your device, or $ANDROID_PRODUCT_OUT/data/fuzz in your build tree.
+func FuzzFactory() android.Module {
+	module := NewFuzz(android.HostAndDeviceSupported)
+	return module.Init()
+}
+
+func NewFuzzInstaller() *baseInstaller {
+	return NewBaseInstaller("fuzz", "fuzz", InstallInData)
+}
+
+type fuzzBinary struct {
+	*binaryDecorator
+	*baseCompiler
+
+	Properties            FuzzProperties
+	dictionary            android.Path
+	corpus                android.Paths
+	corpusIntermediateDir android.Path
+	config                android.Path
+	data                  android.Paths
+	dataIntermediateDir   android.Path
+	installedSharedDeps   []string
+}
+
+func (fuzz *fuzzBinary) linkerProps() []interface{} {
+	props := fuzz.binaryDecorator.linkerProps()
+	props = append(props, &fuzz.Properties)
+	return props
+}
+
+func (fuzz *fuzzBinary) linkerInit(ctx BaseModuleContext) {
+	fuzz.binaryDecorator.linkerInit(ctx)
+}
+
+func (fuzz *fuzzBinary) linkerDeps(ctx DepsContext, deps Deps) Deps {
+	deps.StaticLibs = append(deps.StaticLibs,
+		config.LibFuzzerRuntimeLibrary(ctx.toolchain()))
+	deps = fuzz.binaryDecorator.linkerDeps(ctx, deps)
+	return deps
+}
+
+func (fuzz *fuzzBinary) linkerFlags(ctx ModuleContext, flags Flags) Flags {
+	flags = fuzz.binaryDecorator.linkerFlags(ctx, flags)
+	// RunPaths on devices isn't instantiated by the base linker. `../lib` for
+	// installed fuzz targets (both host and device), and `./lib` for fuzz
+	// target packages.
+	flags.Local.LdFlags = append(flags.Local.LdFlags, `-Wl,-rpath,\$$ORIGIN/../lib`)
+	flags.Local.LdFlags = append(flags.Local.LdFlags, `-Wl,-rpath,\$$ORIGIN/lib`)
+	return flags
+}
+
+// This function performs a breadth-first search over the provided module's
+// dependencies using `visitDirectDeps` to enumerate all shared library
+// dependencies. We require breadth-first expansion, as otherwise we may
+// incorrectly use the core libraries (sanitizer runtimes, libc, libdl, etc.)
+// from a dependency. This may cause issues when dependencies have explicit
+// sanitizer tags, as we may get a dependency on an unsanitized libc, etc.
+func collectAllSharedDependencies(ctx android.SingletonContext, module android.Module) android.Paths {
+	var fringe []android.Module
+
+	seen := make(map[android.Module]bool)
+
+	// Enumerate the first level of dependencies, as we discard all non-library
+	// modules in the BFS loop below.
+	ctx.VisitDirectDeps(module, func(dep android.Module) {
+		if isValidSharedDependency(dep) {
+			fringe = append(fringe, dep)
+		}
+	})
+
+	var sharedLibraries android.Paths
+
+	for i := 0; i < len(fringe); i++ {
+		module := fringe[i]
+		if seen[module] {
+			continue
+		}
+		seen[module] = true
+
+		ccModule := module.(*Module)
+		sharedLibraries = append(sharedLibraries, ccModule.UnstrippedOutputFile())
+		ctx.VisitDirectDeps(module, func(dep android.Module) {
+			if isValidSharedDependency(dep) && !seen[dep] {
+				fringe = append(fringe, dep)
+			}
+		})
+	}
+
+	return sharedLibraries
+}
+
+// This function takes a module and determines if it is a unique shared library
+// that should be installed in the fuzz target output directories. This function
+// returns true, unless:
+//  - The module is not a shared library, or
+//  - The module is a header, stub, or vendor-linked library.
+func isValidSharedDependency(dependency android.Module) bool {
+	// TODO(b/144090547): We should be parsing these modules using
+	// ModuleDependencyTag instead of the current brute-force checking.
+
+	if linkable, ok := dependency.(LinkableInterface); !ok || // Discard non-linkables.
+		!linkable.CcLibraryInterface() || !linkable.Shared() || // Discard static libs.
+		linkable.UseVndk() || // Discard vendor linked libraries.
+		// Discard stubs libs (only CCLibrary variants). Prebuilt libraries should not
+		// be excluded on the basis of they're not CCLibrary()'s.
+		(linkable.CcLibrary() && linkable.BuildStubs()) {
+		return false
+	}
+
+	// We discarded module stubs libraries above, but the LLNDK prebuilts stubs
+	// libraries must be handled differently - by looking for the stubDecorator.
+	// Discard LLNDK prebuilts stubs as well.
+	if ccLibrary, isCcLibrary := dependency.(*Module); isCcLibrary {
+		if _, isLLndkStubLibrary := ccLibrary.linker.(*stubDecorator); isLLndkStubLibrary {
+			return false
+		}
+	}
+
+	return true
+}
+
+func sharedLibraryInstallLocation(
+	libraryPath android.Path, isHost bool, archString string) string {
+	installLocation := "$(PRODUCT_OUT)/data"
+	if isHost {
+		installLocation = "$(HOST_OUT)"
+	}
+	installLocation = filepath.Join(
+		installLocation, "fuzz", archString, "lib", libraryPath.Base())
+	return installLocation
+}
+
+func (fuzz *fuzzBinary) install(ctx ModuleContext, file android.Path) {
+	fuzz.binaryDecorator.baseInstaller.dir = filepath.Join(
+		"fuzz", ctx.Target().Arch.ArchType.String(), ctx.ModuleName())
+	fuzz.binaryDecorator.baseInstaller.dir64 = filepath.Join(
+		"fuzz", ctx.Target().Arch.ArchType.String(), ctx.ModuleName())
+	fuzz.binaryDecorator.baseInstaller.install(ctx, file)
+
+	fuzz.corpus = android.PathsForModuleSrc(ctx, fuzz.Properties.Corpus)
+	builder := android.NewRuleBuilder()
+	intermediateDir := android.PathForModuleOut(ctx, "corpus")
+	for _, entry := range fuzz.corpus {
+		builder.Command().Text("cp").
+			Input(entry).
+			Output(intermediateDir.Join(ctx, entry.Base()))
+	}
+	builder.Build(pctx, ctx, "copy_corpus", "copy corpus")
+	fuzz.corpusIntermediateDir = intermediateDir
+
+	fuzz.data = android.PathsForModuleSrc(ctx, fuzz.Properties.Data)
+	builder = android.NewRuleBuilder()
+	intermediateDir = android.PathForModuleOut(ctx, "data")
+	for _, entry := range fuzz.data {
+		builder.Command().Text("cp").
+			Input(entry).
+			Output(intermediateDir.Join(ctx, entry.Rel()))
+	}
+	builder.Build(pctx, ctx, "copy_data", "copy data")
+	fuzz.dataIntermediateDir = intermediateDir
+
+	if fuzz.Properties.Dictionary != nil {
+		fuzz.dictionary = android.PathForModuleSrc(ctx, *fuzz.Properties.Dictionary)
+		if fuzz.dictionary.Ext() != ".dict" {
+			ctx.PropertyErrorf("dictionary",
+				"Fuzzer dictionary %q does not have '.dict' extension",
+				fuzz.dictionary.String())
+		}
+	}
+
+	if fuzz.Properties.Fuzz_config != nil {
+		configPath := android.PathForModuleOut(ctx, "config").Join(ctx, "config.json")
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        android.WriteFile,
+			Description: "fuzzer infrastructure configuration",
+			Output:      configPath,
+			Args: map[string]string{
+				"content": fuzz.Properties.Fuzz_config.String(),
+			},
+		})
+		fuzz.config = configPath
+	}
+
+	// Grab the list of required shared libraries.
+	seen := make(map[android.Module]bool)
+	var sharedLibraries android.Paths
+	ctx.WalkDeps(func(child, parent android.Module) bool {
+		if seen[child] {
+			return false
+		}
+		seen[child] = true
+
+		if isValidSharedDependency(child) {
+			sharedLibraries = append(sharedLibraries, child.(*Module).UnstrippedOutputFile())
+			return true
+		}
+		return false
+	})
+
+	for _, lib := range sharedLibraries {
+		fuzz.installedSharedDeps = append(fuzz.installedSharedDeps,
+			sharedLibraryInstallLocation(
+				lib, ctx.Host(), ctx.Arch().ArchType.String()))
+	}
+}
+
+func NewFuzz(hod android.HostOrDeviceSupported) *Module {
+	module, binary := NewBinary(hod)
+
+	binary.baseInstaller = NewFuzzInstaller()
+	module.sanitize.SetSanitizer(fuzzer, true)
+
+	fuzz := &fuzzBinary{
+		binaryDecorator: binary,
+		baseCompiler:    NewBaseCompiler(),
+	}
+	module.compiler = fuzz
+	module.linker = fuzz
+	module.installer = fuzz
+
+	// The fuzzer runtime is not present for darwin host modules, disable cc_fuzz modules when targeting darwin.
+	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
+		disableDarwinAndLinuxBionic := struct {
+			Target struct {
+				Darwin struct {
+					Enabled *bool
+				}
+				Linux_bionic struct {
+					Enabled *bool
+				}
+			}
+		}{}
+		disableDarwinAndLinuxBionic.Target.Darwin.Enabled = BoolPtr(false)
+		disableDarwinAndLinuxBionic.Target.Linux_bionic.Enabled = BoolPtr(false)
+		ctx.AppendProperties(&disableDarwinAndLinuxBionic)
+	})
+
+	return module
+}
+
+// Responsible for generating GNU Make rules that package fuzz targets into
+// their architecture & target/host specific zip file.
+type fuzzPackager struct {
+	packages                android.Paths
+	sharedLibInstallStrings []string
+	fuzzTargets             map[string]bool
+}
+
+func fuzzPackagingFactory() android.Singleton {
+	return &fuzzPackager{}
+}
+
+type fileToZip struct {
+	SourceFilePath        android.Path
+	DestinationPathPrefix string
+}
+
+type archOs struct {
+	hostOrTarget string
+	arch         string
+	dir          string
+}
+
+func (s *fuzzPackager) GenerateBuildActions(ctx android.SingletonContext) {
+	// Map between each architecture + host/device combination, and the files that
+	// need to be packaged (in the tuple of {source file, destination folder in
+	// archive}).
+	archDirs := make(map[archOs][]fileToZip)
+
+	// Map tracking whether each shared library has an install rule to avoid duplicate install rules from
+	// multiple fuzzers that depend on the same shared library.
+	sharedLibraryInstalled := make(map[string]bool)
+
+	// List of individual fuzz targets, so that 'make fuzz' also installs the targets
+	// to the correct output directories as well.
+	s.fuzzTargets = make(map[string]bool)
+
+	ctx.VisitAllModules(func(module android.Module) {
+		// Discard non-fuzz targets.
+		ccModule, ok := module.(*Module)
+		if !ok {
+			return
+		}
+
+		fuzzModule, ok := ccModule.compiler.(*fuzzBinary)
+		if !ok {
+			return
+		}
+
+		// Discard vendor-NDK-linked + recovery modules, they're duplicates of
+		// fuzz targets we're going to package anyway.
+		if !ccModule.Enabled() || ccModule.Properties.PreventInstall ||
+			ccModule.UseVndk() || ccModule.InRecovery() {
+			return
+		}
+
+		// Discard modules that are in an unavailable namespace.
+		if !ccModule.ExportedToMake() {
+			return
+		}
+
+		s.fuzzTargets[module.Name()] = true
+
+		hostOrTargetString := "target"
+		if ccModule.Host() {
+			hostOrTargetString = "host"
+		}
+
+		archString := ccModule.Arch().ArchType.String()
+		archDir := android.PathForIntermediates(ctx, "fuzz", hostOrTargetString, archString)
+		archOs := archOs{hostOrTarget: hostOrTargetString, arch: archString, dir: archDir.String()}
+
+		// Grab the list of required shared libraries.
+		sharedLibraries := collectAllSharedDependencies(ctx, module)
+
+		var files []fileToZip
+		builder := android.NewRuleBuilder()
+
+		// Package the corpora into a zipfile.
+		if fuzzModule.corpus != nil {
+			corpusZip := archDir.Join(ctx, module.Name()+"_seed_corpus.zip")
+			command := builder.Command().BuiltTool(ctx, "soong_zip").
+				Flag("-j").
+				FlagWithOutput("-o ", corpusZip)
+			command.FlagWithRspFileInputList("-l ", fuzzModule.corpus)
+			files = append(files, fileToZip{corpusZip, ""})
+		}
+
+		// Package the data into a zipfile.
+		if fuzzModule.data != nil {
+			dataZip := archDir.Join(ctx, module.Name()+"_data.zip")
+			command := builder.Command().BuiltTool(ctx, "soong_zip").
+				FlagWithOutput("-o ", dataZip)
+			for _, f := range fuzzModule.data {
+				intermediateDir := strings.TrimSuffix(f.String(), f.Rel())
+				command.FlagWithArg("-C ", intermediateDir)
+				command.FlagWithInput("-f ", f)
+			}
+			files = append(files, fileToZip{dataZip, ""})
+		}
+
+		// Find and mark all the transiently-dependent shared libraries for
+		// packaging.
+		for _, library := range sharedLibraries {
+			files = append(files, fileToZip{library, "lib"})
+
+			// For each architecture-specific shared library dependency, we need to
+			// install it to the output directory. Setup the install destination here,
+			// which will be used by $(copy-many-files) in the Make backend.
+			installDestination := sharedLibraryInstallLocation(
+				library, ccModule.Host(), archString)
+			if sharedLibraryInstalled[installDestination] {
+				continue
+			}
+			sharedLibraryInstalled[installDestination] = true
+			// Escape all the variables, as the install destination here will be called
+			// via. $(eval) in Make.
+			installDestination = strings.ReplaceAll(
+				installDestination, "$", "$$")
+			s.sharedLibInstallStrings = append(s.sharedLibInstallStrings,
+				library.String()+":"+installDestination)
+		}
+
+		// The executable.
+		files = append(files, fileToZip{ccModule.UnstrippedOutputFile(), ""})
+
+		// The dictionary.
+		if fuzzModule.dictionary != nil {
+			files = append(files, fileToZip{fuzzModule.dictionary, ""})
+		}
+
+		// Additional fuzz config.
+		if fuzzModule.config != nil {
+			files = append(files, fileToZip{fuzzModule.config, ""})
+		}
+
+		fuzzZip := archDir.Join(ctx, module.Name()+".zip")
+		command := builder.Command().BuiltTool(ctx, "soong_zip").
+			Flag("-j").
+			FlagWithOutput("-o ", fuzzZip)
+		for _, file := range files {
+			if file.DestinationPathPrefix != "" {
+				command.FlagWithArg("-P ", file.DestinationPathPrefix)
+			} else {
+				command.Flag("-P ''")
+			}
+			command.FlagWithInput("-f ", file.SourceFilePath)
+		}
+
+		builder.Build(pctx, ctx, "create-"+fuzzZip.String(),
+			"Package "+module.Name()+" for "+archString+"-"+hostOrTargetString)
+
+		archDirs[archOs] = append(archDirs[archOs], fileToZip{fuzzZip, ""})
+	})
+
+	var archOsList []archOs
+	for archOs := range archDirs {
+		archOsList = append(archOsList, archOs)
+	}
+	sort.Slice(archOsList, func(i, j int) bool { return archOsList[i].dir < archOsList[j].dir })
+
+	for _, archOs := range archOsList {
+		filesToZip := archDirs[archOs]
+		arch := archOs.arch
+		hostOrTarget := archOs.hostOrTarget
+		builder := android.NewRuleBuilder()
+		outputFile := android.PathForOutput(ctx, "fuzz-"+hostOrTarget+"-"+arch+".zip")
+		s.packages = append(s.packages, outputFile)
+
+		command := builder.Command().BuiltTool(ctx, "soong_zip").
+			Flag("-j").
+			FlagWithOutput("-o ", outputFile).
+			Flag("-L 0") // No need to try and re-compress the zipfiles.
+
+		for _, fileToZip := range filesToZip {
+			if fileToZip.DestinationPathPrefix != "" {
+				command.FlagWithArg("-P ", fileToZip.DestinationPathPrefix)
+			} else {
+				command.Flag("-P ''")
+			}
+			command.FlagWithInput("-f ", fileToZip.SourceFilePath)
+		}
+
+		builder.Build(pctx, ctx, "create-fuzz-package-"+arch+"-"+hostOrTarget,
+			"Create fuzz target packages for "+arch+"-"+hostOrTarget)
+	}
+}
+
+func (s *fuzzPackager) MakeVars(ctx android.MakeVarsContext) {
+	packages := s.packages.Strings()
+	sort.Strings(packages)
+	sort.Strings(s.sharedLibInstallStrings)
+	// TODO(mitchp): Migrate this to use MakeVarsContext::DistForGoal() when it's
+	// ready to handle phony targets created in Soong. In the meantime, this
+	// exports the phony 'fuzz' target and dependencies on packages to
+	// core/main.mk so that we can use dist-for-goals.
+	ctx.Strict("SOONG_FUZZ_PACKAGING_ARCH_MODULES", strings.Join(packages, " "))
+	ctx.Strict("FUZZ_TARGET_SHARED_DEPS_INSTALL_PAIRS",
+		strings.Join(s.sharedLibInstallStrings, " "))
+
+	// Preallocate the slice of fuzz targets to minimise memory allocations.
+	fuzzTargets := make([]string, 0, len(s.fuzzTargets))
+	for target, _ := range s.fuzzTargets {
+		fuzzTargets = append(fuzzTargets, target)
+	}
+	sort.Strings(fuzzTargets)
+	ctx.Strict("ALL_FUZZ_TARGETS", strings.Join(fuzzTargets, " "))
+}
diff --git a/cc/gen.go b/cc/gen.go
index c9f45ee..17ab45f 100644
--- a/cc/gen.go
+++ b/cc/gen.go
@@ -98,7 +98,8 @@
 	}
 
 	cmd.Text("BISON_PKGDATADIR=prebuilts/build-tools/common/bison").
-		Tool(ctx.Config().PrebuiltBuildTool(ctx, "bison")).
+		FlagWithInput("M4=", ctx.Config().PrebuiltBuildTool(ctx, "m4")).
+		PrebuiltBuildTool(ctx, "bison").
 		Flag("-d").
 		Flags(flags).
 		FlagWithOutput("--defines=", headerFile).
@@ -112,15 +113,27 @@
 
 	aidlPackage := strings.TrimSuffix(aidlFile.Rel(), aidlFile.Base())
 	baseName := strings.TrimSuffix(aidlFile.Base(), aidlFile.Ext())
-	shortName := strings.TrimPrefix(baseName, "I")
+	shortName := baseName
+	// TODO(b/111362593): aidl_to_cpp_common.cpp uses heuristics to figure out if
+	//   an interface name has a leading I. Those same heuristics have been
+	//   moved here.
+	if len(baseName) >= 2 && baseName[0] == 'I' &&
+		strings.ToUpper(baseName)[1] == baseName[1] {
+		shortName = strings.TrimPrefix(baseName, "I")
+	}
 
 	outDir := android.PathForModuleGen(ctx, "aidl")
 	headerI := outDir.Join(ctx, aidlPackage, baseName+".h")
 	headerBn := outDir.Join(ctx, aidlPackage, "Bn"+shortName+".h")
 	headerBp := outDir.Join(ctx, aidlPackage, "Bp"+shortName+".h")
 
+	baseDir := strings.TrimSuffix(aidlFile.String(), aidlFile.Rel())
+	if baseDir != "" {
+		aidlFlags += " -I" + baseDir
+	}
+
 	cmd := rule.Command()
-	cmd.Tool(ctx.Config().HostToolPath(ctx, "aidl-cpp")).
+	cmd.BuiltTool(ctx, "aidl-cpp").
 		FlagWithDepFile("-d", depFile).
 		Flag("--ninja").
 		Flag(aidlFlags).
@@ -237,7 +250,7 @@
 			depFile := android.GenPathWithExt(ctx, "aidl", srcFile, "cpp.d")
 			srcFiles[i] = cppFile
 			deps = append(deps, genAidl(ctx, aidlRule, srcFile, cppFile, depFile, buildFlags.aidlFlags)...)
-		case ".rs", ".fs":
+		case ".rscript", ".fs":
 			cppFile := rsGeneratedCppFile(ctx, srcFile)
 			rsFiles = append(rsFiles, srcFiles[i])
 			srcFiles[i] = cppFile
diff --git a/cc/gen_stub_libs.py b/cc/gen_stub_libs.py
index 81bc398..0de703c 100755
--- a/cc/gen_stub_libs.py
+++ b/cc/gen_stub_libs.py
@@ -108,7 +108,7 @@
     return version.endswith('_PRIVATE') or version.endswith('_PLATFORM')
 
 
-def should_omit_version(version, arch, api, vndk, apex):
+def should_omit_version(version, arch, api, llndk, apex):
     """Returns True if the version section should be ommitted.
 
     We want to omit any sections that do not have any symbols we'll have in the
@@ -120,9 +120,9 @@
     if 'platform-only' in version.tags:
         return True
 
-    no_vndk_no_apex = 'vndk' not in version.tags and 'apex' not in version.tags
-    keep = no_vndk_no_apex or \
-           ('vndk' in version.tags and vndk) or \
+    no_llndk_no_apex = 'llndk' not in version.tags and 'apex' not in version.tags
+    keep = no_llndk_no_apex or \
+           ('llndk' in version.tags and llndk) or \
            ('apex' in version.tags and apex)
     if not keep:
         return True
@@ -133,11 +133,11 @@
     return False
 
 
-def should_omit_symbol(symbol, arch, api, vndk, apex):
+def should_omit_symbol(symbol, arch, api, llndk, apex):
     """Returns True if the symbol should be omitted."""
-    no_vndk_no_apex = 'vndk' not in symbol.tags and 'apex' not in symbol.tags
-    keep = no_vndk_no_apex or \
-           ('vndk' in symbol.tags and vndk) or \
+    no_llndk_no_apex = 'llndk' not in symbol.tags and 'apex' not in symbol.tags
+    keep = no_llndk_no_apex or \
+           ('llndk' in symbol.tags and llndk) or \
            ('apex' in symbol.tags and apex)
     if not keep:
         return True
@@ -250,12 +250,12 @@
 
 class SymbolFileParser(object):
     """Parses NDK symbol files."""
-    def __init__(self, input_file, api_map, arch, api, vndk, apex):
+    def __init__(self, input_file, api_map, arch, api, llndk, apex):
         self.input_file = input_file
         self.api_map = api_map
         self.arch = arch
         self.api = api
-        self.vndk = vndk
+        self.llndk = llndk
         self.apex = apex
         self.current_line = None
 
@@ -284,11 +284,11 @@
         symbol_names = set()
         multiply_defined_symbols = set()
         for version in versions:
-            if should_omit_version(version, self.arch, self.api, self.vndk, self.apex):
+            if should_omit_version(version, self.arch, self.api, self.llndk, self.apex):
                 continue
 
             for symbol in version.symbols:
-                if should_omit_symbol(symbol, self.arch, self.api, self.vndk, self.apex):
+                if should_omit_symbol(symbol, self.arch, self.api, self.llndk, self.apex):
                     continue
 
                 if symbol.name in symbol_names:
@@ -372,12 +372,12 @@
 
 class Generator(object):
     """Output generator that writes stub source files and version scripts."""
-    def __init__(self, src_file, version_script, arch, api, vndk, apex):
+    def __init__(self, src_file, version_script, arch, api, llndk, apex):
         self.src_file = src_file
         self.version_script = version_script
         self.arch = arch
         self.api = api
-        self.vndk = vndk
+        self.llndk = llndk
         self.apex = apex
 
     def write(self, versions):
@@ -387,14 +387,14 @@
 
     def write_version(self, version):
         """Writes a single version block's data to the output files."""
-        if should_omit_version(version, self.arch, self.api, self.vndk, self.apex):
+        if should_omit_version(version, self.arch, self.api, self.llndk, self.apex):
             return
 
         section_versioned = symbol_versioned_in_api(version.tags, self.api)
         version_empty = True
         pruned_symbols = []
         for symbol in version.symbols:
-            if should_omit_symbol(symbol, self.arch, self.api, self.vndk, self.apex):
+            if should_omit_symbol(symbol, self.arch, self.api, self.llndk, self.apex):
                 continue
 
             if symbol_versioned_in_api(symbol.tags, self.api):
@@ -456,7 +456,7 @@
         '--arch', choices=ALL_ARCHITECTURES, required=True,
         help='Architecture being targeted.')
     parser.add_argument(
-        '--vndk', action='store_true', help='Use the VNDK variant.')
+        '--llndk', action='store_true', help='Use the LLNDK variant.')
     parser.add_argument(
         '--apex', action='store_true', help='Use the APEX variant.')
 
@@ -493,14 +493,14 @@
     with open(args.symbol_file) as symbol_file:
         try:
             versions = SymbolFileParser(symbol_file, api_map, args.arch, api,
-                                        args.vndk, args.apex).parse()
+                                        args.llndk, args.apex).parse()
         except MultiplyDefinedSymbolError as ex:
             sys.exit('{}: error: {}'.format(args.symbol_file, ex))
 
     with open(args.stub_src, 'w') as src_file:
         with open(args.version_script, 'w') as version_file:
             generator = Generator(src_file, version_file, args.arch, api,
-                                  args.vndk, args.apex)
+                                  args.llndk, args.apex)
             generator.write(versions)
 
 
diff --git a/cc/gen_test.go b/cc/gen_test.go
index e4219d9..4b9a36e 100644
--- a/cc/gen_test.go
+++ b/cc/gen_test.go
@@ -16,6 +16,7 @@
 
 import (
 	"path/filepath"
+	"strings"
 	"testing"
 )
 
@@ -30,10 +31,10 @@
 			],
 		}`)
 
-		aidl := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_core_shared").Rule("aidl")
-		libfoo := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_core_shared").Module().(*Module)
+		aidl := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Rule("aidl")
+		libfoo := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Module().(*Module)
 
-		if !inList("-I"+filepath.Dir(aidl.Output.String()), libfoo.flags.GlobalFlags) {
+		if !inList("-I"+filepath.Dir(aidl.Output.String()), libfoo.flags.Local.CommonFlags) {
 			t.Errorf("missing aidl includes in global flags")
 		}
 	})
@@ -42,7 +43,8 @@
 		ctx := testCc(t, `
 		filegroup {
 			name: "fg",
-			srcs: ["b.aidl"],
+			srcs: ["sub/c.aidl"],
+			path: "sub",
 		}
 
 		cc_library_shared {
@@ -53,12 +55,18 @@
 			],
 		}`)
 
-		aidl := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_core_shared").Rule("aidl")
-		libfoo := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_core_shared").Module().(*Module)
+		aidl := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Rule("aidl")
+		libfoo := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Module().(*Module)
 
-		if !inList("-I"+filepath.Dir(aidl.Output.String()), libfoo.flags.GlobalFlags) {
+		if !inList("-I"+filepath.Dir(aidl.Output.String()), libfoo.flags.Local.CommonFlags) {
 			t.Errorf("missing aidl includes in global flags")
 		}
+
+		aidlCommand := aidl.RuleParams.Command
+		if !strings.Contains(aidlCommand, "-Isub") {
+			t.Errorf("aidl command for c.aidl should contain \"-Isub\", but was %q", aidlCommand)
+		}
+
 	})
 
 }
diff --git a/cc/genrule.go b/cc/genrule.go
index e594f4b..548d5f2 100644
--- a/cc/genrule.go
+++ b/cc/genrule.go
@@ -26,9 +26,6 @@
 type GenruleExtraProperties struct {
 	Vendor_available   *bool
 	Recovery_available *bool
-
-	// This genrule is for recovery variant
-	InRecovery bool `blueprint:"mutated"`
 }
 
 // cc_genrule is a genrule that can depend on other cc_* objects.
@@ -37,7 +34,9 @@
 func genRuleFactory() android.Module {
 	module := genrule.NewGenRule()
 
-	module.Extra = &GenruleExtraProperties{}
+	extra := &GenruleExtraProperties{}
+	module.Extra = extra
+	module.ImageInterface = extra
 	module.AddProperties(module.Extra)
 
 	android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibBoth)
@@ -46,3 +45,53 @@
 
 	return module
 }
+
+var _ android.ImageInterface = (*GenruleExtraProperties)(nil)
+
+func (g *GenruleExtraProperties) ImageMutatorBegin(ctx android.BaseModuleContext) {}
+
+func (g *GenruleExtraProperties) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
+	if ctx.DeviceConfig().VndkVersion() == "" {
+		return true
+	}
+
+	if ctx.DeviceConfig().ProductVndkVersion() != "" && ctx.ProductSpecific() {
+		return false
+	}
+
+	return Bool(g.Vendor_available) || !(ctx.SocSpecific() || ctx.DeviceSpecific())
+}
+
+func (g *GenruleExtraProperties) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
+	return Bool(g.Recovery_available)
+}
+
+func (g *GenruleExtraProperties) ExtraImageVariations(ctx android.BaseModuleContext) []string {
+	if ctx.DeviceConfig().VndkVersion() == "" {
+		return nil
+	}
+
+	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" {
+			variants = append(variants, VendorVariationPrefix+vndkVersion)
+		}
+	}
+
+	if ctx.DeviceConfig().ProductVndkVersion() == "" {
+		return variants
+	}
+
+	if Bool(g.Vendor_available) || ctx.ProductSpecific() {
+		variants = append(variants, ProductVariationPrefix+ctx.DeviceConfig().PlatformVndkVersion())
+		if vndkVersion := ctx.DeviceConfig().ProductVndkVersion(); vndkVersion != "current" {
+			variants = append(variants, ProductVariationPrefix+vndkVersion)
+		}
+	}
+
+	return variants
+}
+
+func (g *GenruleExtraProperties) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) {
+}
diff --git a/cc/genrule_test.go b/cc/genrule_test.go
index 92024ac..d38cf27 100644
--- a/cc/genrule_test.go
+++ b/cc/genrule_test.go
@@ -21,31 +21,20 @@
 	"android/soong/android"
 )
 
-func testGenruleContext(config android.Config, bp string,
-	fs map[string][]byte) *android.TestContext {
-
+func testGenruleContext(config android.Config) *android.TestContext {
 	ctx := android.NewTestArchContext()
-	ctx.RegisterModuleType("cc_genrule", android.ModuleFactoryAdaptor(genRuleFactory))
-	ctx.Register()
-
-	mockFS := map[string][]byte{
-		"Android.bp": []byte(bp),
-		"tool":       nil,
-		"foo":        nil,
-		"bar":        nil,
-	}
-
-	for k, v := range fs {
-		mockFS[k] = v
-	}
-
-	ctx.MockFileSystem(mockFS)
+	ctx.RegisterModuleType("cc_genrule", genRuleFactory)
+	ctx.Register(config)
 
 	return ctx
 }
 
 func TestArchGenruleCmd(t *testing.T) {
-	config := android.TestArchConfig(buildDir, nil)
+	fs := map[string][]byte{
+		"tool": nil,
+		"foo":  nil,
+		"bar":  nil,
+	}
 	bp := `
 				cc_genrule {
 					name: "gen",
@@ -63,8 +52,9 @@
 					},
 				}
 			`
+	config := android.TestArchConfig(buildDir, nil, bp, fs)
 
-	ctx := testGenruleContext(config, bp, nil)
+	ctx := testGenruleContext(config)
 
 	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
 	if errs == nil {
diff --git a/cc/installer.go b/cc/installer.go
index cb261b7..2f55ac5 100644
--- a/cc/installer.go
+++ b/cc/installer.go
@@ -52,7 +52,7 @@
 	relative string
 	location installLocation
 
-	path android.OutputPath
+	path android.InstallPath
 }
 
 var _ installer = (*baseInstaller)(nil)
@@ -61,16 +61,22 @@
 	return []interface{}{&installer.Properties}
 }
 
-func (installer *baseInstaller) installDir(ctx ModuleContext) android.OutputPath {
+func (installer *baseInstaller) installDir(ctx ModuleContext) android.InstallPath {
 	dir := installer.dir
 	if ctx.toolchain().Is64Bit() && installer.dir64 != "" {
 		dir = installer.dir64
 	}
-	if (!ctx.Host() && !ctx.Arch().Native) || ctx.Target().NativeBridge == android.NativeBridgeEnabled {
+	if ctx.Target().NativeBridge == android.NativeBridgeEnabled {
+		dir = filepath.Join(dir, ctx.Target().NativeBridgeRelativePath)
+	} else if !ctx.Host() && ctx.Config().HasMultilibConflict(ctx.Arch().ArchType) {
 		dir = filepath.Join(dir, ctx.Arch().ArchType.String())
 	}
 	if installer.location == InstallInData && ctx.useVndk() {
-		dir = filepath.Join(dir, "vendor")
+		if ctx.inProduct() {
+			dir = filepath.Join(dir, "product")
+		} else {
+			dir = filepath.Join(dir, "vendor")
+		}
 	}
 	return android.PathForModuleInstall(ctx, dir, installer.subDir,
 		installer.relativeInstallPath(), installer.relative)
diff --git a/cc/kernel_headers.go b/cc/kernel_headers.go
index fff419e..796de62 100644
--- a/cc/kernel_headers.go
+++ b/cc/kernel_headers.go
@@ -25,7 +25,7 @@
 func (stub *kernelHeadersDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps, objs Objects) android.Path {
 	if ctx.Device() {
 		f := &stub.libraryDecorator.flagExporter
-		f.reexportSystemDirs(ctx.DeviceConfig().DeviceKernelHeaderDirs()...)
+		f.reexportSystemDirs(android.PathsForSource(ctx, ctx.DeviceConfig().DeviceKernelHeaderDirs())...)
 	}
 	return stub.libraryDecorator.linkStatic(ctx, flags, deps, objs)
 }
diff --git a/cc/library.go b/cc/library.go
index f98cd36..ae95bc5 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -31,24 +31,7 @@
 	"android/soong/genrule"
 )
 
-type StaticSharedLibraryProperties struct {
-	Srcs   []string `android:"path,arch_variant"`
-	Cflags []string `android:"path,arch_variant"`
-
-	Enabled            *bool    `android:"arch_variant"`
-	Whole_static_libs  []string `android:"arch_variant"`
-	Static_libs        []string `android:"arch_variant"`
-	Shared_libs        []string `android:"arch_variant"`
-	System_shared_libs []string `android:"arch_variant"`
-
-	Export_shared_lib_headers []string `android:"arch_variant"`
-	Export_static_lib_headers []string `android:"arch_variant"`
-}
-
 type LibraryProperties struct {
-	Static StaticSharedLibraryProperties `android:"arch_variant"`
-	Shared StaticSharedLibraryProperties `android:"arch_variant"`
-
 	// local file name to pass to the linker as -unexported_symbols_list
 	Unexported_symbols_list *string `android:"path,arch_variant"`
 	// local file name to pass to the linker as -force_symbols_not_weak_list
@@ -88,6 +71,16 @@
 	// set the name of the output
 	Stem *string `android:"arch_variant"`
 
+	// set suffix of the name of the output
+	Suffix *string `android:"arch_variant"`
+
+	Target struct {
+		Vendor struct {
+			// set suffix of the name of the output
+			Suffix *string `android:"arch_variant"`
+		}
+	}
+
 	// Names of modules to be overridden. Listed modules can only be other shared libraries
 	// (in Make or Soong).
 	// This does not completely prevent installation of the overridden libraries, but if both
@@ -110,11 +103,39 @@
 		// Symbol tags that should be ignored from the symbol file
 		Exclude_symbol_tags []string
 	}
+
+	// Order symbols in .bss section by their sizes.  Only useful for shared libraries.
+	Sort_bss_symbols_by_size *bool
+
+	// Inject boringssl hash into the shared library.  This is only intended for use by external/boringssl.
+	Inject_bssl_hash *bool `android:"arch_variant"`
+}
+
+type StaticProperties struct {
+	Static StaticOrSharedProperties `android:"arch_variant"`
+}
+
+type SharedProperties struct {
+	Shared StaticOrSharedProperties `android:"arch_variant"`
+}
+
+type StaticOrSharedProperties struct {
+	Srcs   []string `android:"path,arch_variant"`
+	Cflags []string `android:"arch_variant"`
+
+	Enabled            *bool    `android:"arch_variant"`
+	Whole_static_libs  []string `android:"arch_variant"`
+	Static_libs        []string `android:"arch_variant"`
+	Shared_libs        []string `android:"arch_variant"`
+	System_shared_libs []string `android:"arch_variant"`
+
+	Export_shared_lib_headers []string `android:"arch_variant"`
+	Export_static_lib_headers []string `android:"arch_variant"`
+
+	Apex_available []string `android:"arch_variant"`
 }
 
 type LibraryMutatedProperties struct {
-	VariantName string `blueprint:"mutated"`
-
 	// Build a static variant
 	BuildStatic bool `blueprint:"mutated"`
 	// Build a shared variant
@@ -137,6 +158,10 @@
 	// listed in local_include_dirs.
 	Export_include_dirs []string `android:"arch_variant"`
 
+	// list of directories that will be added to the system include path
+	// using -isystem for this module and any module that links against this module.
+	Export_system_include_dirs []string `android:"arch_variant"`
+
 	Target struct {
 		Vendor struct {
 			// list of exported include directories, like
@@ -149,12 +174,16 @@
 }
 
 func init() {
-	android.RegisterModuleType("cc_library_static", LibraryStaticFactory)
-	android.RegisterModuleType("cc_library_shared", LibrarySharedFactory)
-	android.RegisterModuleType("cc_library", LibraryFactory)
-	android.RegisterModuleType("cc_library_host_static", LibraryHostStaticFactory)
-	android.RegisterModuleType("cc_library_host_shared", LibraryHostSharedFactory)
-	android.RegisterModuleType("cc_library_headers", LibraryHeaderFactory)
+	RegisterLibraryBuildComponents(android.InitRegistrationContext)
+}
+
+func RegisterLibraryBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("cc_library_static", LibraryStaticFactory)
+	ctx.RegisterModuleType("cc_library_shared", LibrarySharedFactory)
+	ctx.RegisterModuleType("cc_library", LibraryFactory)
+	ctx.RegisterModuleType("cc_library_host_static", LibraryHostStaticFactory)
+	ctx.RegisterModuleType("cc_library_host_shared", LibraryHostSharedFactory)
+	ctx.RegisterModuleType("cc_library_headers", LibraryHeaderFactory)
 }
 
 // cc_library creates both static and/or shared libraries for a device and/or
@@ -163,6 +192,11 @@
 // host.
 func LibraryFactory() android.Module {
 	module, _ := NewLibrary(android.HostAndDeviceSupported)
+	// Can be used as both a static and a shared library.
+	module.sdkMemberTypes = []android.SdkMemberType{
+		sharedLibrarySdkMemberType,
+		staticLibrarySdkMemberType,
+	}
 	return module.Init()
 }
 
@@ -170,6 +204,7 @@
 func LibraryStaticFactory() android.Module {
 	module, library := NewLibrary(android.HostAndDeviceSupported)
 	library.BuildOnlyStatic()
+	module.sdkMemberTypes = []android.SdkMemberType{staticLibrarySdkMemberType}
 	return module.Init()
 }
 
@@ -177,6 +212,7 @@
 func LibrarySharedFactory() android.Module {
 	module, library := NewLibrary(android.HostAndDeviceSupported)
 	library.BuildOnlyShared()
+	module.sdkMemberTypes = []android.SdkMemberType{sharedLibrarySdkMemberType}
 	return module.Init()
 }
 
@@ -185,6 +221,7 @@
 func LibraryHostStaticFactory() android.Module {
 	module, library := NewLibrary(android.HostSupported)
 	library.BuildOnlyStatic()
+	module.sdkMemberTypes = []android.SdkMemberType{staticLibrarySdkMemberType}
 	return module.Init()
 }
 
@@ -192,6 +229,7 @@
 func LibraryHostSharedFactory() android.Module {
 	module, library := NewLibrary(android.HostSupported)
 	library.BuildOnlyShared()
+	module.sdkMemberTypes = []android.SdkMemberType{sharedLibrarySdkMemberType}
 	return module.Init()
 }
 
@@ -208,10 +246,11 @@
 type flagExporter struct {
 	Properties FlagExporterProperties
 
-	dirs       []string
-	systemDirs []string
+	dirs       android.Paths
+	systemDirs android.Paths
 	flags      []string
 	deps       android.Paths
+	headers    android.Paths
 }
 
 func (f *flagExporter) exportedIncludes(ctx ModuleContext) android.Paths {
@@ -223,18 +262,21 @@
 }
 
 func (f *flagExporter) exportIncludes(ctx ModuleContext) {
-	f.dirs = append(f.dirs, f.exportedIncludes(ctx).Strings()...)
+	f.dirs = append(f.dirs, f.exportedIncludes(ctx)...)
+	f.systemDirs = append(f.systemDirs, android.PathsForModuleSrc(ctx, f.Properties.Export_system_include_dirs)...)
 }
 
 func (f *flagExporter) exportIncludesAsSystem(ctx ModuleContext) {
-	f.systemDirs = append(f.systemDirs, f.exportedIncludes(ctx).Strings()...)
+	// all dirs are force exported as system
+	f.systemDirs = append(f.systemDirs, f.exportedIncludes(ctx)...)
+	f.systemDirs = append(f.systemDirs, android.PathsForModuleSrc(ctx, f.Properties.Export_system_include_dirs)...)
 }
 
-func (f *flagExporter) reexportDirs(dirs ...string) {
+func (f *flagExporter) reexportDirs(dirs ...android.Path) {
 	f.dirs = append(f.dirs, dirs...)
 }
 
-func (f *flagExporter) reexportSystemDirs(dirs ...string) {
+func (f *flagExporter) reexportSystemDirs(dirs ...android.Path) {
 	f.systemDirs = append(f.systemDirs, dirs...)
 }
 
@@ -252,11 +294,17 @@
 	f.deps = append(f.deps, deps...)
 }
 
-func (f *flagExporter) exportedDirs() []string {
+// addExportedGeneratedHeaders does nothing but collects generated header files.
+// This can be differ to exportedDeps which may contain phony files to minimize ninja.
+func (f *flagExporter) addExportedGeneratedHeaders(headers ...android.Path) {
+	f.headers = append(f.headers, headers...)
+}
+
+func (f *flagExporter) exportedDirs() android.Paths {
 	return f.dirs
 }
 
-func (f *flagExporter) exportedSystemDirs() []string {
+func (f *flagExporter) exportedSystemDirs() android.Paths {
 	return f.systemDirs
 }
 
@@ -268,11 +316,16 @@
 	return f.deps
 }
 
+func (f *flagExporter) exportedGeneratedHeaders() android.Paths {
+	return f.headers
+}
+
 type exportedFlagsProducer interface {
-	exportedDirs() []string
-	exportedSystemDirs() []string
+	exportedDirs() android.Paths
+	exportedSystemDirs() android.Paths
 	exportedFlags() []string
 	exportedDeps() android.Paths
+	exportedGeneratedHeaders() android.Paths
 }
 
 var _ exportedFlagsProducer = (*flagExporter)(nil)
@@ -281,6 +334,8 @@
 // functionality: static vs. shared linkage, reusing object files for shared libraries
 type libraryDecorator struct {
 	Properties        LibraryProperties
+	StaticProperties  StaticProperties
+	SharedProperties  SharedProperties
 	MutatedProperties LibraryMutatedProperties
 
 	// For reusing static library objects for shared library
@@ -341,11 +396,20 @@
 func (library *libraryDecorator) linkerProps() []interface{} {
 	var props []interface{}
 	props = append(props, library.baseLinker.linkerProps()...)
-	return append(props,
+	props = append(props,
 		&library.Properties,
 		&library.MutatedProperties,
 		&library.flagExporter.Properties,
 		&library.stripper.StripProperties)
+
+	if library.MutatedProperties.BuildShared {
+		props = append(props, &library.SharedProperties)
+	}
+	if library.MutatedProperties.BuildStatic {
+		props = append(props, &library.StaticProperties)
+	}
+
+	return props
 }
 
 func (library *libraryDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
@@ -355,13 +419,13 @@
 	// all code is position independent, and then those warnings get promoted to
 	// errors.
 	if !ctx.Windows() {
-		flags.CFlags = append(flags.CFlags, "-fPIC")
+		flags.Global.CFlags = append(flags.Global.CFlags, "-fPIC")
 	}
 
 	if library.static() {
-		flags.CFlags = append(flags.CFlags, library.Properties.Static.Cflags...)
+		flags.Local.CFlags = append(flags.Local.CFlags, library.StaticProperties.Static.Cflags...)
 	} else if library.shared() {
-		flags.CFlags = append(flags.CFlags, library.Properties.Shared.Cflags...)
+		flags.Local.CFlags = append(flags.Local.CFlags, library.SharedProperties.Shared.Cflags...)
 	}
 
 	if library.shared() {
@@ -392,7 +456,7 @@
 			}
 		}
 
-		flags.LdFlags = append(f, flags.LdFlags...)
+		flags.Global.LdFlags = append(flags.Global.LdFlags, f...)
 	}
 
 	return flags
@@ -402,8 +466,8 @@
 	exportIncludeDirs := library.flagExporter.exportedIncludes(ctx)
 	if len(exportIncludeDirs) > 0 {
 		f := includeDirsToFlags(exportIncludeDirs)
-		flags.GlobalFlags = append(flags.GlobalFlags, f)
-		flags.YasmFlags = append(flags.YasmFlags, f)
+		flags.Local.CommonFlags = append(flags.Local.CommonFlags, f)
+		flags.Local.YasmFlags = append(flags.Local.YasmFlags, f)
 	}
 
 	flags = library.baseCompiler.compilerFlags(ctx, flags, deps)
@@ -423,19 +487,67 @@
 			}
 			return ret
 		}
-		flags.GlobalFlags = removeInclude(flags.GlobalFlags)
-		flags.CFlags = removeInclude(flags.CFlags)
+		flags.Local.CommonFlags = removeInclude(flags.Local.CommonFlags)
+		flags.Local.CFlags = removeInclude(flags.Local.CFlags)
 
 		flags = addStubLibraryCompilerFlags(flags)
 	}
 	return flags
 }
 
-func (library *libraryDecorator) shouldCreateVndkSourceAbiDump(ctx ModuleContext) bool {
-	if library.Properties.Header_abi_checker.Enabled != nil {
-		return Bool(library.Properties.Header_abi_checker.Enabled)
+// Returns a string that represents the class of the ABI dump.
+// Returns an empty string if ABI check is disabled for this library.
+func (library *libraryDecorator) classifySourceAbiDump(ctx ModuleContext) string {
+	enabled := library.Properties.Header_abi_checker.Enabled
+	if enabled != nil && !Bool(enabled) {
+		return ""
 	}
-	return ctx.shouldCreateVndkSourceAbiDump(ctx.Config())
+	// Return NDK if the library is both NDK and LLNDK.
+	if ctx.isNdk() {
+		return "NDK"
+	}
+	if ctx.isLlndkPublic(ctx.Config()) {
+		return "LLNDK"
+	}
+	if ctx.useVndk() && ctx.isVndk() && !ctx.isVndkPrivate(ctx.Config()) {
+		if ctx.isVndkSp() {
+			if ctx.isVndkExt() {
+				return "VNDK-SP-ext"
+			} else {
+				return "VNDK-SP"
+			}
+		} else {
+			if ctx.isVndkExt() {
+				return "VNDK-ext"
+			} else {
+				return "VNDK-core"
+			}
+		}
+	}
+	if Bool(enabled) || ctx.hasStubsVariants() {
+		return "PLATFORM"
+	}
+	return ""
+}
+
+func (library *libraryDecorator) shouldCreateSourceAbiDump(ctx ModuleContext) bool {
+	if !ctx.shouldCreateSourceAbiDump() {
+		return false
+	}
+	if !ctx.isForPlatform() {
+		if !ctx.hasStubsVariants() {
+			// Skip ABI checks if this library is for APEX but isn't exported.
+			return false
+		}
+		if !Bool(library.Properties.Header_abi_checker.Enabled) {
+			// Skip ABI checks if this library is for APEX and did not explicitly enable
+			// ABI checks.
+			// TODO(b/145608479): ABI checks should be enabled by default. Remove this
+			// after evaluating the extra build time.
+			return false
+		}
+	}
+	return library.classifySourceAbiDump(ctx) != ""
 }
 
 func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects {
@@ -449,15 +561,15 @@
 		if len(library.baseCompiler.Properties.Srcs) > 0 {
 			ctx.PropertyErrorf("srcs", "cc_library_headers must not have any srcs")
 		}
-		if len(library.Properties.Static.Srcs) > 0 {
+		if len(library.StaticProperties.Static.Srcs) > 0 {
 			ctx.PropertyErrorf("static.srcs", "cc_library_headers must not have any srcs")
 		}
-		if len(library.Properties.Shared.Srcs) > 0 {
+		if len(library.SharedProperties.Shared.Srcs) > 0 {
 			ctx.PropertyErrorf("shared.srcs", "cc_library_headers must not have any srcs")
 		}
 		return Objects{}
 	}
-	if library.shouldCreateVndkSourceAbiDump(ctx) || library.sabi.Properties.CreateSAbiDumps {
+	if library.shouldCreateSourceAbiDump(ctx) || library.sabi.Properties.CreateSAbiDumps {
 		exportIncludeDirs := library.flagExporter.exportedIncludes(ctx)
 		var SourceAbiFlags []string
 		for _, dir := range exportIncludeDirs.Strings() {
@@ -467,8 +579,8 @@
 			SourceAbiFlags = append(SourceAbiFlags, "-I"+reexportedInclude)
 		}
 		flags.SAbiFlags = SourceAbiFlags
-		total_length := len(library.baseCompiler.Properties.Srcs) + len(deps.GeneratedSources) + len(library.Properties.Shared.Srcs) +
-			len(library.Properties.Static.Srcs)
+		total_length := len(library.baseCompiler.Properties.Srcs) + len(deps.GeneratedSources) +
+			len(library.SharedProperties.Shared.Srcs) + len(library.StaticProperties.Static.Srcs)
 		if total_length > 0 {
 			flags.SAbiDump = true
 		}
@@ -478,11 +590,11 @@
 	buildFlags := flagsToBuilderFlags(flags)
 
 	if library.static() {
-		srcs := android.PathsForModuleSrc(ctx, library.Properties.Static.Srcs)
+		srcs := android.PathsForModuleSrc(ctx, library.StaticProperties.Static.Srcs)
 		objs = objs.Append(compileObjs(ctx, buildFlags, android.DeviceStaticLibrary,
 			srcs, library.baseCompiler.pathDeps, library.baseCompiler.cFlagsDeps))
 	} else if library.shared() {
-		srcs := android.PathsForModuleSrc(ctx, library.Properties.Shared.Srcs)
+		srcs := android.PathsForModuleSrc(ctx, library.SharedProperties.Shared.Srcs)
 		objs = objs.Append(compileObjs(ctx, buildFlags, android.DeviceSharedLibrary,
 			srcs, library.baseCompiler.pathDeps, library.baseCompiler.cFlagsDeps))
 	}
@@ -493,6 +605,7 @@
 type libraryInterface interface {
 	getWholeStaticMissingDeps() []string
 	static() bool
+	shared() bool
 	objs() Objects
 	reuseObjs() (Objects, exportedFlagsProducer)
 	toc() android.OptionalPath
@@ -507,19 +620,39 @@
 
 	// Write LOCAL_ADDITIONAL_DEPENDENCIES for ABI diff
 	androidMkWriteAdditionalDependenciesForSourceAbiDiff(w io.Writer)
+
+	availableFor(string) bool
 }
 
-func (library *libraryDecorator) getLibName(ctx ModuleContext) string {
+func (library *libraryDecorator) getLibNameHelper(baseModuleName string, useVndk bool) string {
 	name := library.libName
 	if name == "" {
 		name = String(library.Properties.Stem)
 		if name == "" {
-			name = ctx.baseModuleName()
+			name = baseModuleName
 		}
 	}
 
+	suffix := ""
+	if useVndk {
+		suffix = String(library.Properties.Target.Vendor.Suffix)
+	}
+	if suffix == "" {
+		suffix = String(library.Properties.Suffix)
+	}
+
+	return name + suffix
+}
+
+func (library *libraryDecorator) getLibName(ctx BaseModuleContext) string {
+	name := library.getLibNameHelper(ctx.baseModuleName(), ctx.useVndk())
+
 	if ctx.isVndkExt() {
-		name = ctx.getVndkExtendsModuleName()
+		// vndk-ext lib should have the same name with original lib
+		ctx.VisitDirectDepsWithTag(vndkExtDepTag, func(module android.Module) {
+			originalName := module.(*Module).outputFile.Path()
+			name = strings.TrimSuffix(originalName.Base(), originalName.Ext())
+		})
 	}
 
 	if ctx.Host() && Bool(library.Properties.Unique_host_soname) {
@@ -528,7 +661,7 @@
 		}
 	}
 
-	return name + library.MutatedProperties.VariantName
+	return name
 }
 
 var versioningMacroNamesListMutex sync.Mutex
@@ -565,12 +698,12 @@
 
 func (library *libraryDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps {
 	if library.static() {
-		if library.Properties.Static.System_shared_libs != nil {
-			library.baseLinker.Properties.System_shared_libs = library.Properties.Static.System_shared_libs
+		if library.StaticProperties.Static.System_shared_libs != nil {
+			library.baseLinker.Properties.System_shared_libs = library.StaticProperties.Static.System_shared_libs
 		}
 	} else if library.shared() {
-		if library.Properties.Shared.System_shared_libs != nil {
-			library.baseLinker.Properties.System_shared_libs = library.Properties.Shared.System_shared_libs
+		if library.SharedProperties.Shared.System_shared_libs != nil {
+			library.baseLinker.Properties.System_shared_libs = library.SharedProperties.Shared.System_shared_libs
 		}
 	}
 
@@ -578,12 +711,12 @@
 
 	if library.static() {
 		deps.WholeStaticLibs = append(deps.WholeStaticLibs,
-			library.Properties.Static.Whole_static_libs...)
-		deps.StaticLibs = append(deps.StaticLibs, library.Properties.Static.Static_libs...)
-		deps.SharedLibs = append(deps.SharedLibs, library.Properties.Static.Shared_libs...)
+			library.StaticProperties.Static.Whole_static_libs...)
+		deps.StaticLibs = append(deps.StaticLibs, library.StaticProperties.Static.Static_libs...)
+		deps.SharedLibs = append(deps.SharedLibs, library.StaticProperties.Static.Shared_libs...)
 
-		deps.ReexportSharedLibHeaders = append(deps.ReexportSharedLibHeaders, library.Properties.Static.Export_shared_lib_headers...)
-		deps.ReexportStaticLibHeaders = append(deps.ReexportStaticLibHeaders, library.Properties.Static.Export_static_lib_headers...)
+		deps.ReexportSharedLibHeaders = append(deps.ReexportSharedLibHeaders, library.StaticProperties.Static.Export_shared_lib_headers...)
+		deps.ReexportStaticLibHeaders = append(deps.ReexportStaticLibHeaders, library.StaticProperties.Static.Export_static_lib_headers...)
 	} else if library.shared() {
 		if ctx.toolchain().Bionic() && !Bool(library.baseLinker.Properties.Nocrt) {
 			if !ctx.useSdk() {
@@ -602,12 +735,12 @@
 				deps.CrtEnd = "ndk_crtend_so." + version
 			}
 		}
-		deps.WholeStaticLibs = append(deps.WholeStaticLibs, library.Properties.Shared.Whole_static_libs...)
-		deps.StaticLibs = append(deps.StaticLibs, library.Properties.Shared.Static_libs...)
-		deps.SharedLibs = append(deps.SharedLibs, library.Properties.Shared.Shared_libs...)
+		deps.WholeStaticLibs = append(deps.WholeStaticLibs, library.SharedProperties.Shared.Whole_static_libs...)
+		deps.StaticLibs = append(deps.StaticLibs, library.SharedProperties.Shared.Static_libs...)
+		deps.SharedLibs = append(deps.SharedLibs, library.SharedProperties.Shared.Shared_libs...)
 
-		deps.ReexportSharedLibHeaders = append(deps.ReexportSharedLibHeaders, library.Properties.Shared.Export_shared_lib_headers...)
-		deps.ReexportStaticLibHeaders = append(deps.ReexportStaticLibHeaders, library.Properties.Shared.Export_static_lib_headers...)
+		deps.ReexportSharedLibHeaders = append(deps.ReexportSharedLibHeaders, library.SharedProperties.Shared.Export_shared_lib_headers...)
+		deps.ReexportStaticLibHeaders = append(deps.ReexportStaticLibHeaders, library.SharedProperties.Shared.Export_static_lib_headers...)
 	}
 	if ctx.useVndk() {
 		deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, library.baseLinker.Properties.Target.Vendor.Exclude_static_libs)
@@ -633,7 +766,7 @@
 	library.objects = deps.WholeStaticLibObjs.Copy()
 	library.objects = library.objects.Append(objs)
 
-	fileName := ctx.ModuleName() + library.MutatedProperties.VariantName + staticLibraryExtension
+	fileName := ctx.ModuleName() + staticLibraryExtension
 	outputFile := android.PathForModuleOut(ctx, fileName)
 	builderFlags := flagsToBuilderFlags(flags)
 
@@ -651,8 +784,7 @@
 
 	TransformObjToStaticLib(ctx, library.objects.objFiles, builderFlags, outputFile, objs.tidyFiles)
 
-	library.coverageOutputFile = TransformCoverageFilesToZip(ctx, library.objects,
-		ctx.ModuleName()+library.MutatedProperties.VariantName)
+	library.coverageOutputFile = TransformCoverageFilesToZip(ctx, library.objects, ctx.ModuleName())
 
 	library.wholeStaticMissingDeps = ctx.GetMissingDependencies()
 
@@ -682,21 +814,21 @@
 		}
 	} else {
 		if unexportedSymbols.Valid() {
-			flags.LdFlags = append(flags.LdFlags, "-Wl,-unexported_symbols_list,"+unexportedSymbols.String())
+			flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-unexported_symbols_list,"+unexportedSymbols.String())
 			linkerDeps = append(linkerDeps, unexportedSymbols.Path())
 		}
 		if forceNotWeakSymbols.Valid() {
-			flags.LdFlags = append(flags.LdFlags, "-Wl,-force_symbols_not_weak_list,"+forceNotWeakSymbols.String())
+			flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-force_symbols_not_weak_list,"+forceNotWeakSymbols.String())
 			linkerDeps = append(linkerDeps, forceNotWeakSymbols.Path())
 		}
 		if forceWeakSymbols.Valid() {
-			flags.LdFlags = append(flags.LdFlags, "-Wl,-force_symbols_weak_list,"+forceWeakSymbols.String())
+			flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-force_symbols_weak_list,"+forceWeakSymbols.String())
 			linkerDeps = append(linkerDeps, forceWeakSymbols.Path())
 		}
 	}
 	if library.buildStubs() {
 		linkerScriptFlags := "-Wl,--version-script," + library.versionScriptPath.String()
-		flags.LdFlags = append(flags.LdFlags, linkerScriptFlags)
+		flags.Local.LdFlags = append(flags.Local.LdFlags, linkerScriptFlags)
 		linkerDeps = append(linkerDeps, library.versionScriptPath)
 	}
 
@@ -708,7 +840,7 @@
 	if ctx.Windows() {
 		importLibraryPath := android.PathForModuleOut(ctx, pathtools.ReplaceExtension(fileName, "lib"))
 
-		flags.LdFlags = append(flags.LdFlags, "-Wl,--out-implib="+importLibraryPath.String())
+		flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--out-implib="+importLibraryPath.String())
 		implicitOutputs = append(implicitOutputs, importLibraryPath)
 	}
 
@@ -716,9 +848,7 @@
 
 	// Optimize out relinking against shared libraries whose interface hasn't changed by
 	// depending on a table of contents file instead of the library itself.
-	tocPath := outputFile.RelPathString()
-	tocPath = pathtools.ReplaceExtension(tocPath, flags.Toolchain.ShlibSuffix()[1:]+".toc")
-	tocFile := android.PathForOutput(ctx, tocPath)
+	tocFile := outputFile.ReplaceExtension(ctx, flags.Toolchain.ShlibSuffix()[1:]+".toc")
 	library.tocFile = android.OptionalPathForPath(tocFile)
 	TransformSharedObjectToToc(ctx, outputFile, tocFile, builderFlags)
 
@@ -730,9 +860,10 @@
 		outputFile = android.PathForModuleOut(ctx, "unstripped", fileName)
 		library.stripper.stripExecutableOrSharedLib(ctx, outputFile, strippedOutputFile, builderFlags)
 	}
-
 	library.unstrippedOutputFile = outputFile
 
+	outputFile = maybeInjectBoringSSLHash(ctx, outputFile, library.Properties.Inject_bssl_hash, fileName)
+
 	if Bool(library.baseLinker.Properties.Use_version_lib) {
 		if ctx.Host() {
 			versionedOutputFile := outputFile
@@ -761,6 +892,18 @@
 	linkerDeps = append(linkerDeps, deps.LateSharedLibsDeps...)
 	linkerDeps = append(linkerDeps, objs.tidyFiles...)
 
+	if Bool(library.Properties.Sort_bss_symbols_by_size) {
+		unsortedOutputFile := android.PathForModuleOut(ctx, "unsorted", fileName)
+		TransformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs,
+			deps.StaticLibs, deps.LateStaticLibs, deps.WholeStaticLibs,
+			linkerDeps, deps.CrtBegin, deps.CrtEnd, false, builderFlags, unsortedOutputFile, implicitOutputs)
+
+		symbolOrderingFile := android.PathForModuleOut(ctx, "unsorted", fileName+".symbol_order")
+		symbolOrderingFlag := library.baseLinker.sortBssSymbolsBySize(ctx, unsortedOutputFile, symbolOrderingFile, builderFlags)
+		builderFlags.localLdFlags += " " + symbolOrderingFlag
+		linkerDeps = append(linkerDeps, symbolOrderingFile)
+	}
+
 	TransformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs,
 		deps.StaticLibs, deps.LateStaticLibs, deps.WholeStaticLibs,
 		linkerDeps, deps.CrtBegin, deps.CrtEnd, false, builderFlags, outputFile, implicitOutputs)
@@ -788,11 +931,16 @@
 	return true
 }
 
-func getRefAbiDumpFile(ctx ModuleContext, vndkVersion, fileName string) android.Path {
-	isLlndkOrNdk := inList(ctx.baseModuleName(), *llndkLibraries(ctx.Config())) || inList(ctx.baseModuleName(), ndkMigratedLibs)
+func (library *libraryDecorator) coverageOutputFilePath() android.OptionalPath {
+	return library.coverageOutputFile
+}
 
-	refAbiDumpTextFile := android.PathForVndkRefAbiDump(ctx, vndkVersion, fileName, isLlndkOrNdk, ctx.isVndk(), false)
-	refAbiDumpGzipFile := android.PathForVndkRefAbiDump(ctx, vndkVersion, fileName, isLlndkOrNdk, ctx.isVndk(), true)
+func getRefAbiDumpFile(ctx ModuleContext, vndkVersion, fileName string) android.Path {
+	isNdk := ctx.isNdk()
+	isLlndkOrVndk := ctx.isLlndkPublic(ctx.Config()) || ctx.isVndk()
+
+	refAbiDumpTextFile := android.PathForVndkRefAbiDump(ctx, vndkVersion, fileName, isNdk, isLlndkOrVndk, false)
+	refAbiDumpGzipFile := android.PathForVndkRefAbiDump(ctx, vndkVersion, fileName, isNdk, isLlndkOrVndk, true)
 
 	if refAbiDumpTextFile.Valid() {
 		if refAbiDumpGzipFile.Valid() {
@@ -810,7 +958,7 @@
 }
 
 func (library *libraryDecorator) linkSAbiDumpFiles(ctx ModuleContext, objs Objects, fileName string, soFile android.Path) {
-	if len(objs.sAbiDumpFiles) > 0 && library.shouldCreateVndkSourceAbiDump(ctx) {
+	if library.shouldCreateSourceAbiDump(ctx) {
 		vndkVersion := ctx.DeviceConfig().PlatformVndkVersion()
 		if ver := ctx.DeviceConfig().VndkVersion(); ver != "" && ver != "current" {
 			vndkVersion = ver
@@ -826,10 +974,12 @@
 		}
 		exportedHeaderFlags := strings.Join(SourceAbiFlags, " ")
 		library.sAbiOutputFile = TransformDumpToLinkedDump(ctx, objs.sAbiDumpFiles, soFile, fileName, exportedHeaderFlags,
-			android.OptionalPathForModuleSrc(ctx, library.Properties.Header_abi_checker.Symbol_file),
+			android.OptionalPathForModuleSrc(ctx, library.symbolFileForAbiCheck(ctx)),
 			library.Properties.Header_abi_checker.Exclude_symbol_versions,
 			library.Properties.Header_abi_checker.Exclude_symbol_tags)
 
+		addLsdumpPath(library.classifySourceAbiDump(ctx) + ":" + library.sAbiOutputFile.String())
+
 		refAbiDumpFile := getRefAbiDumpFile(ctx, vndkVersion, fileName)
 		if refAbiDumpFile != nil {
 			library.sAbiDiff = SourceAbiDiff(ctx, library.sAbiOutputFile.Path(),
@@ -854,43 +1004,49 @@
 	library.reexportSystemDirs(deps.ReexportedSystemDirs...)
 	library.reexportFlags(deps.ReexportedFlags...)
 	library.reexportDeps(deps.ReexportedDeps...)
+	library.addExportedGeneratedHeaders(deps.ReexportedGeneratedHeaders...)
 
 	if Bool(library.Properties.Aidl.Export_aidl_headers) {
 		if library.baseCompiler.hasSrcExt(".aidl") {
-			dir := android.PathForModuleGen(ctx, "aidl").String()
+			dir := android.PathForModuleGen(ctx, "aidl")
 			library.reexportDirs(dir)
-			library.reexportDeps(library.baseCompiler.pathDeps...) // TODO: restrict to aidl deps
+
+			// TODO: restrict to aidl deps
+			library.reexportDeps(library.baseCompiler.pathDeps...)
+			library.addExportedGeneratedHeaders(library.baseCompiler.pathDeps...)
 		}
 	}
 
 	if Bool(library.Properties.Proto.Export_proto_headers) {
 		if library.baseCompiler.hasSrcExt(".proto") {
-			includes := []string{}
+			var includes android.Paths
 			if flags.proto.CanonicalPathFromRoot {
-				includes = append(includes, flags.proto.SubDir.String())
+				includes = append(includes, flags.proto.SubDir)
 			}
-			includes = append(includes, flags.proto.Dir.String())
+			includes = append(includes, flags.proto.Dir)
 			library.reexportDirs(includes...)
-			library.reexportDeps(library.baseCompiler.pathDeps...) // TODO: restrict to proto deps
+
+			// TODO: restrict to proto deps
+			library.reexportDeps(library.baseCompiler.pathDeps...)
+			library.addExportedGeneratedHeaders(library.baseCompiler.pathDeps...)
 		}
 	}
 
 	if library.baseCompiler.hasSrcExt(".sysprop") {
-		dir := android.PathForModuleGen(ctx, "sysprop", "include").String()
+		dir := android.PathForModuleGen(ctx, "sysprop", "include")
 		if library.Properties.Sysprop.Platform != nil {
 			isProduct := ctx.ProductSpecific() && !ctx.useVndk()
 			isVendor := ctx.useVndk()
 			isOwnerPlatform := Bool(library.Properties.Sysprop.Platform)
 
-			usePublic := isProduct || (isOwnerPlatform == isVendor)
-
-			if usePublic {
-				dir = android.PathForModuleGen(ctx, "sysprop/public", "include").String()
+			if !ctx.inRecovery() && (isProduct || (isOwnerPlatform == isVendor)) {
+				dir = android.PathForModuleGen(ctx, "sysprop/public", "include")
 			}
 		}
 
 		library.reexportDirs(dir)
 		library.reexportDeps(library.baseCompiler.pathDeps...)
+		library.addExportedGeneratedHeaders(library.baseCompiler.pathDeps...)
 	}
 
 	if library.buildStubs() {
@@ -901,11 +1057,13 @@
 }
 
 func (library *libraryDecorator) buildStatic() bool {
-	return library.MutatedProperties.BuildStatic && BoolDefault(library.Properties.Static.Enabled, true)
+	return library.MutatedProperties.BuildStatic &&
+		BoolDefault(library.StaticProperties.Static.Enabled, true)
 }
 
 func (library *libraryDecorator) buildShared() bool {
-	return library.MutatedProperties.BuildShared && BoolDefault(library.Properties.Shared.Enabled, true)
+	return library.MutatedProperties.BuildShared &&
+		BoolDefault(library.SharedProperties.Shared.Enabled, true)
 }
 
 func (library *libraryDecorator) getWholeStaticMissingDeps() []string {
@@ -955,13 +1113,18 @@
 			// Bionic libraries (e.g. libc.so) is installed to the bootstrap subdirectory.
 			// The original path becomes a symlink to the corresponding file in the
 			// runtime APEX.
-			if installToBootstrap(ctx.baseModuleName(), ctx.Config()) && !library.buildStubs() && ctx.Arch().Native && !ctx.inRecovery() {
-				if ctx.Device() && isBionic(ctx.baseModuleName()) {
+			translatedArch := ctx.Target().NativeBridge == android.NativeBridgeEnabled
+			if InstallToBootstrap(ctx.baseModuleName(), ctx.Config()) && !library.buildStubs() && !translatedArch && !ctx.inRecovery() {
+				if ctx.Device() {
 					library.installSymlinkToRuntimeApex(ctx, file)
 				}
 				library.baseInstaller.subDir = "bootstrap"
 			}
+		} else if android.DirectlyInAnyApex(ctx, ctx.ModuleName()) && ctx.isLlndk(ctx.Config()) && !isBionic(ctx.baseModuleName()) {
+			// Skip installing LLNDK (non-bionic) libraries moved to APEX.
+			ctx.Module().SkipInstall()
 		}
+
 		library.baseInstaller.install(ctx, file)
 	}
 
@@ -1022,10 +1185,33 @@
 	return library.MutatedProperties.BuildStubs
 }
 
+func (library *libraryDecorator) symbolFileForAbiCheck(ctx ModuleContext) *string {
+	if library.Properties.Header_abi_checker.Symbol_file != nil {
+		return library.Properties.Header_abi_checker.Symbol_file
+	}
+	if ctx.hasStubsVariants() && library.Properties.Stubs.Symbol_file != nil {
+		return library.Properties.Stubs.Symbol_file
+	}
+	return nil
+}
+
 func (library *libraryDecorator) stubsVersion() string {
 	return library.MutatedProperties.StubsVersion
 }
 
+func (library *libraryDecorator) availableFor(what string) bool {
+	var list []string
+	if library.static() {
+		list = library.StaticProperties.Static.Apex_available
+	} else if library.shared() {
+		list = library.SharedProperties.Shared.Apex_available
+	}
+	if len(list) == 0 {
+		return false
+	}
+	return android.CheckAvailableForApex(what, list)
+}
+
 var versioningMacroNamesListKey = android.NewOnceKey("versioningMacroNamesList")
 
 func versioningMacroNamesList(config android.Config) *map[string]string {
@@ -1074,16 +1260,16 @@
 
 		// Check libraries in addition to cflags, since libraries may be exporting different
 		// include directories.
-		if len(staticCompiler.Properties.Static.Cflags) == 0 &&
-			len(sharedCompiler.Properties.Shared.Cflags) == 0 &&
-			len(staticCompiler.Properties.Static.Whole_static_libs) == 0 &&
-			len(sharedCompiler.Properties.Shared.Whole_static_libs) == 0 &&
-			len(staticCompiler.Properties.Static.Static_libs) == 0 &&
-			len(sharedCompiler.Properties.Shared.Static_libs) == 0 &&
-			len(staticCompiler.Properties.Static.Shared_libs) == 0 &&
-			len(sharedCompiler.Properties.Shared.Shared_libs) == 0 &&
-			staticCompiler.Properties.Static.System_shared_libs == nil &&
-			sharedCompiler.Properties.Shared.System_shared_libs == nil {
+		if len(staticCompiler.StaticProperties.Static.Cflags) == 0 &&
+			len(sharedCompiler.SharedProperties.Shared.Cflags) == 0 &&
+			len(staticCompiler.StaticProperties.Static.Whole_static_libs) == 0 &&
+			len(sharedCompiler.SharedProperties.Shared.Whole_static_libs) == 0 &&
+			len(staticCompiler.StaticProperties.Static.Static_libs) == 0 &&
+			len(sharedCompiler.SharedProperties.Shared.Static_libs) == 0 &&
+			len(staticCompiler.StaticProperties.Static.Shared_libs) == 0 &&
+			len(sharedCompiler.SharedProperties.Shared.Shared_libs) == 0 &&
+			staticCompiler.StaticProperties.Static.System_shared_libs == nil &&
+			sharedCompiler.SharedProperties.Shared.System_shared_libs == nil {
 
 			mctx.AddInterVariantDependency(reuseObjTag, shared, static)
 			sharedCompiler.baseCompiler.Properties.OriginalSrcs =
@@ -1098,44 +1284,62 @@
 }
 
 func LinkageMutator(mctx android.BottomUpMutatorContext) {
+	cc_prebuilt := false
 	if m, ok := mctx.Module().(*Module); ok && m.linker != nil {
-		switch library := m.linker.(type) {
-		case prebuiltLibraryInterface:
-			// Always create both the static and shared variants for prebuilt libraries, and then disable the one
-			// that is not being used.  This allows them to share the name of a cc_library module, which requires that
-			// all the variants of the cc_library also exist on the prebuilt.
-			modules := mctx.CreateLocalVariations("static", "shared")
-			static := modules[0].(*Module)
-			shared := modules[1].(*Module)
+		_, cc_prebuilt = m.linker.(prebuiltLibraryInterface)
+	}
+	if cc_prebuilt {
+		library := mctx.Module().(*Module).linker.(prebuiltLibraryInterface)
 
-			static.linker.(prebuiltLibraryInterface).setStatic()
-			shared.linker.(prebuiltLibraryInterface).setShared()
+		// Always create both the static and shared variants for prebuilt libraries, and then disable the one
+		// that is not being used.  This allows them to share the name of a cc_library module, which requires that
+		// all the variants of the cc_library also exist on the prebuilt.
+		modules := mctx.CreateLocalVariations("static", "shared")
+		static := modules[0].(*Module)
+		shared := modules[1].(*Module)
 
-			if !library.buildStatic() {
-				static.linker.(prebuiltLibraryInterface).disablePrebuilt()
+		static.linker.(prebuiltLibraryInterface).setStatic()
+		shared.linker.(prebuiltLibraryInterface).setShared()
+
+		if !library.buildStatic() {
+			static.linker.(prebuiltLibraryInterface).disablePrebuilt()
+		}
+		if !library.buildShared() {
+			shared.linker.(prebuiltLibraryInterface).disablePrebuilt()
+		}
+	} else if library, ok := mctx.Module().(LinkableInterface); ok && library.CcLibraryInterface() {
+
+		// Non-cc.Modules may need an empty variant for their mutators.
+		variations := []string{}
+		if library.NonCcVariants() {
+			variations = append(variations, "")
+		}
+
+		if library.BuildStaticVariant() && library.BuildSharedVariant() {
+			variations := append([]string{"static", "shared"}, variations...)
+
+			modules := mctx.CreateLocalVariations(variations...)
+			static := modules[0].(LinkableInterface)
+			shared := modules[1].(LinkableInterface)
+
+			static.SetStatic()
+			shared.SetShared()
+
+			if _, ok := library.(*Module); ok {
+				reuseStaticLibrary(mctx, static.(*Module), shared.(*Module))
 			}
-			if !library.buildShared() {
-				shared.linker.(prebuiltLibraryInterface).disablePrebuilt()
-			}
+		} else if library.BuildStaticVariant() {
+			variations := append([]string{"static"}, variations...)
 
-		case libraryInterface:
-			if library.buildStatic() && library.buildShared() {
-				modules := mctx.CreateLocalVariations("static", "shared")
-				static := modules[0].(*Module)
-				shared := modules[1].(*Module)
+			modules := mctx.CreateLocalVariations(variations...)
+			modules[0].(LinkableInterface).SetStatic()
+		} else if library.BuildSharedVariant() {
+			variations := append([]string{"shared"}, variations...)
 
-				static.linker.(libraryInterface).setStatic()
-				shared.linker.(libraryInterface).setShared()
-
-				reuseStaticLibrary(mctx, static, shared)
-
-			} else if library.buildStatic() {
-				modules := mctx.CreateLocalVariations("static")
-				modules[0].(*Module).linker.(libraryInterface).setStatic()
-			} else if library.buildShared() {
-				modules := mctx.CreateLocalVariations("shared")
-				modules[0].(*Module).linker.(libraryInterface).setShared()
-			}
+			modules := mctx.CreateLocalVariations(variations...)
+			modules[0].(LinkableInterface).SetShared()
+		} else if len(variations) > 0 {
+			mctx.CreateLocalVariations(variations...)
 		}
 	}
 }
@@ -1151,7 +1355,7 @@
 
 var stubsVersionsLock sync.Mutex
 
-func latestStubsVersionFor(config android.Config, name string) string {
+func LatestStubsVersionFor(config android.Config, name string) string {
 	versions, ok := stubsVersionsFor(config)[name]
 	if ok && len(versions) > 0 {
 		// the versions are alreay sorted in ascending order
@@ -1163,11 +1367,10 @@
 // Version mutator splits a module into the mandatory non-stubs variant
 // (which is unnamed) and zero or more stubs variants.
 func VersionMutator(mctx android.BottomUpMutatorContext) {
-	if m, ok := mctx.Module().(*Module); ok && !m.inRecovery() && m.linker != nil {
-		if library, ok := m.linker.(*libraryDecorator); ok && library.buildShared() &&
-			len(library.Properties.Stubs.Versions) > 0 {
+	if library, ok := mctx.Module().(LinkableInterface); ok && !library.InRecovery() {
+		if library.CcLibrary() && library.BuildSharedVariant() && len(library.StubsVersions()) > 0 {
 			versions := []string{}
-			for _, v := range library.Properties.Stubs.Versions {
+			for _, v := range library.StubsVersions() {
 				if _, err := strconv.Atoi(v); err != nil {
 					mctx.PropertyErrorf("versions", "%q is not a number", v)
 				}
@@ -1191,14 +1394,9 @@
 
 			modules := mctx.CreateVariations(versions...)
 			for i, m := range modules {
-				l := m.(*Module).linker.(*libraryDecorator)
 				if versions[i] != "" {
-					l.MutatedProperties.BuildStubs = true
-					l.MutatedProperties.StubsVersion = versions[i]
-					m.(*Module).Properties.HideFromMake = true
-					m.(*Module).sanitize = nil
-					m.(*Module).stl = nil
-					m.(*Module).Properties.PreventInstall = true
+					m.(LinkableInterface).SetBuildStubs()
+					m.(LinkableInterface).SetStubsVersions(versions[i])
 				}
 			}
 		} else {
@@ -1207,9 +1405,47 @@
 		return
 	}
 	if genrule, ok := mctx.Module().(*genrule.Module); ok {
-		if props, ok := genrule.Extra.(*GenruleExtraProperties); ok && !props.InRecovery {
-			mctx.CreateVariations("")
-			return
+		if _, ok := genrule.Extra.(*GenruleExtraProperties); ok {
+			if !genrule.InRecovery() {
+				mctx.CreateVariations("")
+				return
+			}
 		}
 	}
 }
+
+// maybeInjectBoringSSLHash adds a rule to run bssl_inject_hash on the output file if the module has the
+// inject_bssl_hash or if any static library dependencies have inject_bssl_hash set.  It returns the output path
+// that the linked output file should be written to.
+// TODO(b/137267623): Remove this in favor of a cc_genrule when they support operating on shared libraries.
+func maybeInjectBoringSSLHash(ctx android.ModuleContext, outputFile android.ModuleOutPath,
+	inject *bool, fileName string) android.ModuleOutPath {
+	// TODO(b/137267623): Remove this in favor of a cc_genrule when they support operating on shared libraries.
+	injectBoringSSLHash := Bool(inject)
+	ctx.VisitDirectDeps(func(dep android.Module) {
+		tag := ctx.OtherModuleDependencyTag(dep)
+		if tag == StaticDepTag || tag == staticExportDepTag || tag == wholeStaticDepTag || tag == lateStaticDepTag {
+			if cc, ok := dep.(*Module); ok {
+				if library, ok := cc.linker.(*libraryDecorator); ok {
+					if Bool(library.Properties.Inject_bssl_hash) {
+						injectBoringSSLHash = true
+					}
+				}
+			}
+		}
+	})
+	if injectBoringSSLHash {
+		hashedOutputfile := outputFile
+		outputFile = android.PathForModuleOut(ctx, "unhashed", fileName)
+
+		rule := android.NewRuleBuilder()
+		rule.Command().
+			BuiltTool(ctx, "bssl_inject_hash").
+			Flag("-sha256").
+			FlagWithInput("-in-object ", outputFile).
+			FlagWithOutput("-o ", hashedOutputfile)
+		rule.Build(pctx, ctx, "injectCryptoHash", "inject crypto hash")
+	}
+
+	return outputFile
+}
diff --git a/cc/library_sdk_member.go b/cc/library_sdk_member.go
new file mode 100644
index 0000000..fd5a4da
--- /dev/null
+++ b/cc/library_sdk_member.go
@@ -0,0 +1,344 @@
+// 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 cc
+
+import (
+	"path/filepath"
+	"reflect"
+
+	"android/soong/android"
+	"github.com/google/blueprint"
+)
+
+// This file contains support for using cc library modules within an sdk.
+
+var sharedLibrarySdkMemberType = &librarySdkMemberType{
+	SdkMemberTypeBase: android.SdkMemberTypeBase{
+		PropertyName: "native_shared_libs",
+		SupportsSdk:  true,
+	},
+	prebuiltModuleType: "cc_prebuilt_library_shared",
+	linkTypes:          []string{"shared"},
+}
+
+var staticLibrarySdkMemberType = &librarySdkMemberType{
+	SdkMemberTypeBase: android.SdkMemberTypeBase{
+		PropertyName: "native_static_libs",
+		SupportsSdk:  true,
+	},
+	prebuiltModuleType: "cc_prebuilt_library_static",
+	linkTypes:          []string{"static"},
+}
+
+func init() {
+	// Register sdk member types.
+	android.RegisterSdkMemberType(sharedLibrarySdkMemberType)
+	android.RegisterSdkMemberType(staticLibrarySdkMemberType)
+}
+
+type librarySdkMemberType struct {
+	android.SdkMemberTypeBase
+
+	prebuiltModuleType string
+
+	// The set of link types supported, set of "static", "shared".
+	linkTypes []string
+}
+
+func (mt *librarySdkMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
+	targets := mctx.MultiTargets()
+	for _, lib := range names {
+		for _, target := range targets {
+			name, version := StubsLibNameAndVersion(lib)
+			if version == "" {
+				version = LatestStubsVersionFor(mctx.Config(), name)
+			}
+			for _, linkType := range mt.linkTypes {
+				mctx.AddFarVariationDependencies(append(target.Variations(), []blueprint.Variation{
+					{Mutator: "image", Variation: android.CoreVariation},
+					{Mutator: "link", Variation: linkType},
+					{Mutator: "version", Variation: version},
+				}...), dependencyTag, name)
+			}
+		}
+	}
+}
+
+func (mt *librarySdkMemberType) IsInstance(module android.Module) bool {
+	// Check the module to see if it can be used with this module type.
+	if m, ok := module.(*Module); ok {
+		for _, allowableMemberType := range m.sdkMemberTypes {
+			if allowableMemberType == mt {
+				return true
+			}
+		}
+	}
+
+	return false
+}
+
+// copy exported header files and stub *.so files
+func (mt *librarySdkMemberType) BuildSnapshot(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder, member android.SdkMember) {
+	info := mt.organizeVariants(member)
+	buildSharedNativeLibSnapshot(sdkModuleContext, info, builder, member)
+}
+
+// Organize the variants by architecture.
+func (mt *librarySdkMemberType) organizeVariants(member android.SdkMember) *nativeLibInfo {
+	memberName := member.Name()
+	info := &nativeLibInfo{
+		name:       memberName,
+		memberType: mt,
+	}
+
+	for _, variant := range member.Variants() {
+		ccModule := variant.(*Module)
+
+		// Separate out the generated include dirs (which are arch specific) from the
+		// include dirs (which may not be).
+		exportedIncludeDirs, exportedGeneratedIncludeDirs := android.FilterPathListPredicate(
+			ccModule.ExportedIncludeDirs(), isGeneratedHeaderDirectory)
+
+		info.archVariantProperties = append(info.archVariantProperties, nativeLibInfoProperties{
+			name:                         memberName,
+			archType:                     ccModule.Target().Arch.ArchType.String(),
+			ExportedIncludeDirs:          exportedIncludeDirs,
+			ExportedGeneratedIncludeDirs: exportedGeneratedIncludeDirs,
+			ExportedSystemIncludeDirs:    ccModule.ExportedSystemIncludeDirs(),
+			ExportedFlags:                ccModule.ExportedFlags(),
+			exportedGeneratedHeaders:     ccModule.ExportedGeneratedHeaders(),
+			outputFile:                   ccModule.OutputFile().Path(),
+		})
+	}
+
+	// Initialize the unexported properties that will not be set during the
+	// extraction process.
+	info.commonProperties.name = memberName
+
+	// Extract common properties from the arch specific properties.
+	extractCommonProperties(&info.commonProperties, info.archVariantProperties)
+
+	return info
+}
+
+func isGeneratedHeaderDirectory(p android.Path) bool {
+	_, gen := p.(android.WritablePath)
+	return gen
+}
+
+// Extract common properties from a slice of property structures of the same type.
+//
+// All the property structures must be of the same type.
+// commonProperties - must be a pointer to the structure into which common properties will be added.
+// inputPropertiesSlice - must be a slice of input properties structures.
+//
+// Iterates over each exported field (capitalized name) and checks to see whether they
+// have the same value (using DeepEquals) across all the input properties. If it does not then no
+// change is made. Otherwise, the common value is stored in the field in the commonProperties
+// and the field in each of the input properties structure is set to its default value.
+func extractCommonProperties(commonProperties interface{}, inputPropertiesSlice interface{}) {
+	commonStructValue := reflect.ValueOf(commonProperties).Elem()
+	propertiesStructType := commonStructValue.Type()
+
+	// Create an empty structure from which default values for the field can be copied.
+	emptyStructValue := reflect.New(propertiesStructType).Elem()
+
+	for f := 0; f < propertiesStructType.NumField(); f++ {
+		// Check to see if all the structures have the same value for the field. The commonValue
+		// is nil on entry to the loop and if it is nil on exit then there is no common value,
+		// otherwise it points to the common value.
+		var commonValue *reflect.Value
+		sliceValue := reflect.ValueOf(inputPropertiesSlice)
+
+		for i := 0; i < sliceValue.Len(); i++ {
+			structValue := sliceValue.Index(i)
+			fieldValue := structValue.Field(f)
+			if !fieldValue.CanInterface() {
+				// The field is not exported so ignore it.
+				continue
+			}
+
+			if commonValue == nil {
+				// Use the first value as the commonProperties value.
+				commonValue = &fieldValue
+			} else {
+				// If the value does not match the current common value then there is
+				// no value in common so break out.
+				if !reflect.DeepEqual(fieldValue.Interface(), commonValue.Interface()) {
+					commonValue = nil
+					break
+				}
+			}
+		}
+
+		// If the fields all have a common value then store it in the common struct field
+		// and set the input struct's field to the empty value.
+		if commonValue != nil {
+			emptyValue := emptyStructValue.Field(f)
+			commonStructValue.Field(f).Set(*commonValue)
+			for i := 0; i < sliceValue.Len(); i++ {
+				structValue := sliceValue.Index(i)
+				fieldValue := structValue.Field(f)
+				fieldValue.Set(emptyValue)
+			}
+		}
+	}
+}
+
+func buildSharedNativeLibSnapshot(sdkModuleContext android.ModuleContext, info *nativeLibInfo, builder android.SnapshotBuilder, member android.SdkMember) {
+	// a function for emitting include dirs
+	addExportedDirCopyCommandsForNativeLibs := func(lib nativeLibInfoProperties) {
+		// Do not include ExportedGeneratedIncludeDirs in the list of directories whose
+		// contents are copied as they are copied from exportedGeneratedHeaders below.
+		includeDirs := lib.ExportedIncludeDirs
+		includeDirs = append(includeDirs, lib.ExportedSystemIncludeDirs...)
+		for _, dir := range includeDirs {
+			// lib.ArchType is "" for common properties.
+			targetDir := filepath.Join(lib.archType, nativeIncludeDir)
+
+			// TODO(jiyong) copy headers having other suffixes
+			headers, _ := sdkModuleContext.GlobWithDeps(dir.String()+"/**/*.h", nil)
+			for _, file := range headers {
+				src := android.PathForSource(sdkModuleContext, file)
+				dest := filepath.Join(targetDir, file)
+				builder.CopyToSnapshot(src, dest)
+			}
+		}
+
+		genHeaders := lib.exportedGeneratedHeaders
+		for _, file := range genHeaders {
+			// lib.ArchType is "" for common properties.
+			targetDir := filepath.Join(lib.archType, nativeGeneratedIncludeDir)
+
+			dest := filepath.Join(targetDir, lib.name, file.Rel())
+			builder.CopyToSnapshot(file, dest)
+		}
+	}
+
+	addExportedDirCopyCommandsForNativeLibs(info.commonProperties)
+
+	// for each architecture
+	for _, av := range info.archVariantProperties {
+		builder.CopyToSnapshot(av.outputFile, nativeLibraryPathFor(av))
+
+		addExportedDirCopyCommandsForNativeLibs(av)
+	}
+
+	info.generatePrebuiltLibrary(sdkModuleContext, builder, member)
+}
+
+func (info *nativeLibInfo) generatePrebuiltLibrary(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder, member android.SdkMember) {
+
+	// a function for emitting include dirs
+	addExportedDirsForNativeLibs := func(lib nativeLibInfoProperties, properties android.BpPropertySet, systemInclude bool) {
+		includeDirs := nativeIncludeDirPathsFor(lib, systemInclude)
+		if len(includeDirs) == 0 {
+			return
+		}
+		var propertyName string
+		if !systemInclude {
+			propertyName = "export_include_dirs"
+		} else {
+			propertyName = "export_system_include_dirs"
+		}
+		properties.AddProperty(propertyName, includeDirs)
+	}
+
+	pbm := builder.AddPrebuiltModule(member, info.memberType.prebuiltModuleType)
+
+	addExportedDirsForNativeLibs(info.commonProperties, pbm, false /*systemInclude*/)
+	addExportedDirsForNativeLibs(info.commonProperties, pbm, true /*systemInclude*/)
+
+	archProperties := pbm.AddPropertySet("arch")
+	for _, av := range info.archVariantProperties {
+		archTypeProperties := archProperties.AddPropertySet(av.archType)
+		archTypeProperties.AddProperty("srcs", []string{nativeLibraryPathFor(av)})
+
+		// export_* properties are added inside the arch: {<arch>: {...}} block
+		addExportedDirsForNativeLibs(av, archTypeProperties, false /*systemInclude*/)
+		addExportedDirsForNativeLibs(av, archTypeProperties, true /*systemInclude*/)
+	}
+	pbm.AddProperty("stl", "none")
+	pbm.AddProperty("system_shared_libs", []string{})
+}
+
+const (
+	nativeIncludeDir          = "include"
+	nativeGeneratedIncludeDir = "include_gen"
+	nativeStubDir             = "lib"
+)
+
+// path to the native library. Relative to <sdk_root>/<api_dir>
+func nativeLibraryPathFor(lib nativeLibInfoProperties) string {
+	return filepath.Join(lib.archType,
+		nativeStubDir, lib.outputFile.Base())
+}
+
+// paths to the include dirs of a native shared library. Relative to <sdk_root>/<api_dir>
+func nativeIncludeDirPathsFor(lib nativeLibInfoProperties, systemInclude bool) []string {
+	var result []string
+	var includeDirs []android.Path
+	if !systemInclude {
+		// Include the generated include dirs in the exported include dirs.
+		includeDirs = append(lib.ExportedIncludeDirs, lib.ExportedGeneratedIncludeDirs...)
+	} else {
+		includeDirs = lib.ExportedSystemIncludeDirs
+	}
+	for _, dir := range includeDirs {
+		var path string
+		if isGeneratedHeaderDirectory(dir) {
+			path = filepath.Join(nativeGeneratedIncludeDir, lib.name)
+		} else {
+			path = filepath.Join(nativeIncludeDir, dir.String())
+		}
+
+		// lib.ArchType is "" for common properties.
+		path = filepath.Join(lib.archType, path)
+		result = append(result, path)
+	}
+	return result
+}
+
+// nativeLibInfoProperties represents properties of a native lib
+//
+// The exported (capitalized) fields will be examined and may be changed during common value extraction.
+// The unexported fields will be left untouched.
+type nativeLibInfoProperties struct {
+	// The name of the library, is not exported as this must not be changed during optimization.
+	name string
+
+	// archType is not exported as if set (to a non default value) it is always arch specific.
+	// This is "" for common properties.
+	archType string
+
+	ExportedIncludeDirs          android.Paths
+	ExportedGeneratedIncludeDirs android.Paths
+	ExportedSystemIncludeDirs    android.Paths
+	ExportedFlags                []string
+
+	// exportedGeneratedHeaders is not exported as if set it is always arch specific.
+	exportedGeneratedHeaders android.Paths
+
+	// outputFile is not exported as it is always arch specific.
+	outputFile android.Path
+}
+
+// nativeLibInfo represents a collection of arch-specific modules having the same name
+type nativeLibInfo struct {
+	name                  string
+	memberType            *librarySdkMemberType
+	archVariantProperties []nativeLibInfoProperties
+	commonProperties      nativeLibInfoProperties
+}
diff --git a/cc/library_test.go b/cc/library_test.go
index 859b05a..b8d8895 100644
--- a/cc/library_test.go
+++ b/cc/library_test.go
@@ -24,23 +24,26 @@
 		ctx := testCc(t, `
 		cc_library {
 			name: "libfoo",
-			srcs: ["foo.c"],
+			srcs: ["foo.c", "baz.o"],
 		}`)
 
-		libfooShared := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_core_shared").Rule("ld")
-		libfooStatic := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_core_static").Output("libfoo.a")
+		libfooShared := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Rule("ld")
+		libfooStatic := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_static").Output("libfoo.a")
 
-		if len(libfooShared.Inputs) != 1 {
+		if len(libfooShared.Inputs) != 2 {
 			t.Fatalf("unexpected inputs to libfoo shared: %#v", libfooShared.Inputs.Strings())
 		}
 
-		if len(libfooStatic.Inputs) != 1 {
+		if len(libfooStatic.Inputs) != 2 {
 			t.Fatalf("unexpected inputs to libfoo static: %#v", libfooStatic.Inputs.Strings())
 		}
 
 		if libfooShared.Inputs[0] != libfooStatic.Inputs[0] {
 			t.Errorf("static object not reused for shared library")
 		}
+		if libfooShared.Inputs[1] != libfooStatic.Inputs[1] {
+			t.Errorf("static object not reused for shared library")
+		}
 	})
 
 	t.Run("extra static source", func(t *testing.T) {
@@ -53,8 +56,8 @@
 			},
 		}`)
 
-		libfooShared := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_core_shared").Rule("ld")
-		libfooStatic := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_core_static").Output("libfoo.a")
+		libfooShared := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Rule("ld")
+		libfooStatic := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_static").Output("libfoo.a")
 
 		if len(libfooShared.Inputs) != 1 {
 			t.Fatalf("unexpected inputs to libfoo shared: %#v", libfooShared.Inputs.Strings())
@@ -79,8 +82,8 @@
 			},
 		}`)
 
-		libfooShared := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_core_shared").Rule("ld")
-		libfooStatic := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_core_static").Output("libfoo.a")
+		libfooShared := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Rule("ld")
+		libfooStatic := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_static").Output("libfoo.a")
 
 		if len(libfooShared.Inputs) != 2 {
 			t.Fatalf("unexpected inputs to libfoo shared: %#v", libfooShared.Inputs.Strings())
@@ -105,8 +108,8 @@
 			},
 		}`)
 
-		libfooShared := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_core_shared").Rule("ld")
-		libfooStatic := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_core_static").Output("libfoo.a")
+		libfooShared := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Rule("ld")
+		libfooStatic := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_static").Output("libfoo.a")
 
 		if len(libfooShared.Inputs) != 1 {
 			t.Fatalf("unexpected inputs to libfoo shared: %#v", libfooShared.Inputs.Strings())
@@ -131,8 +134,8 @@
 			},
 		}`)
 
-		libfooShared := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_core_shared").Rule("ld")
-		libfooStatic := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_core_static").Output("libfoo.a")
+		libfooShared := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Rule("ld")
+		libfooStatic := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_static").Output("libfoo.a")
 
 		if len(libfooShared.Inputs) != 1 {
 			t.Fatalf("unexpected inputs to libfoo shared: %#v", libfooShared.Inputs.Strings())
@@ -162,8 +165,8 @@
 			},
 		}`)
 
-		libfooShared := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_core_shared").Rule("ld")
-		libfooStatic := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_core_static").Output("libfoo.a")
+		libfooShared := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Rule("ld")
+		libfooStatic := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_static").Output("libfoo.a")
 
 		if len(libfooShared.Inputs) != 3 {
 			t.Fatalf("unexpected inputs to libfoo shared: %#v", libfooShared.Inputs.Strings())
@@ -177,8 +180,8 @@
 			t.Errorf("static objects not reused for shared library")
 		}
 
-		libfoo := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_core_shared").Module().(*Module)
-		if !inList("-DGOOGLE_PROTOBUF_NO_RTTI", libfoo.flags.CFlags) {
+		libfoo := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Module().(*Module)
+		if !inList("-DGOOGLE_PROTOBUF_NO_RTTI", libfoo.flags.Local.CFlags) {
 			t.Errorf("missing protobuf cflags")
 		}
 	})
diff --git a/cc/linkable.go b/cc/linkable.go
new file mode 100644
index 0000000..106092b
--- /dev/null
+++ b/cc/linkable.go
@@ -0,0 +1,75 @@
+package cc
+
+import (
+	"github.com/google/blueprint"
+
+	"android/soong/android"
+)
+
+type LinkableInterface interface {
+	Module() android.Module
+	CcLibrary() bool
+	CcLibraryInterface() bool
+
+	OutputFile() android.OptionalPath
+
+	IncludeDirs() android.Paths
+	SetDepsInLinkOrder([]android.Path)
+	GetDepsInLinkOrder() []android.Path
+
+	HasStaticVariant() bool
+	GetStaticVariant() LinkableInterface
+
+	NonCcVariants() bool
+
+	StubsVersions() []string
+	BuildStubs() bool
+	SetBuildStubs()
+	SetStubsVersions(string)
+	HasStubsVariants() bool
+	SelectedStl() string
+	ApiLevel() string
+
+	BuildStaticVariant() bool
+	BuildSharedVariant() bool
+	SetStatic()
+	SetShared()
+	Static() bool
+	Shared() bool
+	Toc() android.OptionalPath
+
+	InRecovery() bool
+	OnlyInRecovery() bool
+
+	UseVndk() bool
+	MustUseVendorVariant() bool
+	IsVndk() bool
+	HasVendorVariant() bool
+
+	SdkVersion() string
+
+	ToolchainLibrary() bool
+	NdkPrebuiltStl() bool
+	StubDecorator() bool
+
+	AllStaticDeps() []string
+}
+
+type DependencyTag struct {
+	blueprint.BaseDependencyTag
+	Name    string
+	Library bool
+	Shared  bool
+
+	ReexportFlags bool
+
+	ExplicitlyVersioned bool
+}
+
+var (
+	SharedDepTag = DependencyTag{Name: "shared", Library: true, Shared: true}
+	StaticDepTag = DependencyTag{Name: "static", Library: true}
+
+	CrtBeginDepTag = DependencyTag{Name: "crtbegin"}
+	CrtEndDepTag   = DependencyTag{Name: "crtend"}
+)
diff --git a/cc/linker.go b/cc/linker.go
index dda2fcb..61ae757 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -125,6 +125,10 @@
 			// variant of the C/C++ module.
 			Shared_libs []string
 
+			// list of static libs that only should be used to build the recovery
+			// variant of the C/C++ module.
+			Static_libs []string
+
 			// list of shared libs that should not be used to build
 			// the recovery variant of the C/C++ module.
 			Exclude_shared_libs []string
@@ -147,9 +151,6 @@
 
 	// local file name to pass to the linker as --version_script
 	Version_script *string `android:"path,arch_variant"`
-
-	// Local file name to pass to the linker as --symbol-ordering-file
-	Symbol_ordering_file *string `android:"arch_variant"`
 }
 
 func NewBaseLinker(sanitize *sanitize) *baseLinker {
@@ -214,6 +215,7 @@
 		deps.SharedLibs = append(deps.SharedLibs, linker.Properties.Target.Recovery.Shared_libs...)
 		deps.SharedLibs = removeListFromList(deps.SharedLibs, linker.Properties.Target.Recovery.Exclude_shared_libs)
 		deps.ReexportSharedLibHeaders = removeListFromList(deps.ReexportSharedLibHeaders, linker.Properties.Target.Recovery.Exclude_shared_libs)
+		deps.StaticLibs = append(deps.StaticLibs, linker.Properties.Target.Recovery.Static_libs...)
 		deps.StaticLibs = removeListFromList(deps.StaticLibs, linker.Properties.Target.Recovery.Exclude_static_libs)
 		deps.HeaderLibs = removeListFromList(deps.HeaderLibs, linker.Properties.Target.Recovery.Exclude_header_libs)
 		deps.ReexportHeaderLibHeaders = removeListFromList(deps.ReexportHeaderLibHeaders, linker.Properties.Target.Recovery.Exclude_header_libs)
@@ -329,61 +331,66 @@
 	}
 
 	if linker.useClangLld(ctx) {
-		flags.LdFlags = append(flags.LdFlags, fmt.Sprintf("${config.%sGlobalLldflags}", hod))
+		flags.Global.LdFlags = append(flags.Global.LdFlags, fmt.Sprintf("${config.%sGlobalLldflags}", hod))
 		if !BoolDefault(linker.Properties.Pack_relocations, true) {
-			flags.LdFlags = append(flags.LdFlags, "-Wl,--pack-dyn-relocs=none")
+			flags.Global.LdFlags = append(flags.Global.LdFlags, "-Wl,--pack-dyn-relocs=none")
 		} else if ctx.Device() {
 			// The SHT_RELR relocations is only supported by API level >= 28.
 			// Do not turn this on if older version NDK is used.
 			if !ctx.useSdk() || CheckSdkVersionAtLeast(ctx, 28) {
-				flags.LdFlags = append(flags.LdFlags, "-Wl,--pack-dyn-relocs=android+relr")
-				flags.LdFlags = append(flags.LdFlags, "-Wl,--use-android-relr-tags")
+				flags.Global.LdFlags = append(flags.Global.LdFlags,
+					"-Wl,--pack-dyn-relocs=android+relr",
+					"-Wl,--use-android-relr-tags")
 			}
 		}
 	} else {
-		flags.LdFlags = append(flags.LdFlags, fmt.Sprintf("${config.%sGlobalLdflags}", hod))
+		flags.Global.LdFlags = append(flags.Global.LdFlags, fmt.Sprintf("${config.%sGlobalLdflags}", hod))
 	}
 	if Bool(linker.Properties.Allow_undefined_symbols) {
 		if ctx.Darwin() {
 			// darwin defaults to treating undefined symbols as errors
-			flags.LdFlags = append(flags.LdFlags, "-Wl,-undefined,dynamic_lookup")
+			flags.Global.LdFlags = append(flags.Global.LdFlags, "-Wl,-undefined,dynamic_lookup")
 		}
 	} else if !ctx.Darwin() && !ctx.Windows() {
-		flags.LdFlags = append(flags.LdFlags, "-Wl,--no-undefined")
+		flags.Global.LdFlags = append(flags.Global.LdFlags, "-Wl,--no-undefined")
 	}
 
 	if linker.useClangLld(ctx) {
-		flags.LdFlags = append(flags.LdFlags, toolchain.ClangLldflags())
+		flags.Global.LdFlags = append(flags.Global.LdFlags, toolchain.ClangLldflags())
 	} else {
-		flags.LdFlags = append(flags.LdFlags, toolchain.ClangLdflags())
+		flags.Global.LdFlags = append(flags.Global.LdFlags, toolchain.ClangLdflags())
 	}
 
 	if !ctx.toolchain().Bionic() && !ctx.Fuchsia() {
 		CheckBadHostLdlibs(ctx, "host_ldlibs", linker.Properties.Host_ldlibs)
 
-		flags.LdFlags = append(flags.LdFlags, linker.Properties.Host_ldlibs...)
+		flags.Local.LdFlags = append(flags.Local.LdFlags, linker.Properties.Host_ldlibs...)
 
 		if !ctx.Windows() {
 			// Add -ldl, -lpthread, -lm and -lrt to host builds to match the default behavior of device
 			// builds
-			flags.LdFlags = append(flags.LdFlags,
+			flags.Global.LdFlags = append(flags.Global.LdFlags,
 				"-ldl",
 				"-lpthread",
 				"-lm",
 			)
 			if !ctx.Darwin() {
-				flags.LdFlags = append(flags.LdFlags, "-lrt")
+				flags.Global.LdFlags = append(flags.Global.LdFlags, "-lrt")
 			}
 		}
 	}
 
 	if ctx.Fuchsia() {
-		flags.LdFlags = append(flags.LdFlags, "-lfdio", "-lzircon")
+		flags.Global.LdFlags = append(flags.Global.LdFlags, "-lfdio", "-lzircon")
+	}
+
+	if ctx.toolchain().LibclangRuntimeLibraryArch() != "" {
+		flags.Global.LdFlags = append(flags.Global.LdFlags, "-Wl,--exclude-libs="+config.BuiltinsRuntimeLibrary(ctx.toolchain())+".a")
 	}
 
 	CheckBadLinkerFlags(ctx, "ldflags", linker.Properties.Ldflags)
 
-	flags.LdFlags = append(flags.LdFlags, proptools.NinjaAndShellEscapeList(linker.Properties.Ldflags)...)
+	flags.Local.LdFlags = append(flags.Local.LdFlags, proptools.NinjaAndShellEscapeList(linker.Properties.Ldflags)...)
 
 	if ctx.Host() && !ctx.Windows() {
 		rpath_prefix := `\$$ORIGIN/`
@@ -393,7 +400,7 @@
 
 		if !ctx.static() {
 			for _, rpath := range linker.dynamicProperties.RunPaths {
-				flags.LdFlags = append(flags.LdFlags, "-Wl,-rpath,"+rpath_prefix+rpath)
+				flags.Global.LdFlags = append(flags.Global.LdFlags, "-Wl,-rpath,"+rpath_prefix+rpath)
 			}
 		}
 	}
@@ -403,10 +410,10 @@
 		// to older devices requires the old style hash. Fortunately, we can build with both and
 		// it'll work anywhere.
 		// This is not currently supported on MIPS architectures.
-		flags.LdFlags = append(flags.LdFlags, "-Wl,--hash-style=both")
+		flags.Global.LdFlags = append(flags.Global.LdFlags, "-Wl,--hash-style=both")
 	}
 
-	flags.LdFlags = append(flags.LdFlags, toolchain.ToolchainClangLdflags())
+	flags.Global.LdFlags = append(flags.Global.LdFlags, toolchain.ToolchainClangLdflags())
 
 	if Bool(linker.Properties.Group_static_libs) {
 		flags.GroupStaticLibs = true
@@ -428,13 +435,13 @@
 			if ctx.Darwin() {
 				ctx.PropertyErrorf("version_script", "Not supported on Darwin")
 			} else {
-				flags.LdFlags = append(flags.LdFlags,
+				flags.Local.LdFlags = append(flags.Local.LdFlags,
 					"-Wl,--version-script,"+versionScript.String())
 				flags.LdFlagsDeps = append(flags.LdFlagsDeps, versionScript.Path())
 
 				if linker.sanitize.isSanitizerEnabled(cfi) {
 					cfiExportsMap := android.PathForSource(ctx, cfiExportsMapPath)
-					flags.LdFlags = append(flags.LdFlags,
+					flags.Local.LdFlags = append(flags.Local.LdFlags,
 						"-Wl,--version-script,"+cfiExportsMap.String())
 					flags.LdFlagsDeps = append(flags.LdFlagsDeps, cfiExportsMap)
 				}
@@ -442,16 +449,6 @@
 		}
 	}
 
-	if !linker.dynamicProperties.BuildStubs {
-		symbolOrderingFile := ctx.ExpandOptionalSource(
-			linker.Properties.Symbol_ordering_file, "Symbol_ordering_file")
-		if symbolOrderingFile.Valid() {
-			flags.LdFlags = append(flags.LdFlags,
-				"-Wl,--symbol-ordering-file,"+symbolOrderingFile.String())
-			flags.LdFlagsDeps = append(flags.LdFlagsDeps, symbolOrderingFile.Path())
-		}
-	}
-
 	return flags
 }
 
@@ -483,7 +480,33 @@
 		Input:       in,
 		Output:      out,
 		Args: map[string]string{
-			"buildNumberFromFile": ctx.Config().BuildNumberFromFile(),
+			"buildNumberFromFile": proptools.NinjaEscape(ctx.Config().BuildNumberFromFile()),
 		},
 	})
 }
+
+// Rule to generate .bss symbol ordering file.
+
+var (
+	_                      = pctx.SourcePathVariable("genSortedBssSymbolsPath", "build/soong/scripts/gen_sorted_bss_symbols.sh")
+	gen_sorted_bss_symbols = pctx.AndroidStaticRule("gen_sorted_bss_symbols",
+		blueprint.RuleParams{
+			Command:     "CROSS_COMPILE=$crossCompile $genSortedBssSymbolsPath ${in} ${out}",
+			CommandDeps: []string{"$genSortedBssSymbolsPath", "${crossCompile}nm"},
+		},
+		"crossCompile")
+)
+
+func (linker *baseLinker) sortBssSymbolsBySize(ctx ModuleContext, in android.Path, symbolOrderingFile android.ModuleOutPath, flags builderFlags) string {
+	crossCompile := gccCmd(flags.toolchain, "")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        gen_sorted_bss_symbols,
+		Description: "generate bss symbol order " + symbolOrderingFile.Base(),
+		Output:      symbolOrderingFile,
+		Input:       in,
+		Args: map[string]string{
+			"crossCompile": crossCompile,
+		},
+	})
+	return "-Wl,--symbol-ordering-file," + symbolOrderingFile.String()
+}
diff --git a/cc/llndk_library.go b/cc/llndk_library.go
index 8290103..3dee692 100644
--- a/cc/llndk_library.go
+++ b/cc/llndk_library.go
@@ -76,17 +76,12 @@
 }
 
 func (stub *llndkStubDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects {
-	vndk_ver := ctx.DeviceConfig().VndkVersion()
-	if vndk_ver == "current" {
-		platform_vndk_ver := ctx.DeviceConfig().PlatformVndkVersion()
-		if !inList(platform_vndk_ver, ctx.Config().PlatformVersionCombinedCodenames()) {
-			vndk_ver = platform_vndk_ver
-		}
-	} else if vndk_ver == "" {
-		// For non-enforcing devices, use "current"
-		vndk_ver = "current"
+	vndkVer := ctx.Module().(*Module).VndkVersion()
+	if !inList(vndkVer, ctx.Config().PlatformVersionCombinedCodenames()) || vndkVer == "" {
+		// For non-enforcing devices, vndkVer is empty. Use "current" in that case, too.
+		vndkVer = "current"
 	}
-	objs, versionScript := compileStubLibrary(ctx, flags, String(stub.Properties.Symbol_file), vndk_ver, "--vndk")
+	objs, versionScript := compileStubLibrary(ctx, flags, String(stub.Properties.Symbol_file), vndkVer, "--llndk")
 	stub.versionScriptPath = versionScript
 	return objs
 }
@@ -133,7 +128,7 @@
 
 	if !Bool(stub.Properties.Unversioned) {
 		linkerScriptFlag := "-Wl,--version-script," + stub.versionScriptPath.String()
-		flags.LdFlags = append(flags.LdFlags, linkerScriptFlag)
+		flags.Local.LdFlags = append(flags.Local.LdFlags, linkerScriptFlag)
 		flags.LdFlagsDeps = append(flags.LdFlagsDeps, stub.versionScriptPath)
 	}
 
@@ -146,9 +141,9 @@
 		}
 
 		if Bool(stub.Properties.Export_headers_as_system) {
-			stub.reexportSystemDirs(genHeaderOutDir.String())
+			stub.reexportSystemDirs(genHeaderOutDir)
 		} else {
-			stub.reexportDirs(genHeaderOutDir.String())
+			stub.reexportDirs(genHeaderOutDir)
 		}
 
 		stub.reexportDeps(timestampFiles...)
@@ -177,7 +172,6 @@
 		libraryDecorator: library,
 	}
 	stub.Properties.Vendor_available = BoolPtr(true)
-	module.Properties.UseVndk = true
 	module.compiler = stub
 	module.linker = stub
 	module.installer = nil
@@ -232,7 +226,7 @@
 		&library.MutatedProperties,
 		&library.flagExporter.Properties)
 
-	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibBoth)
+	module.Init()
 
 	return module
 }
diff --git a/cc/lto.go b/cc/lto.go
index 1084869..4489fc7 100644
--- a/cc/lto.go
+++ b/cc/lto.go
@@ -80,6 +80,12 @@
 }
 
 func (lto *lto) flags(ctx BaseModuleContext, flags Flags) Flags {
+	// TODO(b/131771163): Disable LTO when using explicit fuzzing configurations.
+	// LTO breaks fuzzer builds.
+	if inList("-fsanitize=fuzzer-no-link", flags.Local.CFlags) {
+		return flags
+	}
+
 	if lto.LTO() {
 		var ltoFlag string
 		if Bool(lto.Properties.Lto.Thin) {
@@ -88,27 +94,28 @@
 			ltoFlag = "-flto"
 		}
 
-		flags.CFlags = append(flags.CFlags, ltoFlag)
-		flags.LdFlags = append(flags.LdFlags, ltoFlag)
+		flags.Local.CFlags = append(flags.Local.CFlags, ltoFlag)
+		flags.Local.LdFlags = append(flags.Local.LdFlags, ltoFlag)
 
 		if ctx.Config().IsEnvTrue("USE_THINLTO_CACHE") && Bool(lto.Properties.Lto.Thin) && lto.useClangLld(ctx) {
 			// Set appropriate ThinLTO cache policy
 			cacheDirFormat := "-Wl,--thinlto-cache-dir="
 			cacheDir := android.PathForOutput(ctx, "thinlto-cache").String()
-			flags.LdFlags = append(flags.LdFlags, cacheDirFormat+cacheDir)
+			flags.Local.LdFlags = append(flags.Local.LdFlags, cacheDirFormat+cacheDir)
 
 			// Limit the size of the ThinLTO cache to the lesser of 10% of available
 			// disk space and 10GB.
 			cachePolicyFormat := "-Wl,--thinlto-cache-policy="
 			policy := "cache_size=10%:cache_size_bytes=10g"
-			flags.LdFlags = append(flags.LdFlags, cachePolicyFormat+policy)
+			flags.Local.LdFlags = append(flags.Local.LdFlags, cachePolicyFormat+policy)
 		}
 
 		// If the module does not have a profile, be conservative and do not inline
 		// or unroll loops during LTO, in order to prevent significant size bloat.
 		if !ctx.isPgoCompile() {
-			flags.LdFlags = append(flags.LdFlags, "-Wl,-plugin-opt,-inline-threshold=0")
-			flags.LdFlags = append(flags.LdFlags, "-Wl,-plugin-opt,-unroll-threshold=0")
+			flags.Local.LdFlags = append(flags.Local.LdFlags,
+				"-Wl,-plugin-opt,-inline-threshold=0",
+				"-Wl,-plugin-opt,-unroll-threshold=0")
 		}
 	}
 	return flags
@@ -142,7 +149,7 @@
 		mctx.WalkDeps(func(dep android.Module, parent android.Module) bool {
 			tag := mctx.OtherModuleDependencyTag(dep)
 			switch tag {
-			case staticDepTag, staticExportDepTag, lateStaticDepTag, wholeStaticDepTag, objDepTag, reuseObjTag:
+			case StaticDepTag, staticExportDepTag, lateStaticDepTag, wholeStaticDepTag, objDepTag, reuseObjTag:
 				if dep, ok := dep.(*Module); ok && dep.lto != nil &&
 					!dep.lto.Disabled() {
 					if full && !Bool(dep.lto.Properties.Lto.Full) {
diff --git a/cc/makevars.go b/cc/makevars.go
index 78a32c8..0f9f4c1 100644
--- a/cc/makevars.go
+++ b/cc/makevars.go
@@ -63,6 +63,13 @@
 	}
 }
 
+type notOnHostContext struct {
+}
+
+func (c *notOnHostContext) Host() bool {
+	return false
+}
+
 func makeVarsProvider(ctx android.MakeVarsContext) {
 	vendorPublicLibraries := vendorPublicLibraries(ctx.Config())
 
@@ -94,25 +101,6 @@
 
 	ctx.Strict("BOARD_VNDK_VERSION", ctx.DeviceConfig().VndkVersion())
 
-	ctx.Strict("VNDK_CORE_LIBRARIES", strings.Join(*vndkCoreLibraries(ctx.Config()), " "))
-	ctx.Strict("VNDK_SAMEPROCESS_LIBRARIES", strings.Join(*vndkSpLibraries(ctx.Config()), " "))
-
-	// Make uses LLNDK_LIBRARIES to determine which libraries to install.
-	// HWASAN is only part of the LL-NDK in builds in which libc depends on HWASAN.
-	// Therefore, by removing the library here, we cause it to only be installed if libc
-	// depends on it.
-	installedLlndkLibraries := []string{}
-	for _, lib := range *llndkLibraries(ctx.Config()) {
-		if strings.HasPrefix(lib, "libclang_rt.hwasan-") {
-			continue
-		}
-		installedLlndkLibraries = append(installedLlndkLibraries, lib)
-	}
-	ctx.Strict("LLNDK_LIBRARIES", strings.Join(installedLlndkLibraries, " "))
-
-	ctx.Strict("VNDK_PRIVATE_LIBRARIES", strings.Join(*vndkPrivateLibraries(ctx.Config()), " "))
-	ctx.Strict("VNDK_USING_CORE_VARIANT_LIBRARIES", strings.Join(*vndkUsingCoreVariantLibraries(ctx.Config()), " "))
-
 	// Filter vendor_public_library that are exported to make
 	exportedVendorPublicLibraries := []string{}
 	ctx.VisitAllModules(func(module android.Module) {
@@ -138,7 +126,6 @@
 
 	ctx.Strict("ADDRESS_SANITIZER_CONFIG_EXTRA_CFLAGS", strings.Join(asanCflags, " "))
 	ctx.Strict("ADDRESS_SANITIZER_CONFIG_EXTRA_LDFLAGS", strings.Join(asanLdflags, " "))
-	ctx.Strict("ADDRESS_SANITIZER_CONFIG_EXTRA_STATIC_LIBRARIES", strings.Join(asanLibs, " "))
 
 	ctx.Strict("HWADDRESS_SANITIZER_CONFIG_EXTRA_CFLAGS", strings.Join(hwasanCflags, " "))
 	ctx.Strict("HWADDRESS_SANITIZER_GLOBAL_OPTIONS", strings.Join(hwasanGlobalOptions, ","))
@@ -160,6 +147,7 @@
 	ctx.Strict("WITH_TIDY_FLAGS", "${config.TidyWithTidyFlags}")
 
 	ctx.Strict("AIDL_CPP", "${aidlCmd}")
+	ctx.Strict("ALLOWED_MANUAL_INTERFACE_PATHS", strings.Join(allowedManualInterfacePaths, " "))
 
 	ctx.Strict("M4", "${m4Cmd}")
 
@@ -250,9 +238,9 @@
 	}
 
 	clangPrefix := secondPrefix + "CLANG_" + typePrefix
-	clangExtras := "-target " + toolchain.ClangTriple()
-	clangExtras += " -B" + config.ToolPath(toolchain)
+	clangExtras := "-B" + config.ToolPath(toolchain)
 
+	ctx.Strict(clangPrefix+"TRIPLE", toolchain.ClangTriple())
 	ctx.Strict(clangPrefix+"GLOBAL_CFLAGS", strings.Join([]string{
 		toolchain.ClangCflags(),
 		"${config.CommonClangGlobalCflags}",
diff --git a/cc/ndk_headers.go b/cc/ndk_headers.go
index 4065128..5744bb2 100644
--- a/cc/ndk_headers.go
+++ b/cc/ndk_headers.go
@@ -16,7 +16,6 @@
 
 import (
 	"fmt"
-	"os"
 	"path/filepath"
 	"strings"
 
@@ -48,7 +47,7 @@
 }
 
 // Returns the NDK base include path for use with sdk_version current. Usable with -I.
-func getCurrentIncludePath(ctx android.ModuleContext) android.OutputPath {
+func getCurrentIncludePath(ctx android.ModuleContext) android.InstallPath {
 	return getNdkSysrootBase(ctx).Join(ctx, "usr/include")
 }
 
@@ -94,7 +93,7 @@
 }
 
 func getHeaderInstallDir(ctx android.ModuleContext, header android.Path, from string,
-	to string) android.OutputPath {
+	to string) android.InstallPath {
 	// Output path is the sysroot base + "usr/include" + to directory + directory component
 	// of the file without the leading from directory stripped.
 	//
@@ -255,16 +254,8 @@
 	depsPath := android.PathForSource(ctx, "bionic/libc/versioner-dependencies")
 	depsGlob := ctx.Glob(filepath.Join(depsPath.String(), "**/*"), nil)
 	for i, path := range depsGlob {
-		fileInfo, err := os.Lstat(path.String())
-		if err != nil {
-			ctx.ModuleErrorf("os.Lstat(%q) failed: %s", path.String, err)
-		}
-		if fileInfo.Mode()&os.ModeSymlink == os.ModeSymlink {
-			dest, err := os.Readlink(path.String())
-			if err != nil {
-				ctx.ModuleErrorf("os.Readlink(%q) failed: %s",
-					path.String, err)
-			}
+		if ctx.IsSymlink(path) {
+			dest := ctx.Readlink(path)
 			// Additional .. to account for the symlink itself.
 			depsGlob[i] = android.PathForSource(
 				ctx, filepath.Clean(filepath.Join(path.String(), "..", dest)))
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index 969cb3f..00338b9 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -54,6 +54,7 @@
 		"mediandk",
 		"nativewindow",
 		"m",
+		"neuralnetworks",
 		"OpenMAXAL",
 		"OpenSLES",
 		"stdc++",
@@ -227,7 +228,7 @@
 	}
 }
 
-func ndkApiMutator(mctx android.BottomUpMutatorContext) {
+func NdkApiMutator(mctx android.BottomUpMutatorContext) {
 	if m, ok := mctx.Module().(*Module); ok {
 		if m.Enabled() {
 			if compiler, ok := m.compiler.(*stubDecorator); ok {
@@ -256,10 +257,11 @@
 }
 
 func addStubLibraryCompilerFlags(flags Flags) Flags {
-	flags.CFlags = append(flags.CFlags,
+	flags.Global.CFlags = append(flags.Global.CFlags,
 		// We're knowingly doing some otherwise unsightly things with builtin
 		// functions here. We're just generating stub libraries, so ignore it.
 		"-Wno-incompatible-library-redeclaration",
+		"-Wno-incomplete-setjmp-declaration",
 		"-Wno-builtin-requires-header",
 		"-Wno-invalid-noreturn",
 		"-Wall",
@@ -268,6 +270,10 @@
 		// (avoids the need to link an unwinder into a fake library).
 		"-fno-unwind-tables",
 	)
+	// All symbols in the stubs library should be visible.
+	if inList("-fvisibility=hidden", flags.Local.CFlags) {
+		flags.Local.CFlags = append(flags.Local.CFlags, "-fvisibility=default")
+	}
 	return flags
 }
 
@@ -336,7 +342,7 @@
 
 	if useVersionScript {
 		linkerScriptFlag := "-Wl,--version-script," + stub.versionScriptPath.String()
-		flags.LdFlags = append(flags.LdFlags, linkerScriptFlag)
+		flags.Local.LdFlags = append(flags.Local.LdFlags, linkerScriptFlag)
 		flags.LdFlagsDeps = append(flags.LdFlagsDeps, stub.versionScriptPath)
 	}
 
@@ -384,7 +390,7 @@
 
 // ndk_library creates a stub library that exposes dummy implementation
 // of functions and variables for use at build time only.
-func ndkLibraryFactory() android.Module {
+func NdkLibraryFactory() android.Module {
 	module := newStubLibrary()
 	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibBoth)
 	module.ModuleBase.EnableNativeBridgeSupportByDefault()
diff --git a/cc/ndk_prebuilt.go b/cc/ndk_prebuilt.go
index 4356732..e849aee 100644
--- a/cc/ndk_prebuilt.go
+++ b/cc/ndk_prebuilt.go
@@ -23,8 +23,8 @@
 )
 
 func init() {
-	android.RegisterModuleType("ndk_prebuilt_object", ndkPrebuiltObjectFactory)
-	android.RegisterModuleType("ndk_prebuilt_static_stl", ndkPrebuiltStaticStlFactory)
+	android.RegisterModuleType("ndk_prebuilt_object", NdkPrebuiltObjectFactory)
+	android.RegisterModuleType("ndk_prebuilt_static_stl", NdkPrebuiltStaticStlFactory)
 	android.RegisterModuleType("ndk_prebuilt_shared_stl", NdkPrebuiltSharedStlFactory)
 }
 
@@ -68,7 +68,7 @@
 // operations. Soong's module name format is ndk_<NAME>.o.<sdk_version> where
 // the object is located under
 // ./prebuilts/ndk/current/platforms/android-<sdk_version>/arch-$(HOST_ARCH)/usr/lib/<NAME>.o.
-func ndkPrebuiltObjectFactory() android.Module {
+func NdkPrebuiltObjectFactory() android.Module {
 	module := newBaseModule(android.DeviceSupported, android.MultilibBoth)
 	module.ModuleBase.EnableNativeBridgeSupportByDefault()
 	module.linker = &ndkPrebuiltObjectLinker{
@@ -126,7 +126,7 @@
 // library (stl) library for linking operation. The soong's module name format
 // is ndk_<NAME>.a where the library is located under
 // ./prebuilts/ndk/current/sources/cxx-stl/llvm-libc++/libs/$(HOST_ARCH)/<NAME>.a.
-func ndkPrebuiltStaticStlFactory() android.Module {
+func NdkPrebuiltStaticStlFactory() android.Module {
 	module, library := NewLibrary(android.DeviceSupported)
 	library.BuildOnlyStatic()
 	module.compiler = nil
diff --git a/cc/ndk_sysroot.go b/cc/ndk_sysroot.go
index e39bae5..56fd54b 100644
--- a/cc/ndk_sysroot.go
+++ b/cc/ndk_sysroot.go
@@ -58,7 +58,7 @@
 
 func init() {
 	android.RegisterModuleType("ndk_headers", ndkHeadersFactory)
-	android.RegisterModuleType("ndk_library", ndkLibraryFactory)
+	android.RegisterModuleType("ndk_library", NdkLibraryFactory)
 	android.RegisterModuleType("versioned_ndk_headers", versionedNdkHeadersFactory)
 	android.RegisterModuleType("preprocessed_ndk_headers", preprocessedNdkHeadersFactory)
 	android.RegisterSingletonType("ndk", NdkSingleton)
@@ -66,12 +66,12 @@
 	pctx.Import("android/soong/android")
 }
 
-func getNdkInstallBase(ctx android.PathContext) android.OutputPath {
-	return android.PathForOutput(ctx, "ndk")
+func getNdkInstallBase(ctx android.PathContext) android.InstallPath {
+	return android.PathForNdkInstall(ctx)
 }
 
 // Returns the main install directory for the NDK sysroot. Usable with --sysroot.
-func getNdkSysrootBase(ctx android.PathContext) android.OutputPath {
+func getNdkSysrootBase(ctx android.PathContext) android.InstallPath {
 	return getNdkInstallBase(ctx).Join(ctx, "sysroot")
 }
 
diff --git a/cc/object.go b/cc/object.go
index 50ecc38..ad31d09 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -33,13 +33,28 @@
 	Properties ObjectLinkerProperties
 }
 
+type ObjectLinkerProperties struct {
+	// list of modules that should only provide headers for this module.
+	Header_libs []string `android:"arch_variant,variant_prepend"`
+
+	// names of other cc_object modules to link into this module using partial linking
+	Objs []string `android:"arch_variant"`
+
+	// if set, add an extra objcopy --prefix-symbols= step
+	Prefix_symbols *string
+
+	// if set, the path to a linker script to pass to ld -r when combining multiple object files.
+	Linker_script *string `android:"path,arch_variant"`
+}
+
 // cc_object runs the compiler without running the linker. It is rarely
 // necessary, but sometimes used to generate .s files from .c files to use as
 // input to a cc_genrule module.
 func ObjectFactory() android.Module {
 	module := newBaseModule(android.HostAndDeviceSupported, android.MultilibBoth)
+	module.sanitize = &sanitize{}
 	module.linker = &objectLinker{
-		baseLinker: NewBaseLinker(nil),
+		baseLinker: NewBaseLinker(module.sanitize),
 	}
 	module.compiler = NewBaseCompiler()
 
@@ -66,13 +81,18 @@
 		deps.LateSharedLibs = append(deps.LateSharedLibs, "libc")
 	}
 
+	deps.HeaderLibs = append(deps.HeaderLibs, object.Properties.Header_libs...)
 	deps.ObjFiles = append(deps.ObjFiles, object.Properties.Objs...)
 	return deps
 }
 
-func (*objectLinker) linkerFlags(ctx ModuleContext, flags Flags) Flags {
-	flags.LdFlags = append(flags.LdFlags, ctx.toolchain().ToolchainClangLdflags())
+func (object *objectLinker) linkerFlags(ctx ModuleContext, flags Flags) Flags {
+	flags.Global.LdFlags = append(flags.Global.LdFlags, ctx.toolchain().ToolchainClangLdflags())
 
+	if lds := android.OptionalPathForModuleSrc(ctx, object.Properties.Linker_script); lds.Valid() {
+		flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-T,"+lds.String())
+		flags.LdFlagsDeps = append(flags.LdFlagsDeps, lds.Path())
+	}
 	return flags
 }
 
@@ -84,7 +104,7 @@
 	var outputFile android.Path
 	builderFlags := flagsToBuilderFlags(flags)
 
-	if len(objs.objFiles) == 1 {
+	if len(objs.objFiles) == 1 && String(object.Properties.Linker_script) == "" {
 		outputFile = objs.objFiles[0]
 
 		if String(object.Properties.Prefix_symbols) != "" {
@@ -104,7 +124,7 @@
 			output = input
 		}
 
-		TransformObjsToObj(ctx, objs.objFiles, builderFlags, output)
+		TransformObjsToObj(ctx, objs.objFiles, builderFlags, output, flags.LdFlagsDeps)
 	}
 
 	ctx.CheckbuildFile(outputFile)
@@ -118,3 +138,7 @@
 func (object *objectLinker) nativeCoverage() bool {
 	return true
 }
+
+func (object *objectLinker) coverageOutputFilePath() android.OptionalPath {
+	return android.OptionalPath{}
+}
diff --git a/cc/object_test.go b/cc/object_test.go
new file mode 100644
index 0000000..6ff8a00
--- /dev/null
+++ b/cc/object_test.go
@@ -0,0 +1,31 @@
+// 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 cc
+
+import (
+	"testing"
+)
+
+func TestLinkerScript(t *testing.T) {
+	t.Run("script", func(t *testing.T) {
+		testCc(t, `
+		cc_object {
+			name: "foo",
+			srcs: ["baz.o"],
+			linker_script: "foo.lds",
+		}`)
+	})
+
+}
diff --git a/cc/pgo.go b/cc/pgo.go
index 4e915ff..0072355 100644
--- a/cc/pgo.go
+++ b/cc/pgo.go
@@ -89,18 +89,18 @@
 }
 
 func (props *PgoProperties) addProfileGatherFlags(ctx ModuleContext, flags Flags) Flags {
-	flags.CFlags = append(flags.CFlags, props.Pgo.Cflags...)
+	flags.Local.CFlags = append(flags.Local.CFlags, props.Pgo.Cflags...)
 
 	if props.isInstrumentation() {
-		flags.CFlags = append(flags.CFlags, profileInstrumentFlag)
+		flags.Local.CFlags = append(flags.Local.CFlags, profileInstrumentFlag)
 		// The profile runtime is added below in deps().  Add the below
 		// flag, which is the only other link-time action performed by
 		// the Clang driver during link.
-		flags.LdFlags = append(flags.LdFlags, "-u__llvm_profile_runtime")
+		flags.Local.LdFlags = append(flags.Local.LdFlags, "-u__llvm_profile_runtime")
 	}
 	if props.isSampling() {
-		flags.CFlags = append(flags.CFlags, profileSamplingFlag)
-		flags.LdFlags = append(flags.LdFlags, profileSamplingFlag)
+		flags.Local.CFlags = append(flags.Local.CFlags, profileSamplingFlag)
+		flags.Local.LdFlags = append(flags.Local.LdFlags, profileSamplingFlag)
 	}
 	return flags
 }
@@ -170,8 +170,8 @@
 		profileFilePath := profileFile.Path()
 		profileUseFlags := props.profileUseFlags(ctx, profileFilePath.String())
 
-		flags.CFlags = append(flags.CFlags, profileUseFlags...)
-		flags.LdFlags = append(flags.LdFlags, profileUseFlags...)
+		flags.Local.CFlags = append(flags.Local.CFlags, profileUseFlags...)
+		flags.Local.LdFlags = append(flags.Local.LdFlags, profileUseFlags...)
 
 		// Update CFlagsDeps and LdFlagsDeps so the module is rebuilt
 		// if profileFile gets updated
@@ -210,11 +210,6 @@
 		ctx.ModuleErrorf("PGO specification is missing properties: " + missingProps)
 	}
 
-	// Sampling not supported yet
-	if isSampling {
-		ctx.PropertyErrorf("pgo.sampling", "\"sampling\" is not supported yet)")
-	}
-
 	if isSampling && isInstrumentation {
 		ctx.PropertyErrorf("pgo", "Exactly one of \"instrumentation\" and \"sampling\" properties must be set")
 	}
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index dc6c43a..b0cf489 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -19,9 +19,13 @@
 )
 
 func init() {
-	android.RegisterModuleType("cc_prebuilt_library_shared", prebuiltSharedLibraryFactory)
-	android.RegisterModuleType("cc_prebuilt_library_static", prebuiltStaticLibraryFactory)
-	android.RegisterModuleType("cc_prebuilt_binary", prebuiltBinaryFactory)
+	RegisterPrebuiltBuildComponents(android.InitRegistrationContext)
+}
+
+func RegisterPrebuiltBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("cc_prebuilt_library_shared", PrebuiltSharedLibraryFactory)
+	ctx.RegisterModuleType("cc_prebuilt_library_static", PrebuiltStaticLibraryFactory)
+	ctx.RegisterModuleType("cc_prebuilt_binary", prebuiltBinaryFactory)
 }
 
 type prebuiltLinkerInterface interface {
@@ -90,6 +94,7 @@
 		p.libraryDecorator.reexportSystemDirs(deps.ReexportedSystemDirs...)
 		p.libraryDecorator.reexportFlags(deps.ReexportedFlags...)
 		p.libraryDecorator.reexportDeps(deps.ReexportedDeps...)
+		p.libraryDecorator.addExportedGeneratedHeaders(deps.ReexportedGeneratedHeaders...)
 
 		builderFlags := flagsToBuilderFlags(flags)
 
@@ -97,7 +102,7 @@
 
 		if p.shared() {
 			p.unstrippedOutputFile = in
-			libName := ctx.baseModuleName() + flags.Toolchain.ShlibSuffix()
+			libName := p.libraryDecorator.getLibName(ctx) + flags.Toolchain.ShlibSuffix()
 			if p.needsStrip(ctx) {
 				stripped := android.PathForModuleOut(ctx, "stripped", libName)
 				p.stripExecutableOrSharedLib(ctx, in, stripped, builderFlags)
@@ -129,16 +134,8 @@
 	p.properties.Srcs = nil
 }
 
-// cc_prebuilt_library_shared installs a precompiled shared library that are
-// listed in the srcs property in the device's directory.
-func prebuiltSharedLibraryFactory() android.Module {
-	module, _ := NewPrebuiltSharedLibrary(android.HostAndDeviceSupported)
-	return module.Init()
-}
-
-func NewPrebuiltSharedLibrary(hod android.HostOrDeviceSupported) (*Module, *libraryDecorator) {
+func NewPrebuiltLibrary(hod android.HostOrDeviceSupported) (*Module, *libraryDecorator) {
 	module, library := NewLibrary(hod)
-	library.BuildOnlyShared()
 	module.compiler = nil
 
 	prebuilt := &prebuiltLibraryLinker{
@@ -150,7 +147,23 @@
 
 	android.InitPrebuiltModule(module, &prebuilt.properties.Srcs)
 
-	// Prebuilt libraries can be included in APEXes
+	// Prebuilt libraries can be used in SDKs.
+	android.InitSdkAwareModule(module)
+	return module, library
+}
+
+// cc_prebuilt_library_shared installs a precompiled shared library that are
+// listed in the srcs property in the device's directory.
+func PrebuiltSharedLibraryFactory() android.Module {
+	module, _ := NewPrebuiltSharedLibrary(android.HostAndDeviceSupported)
+	return module.Init()
+}
+
+func NewPrebuiltSharedLibrary(hod android.HostOrDeviceSupported) (*Module, *libraryDecorator) {
+	module, library := NewPrebuiltLibrary(hod)
+	library.BuildOnlyShared()
+
+	// Prebuilt shared libraries can be included in APEXes
 	android.InitApexModule(module)
 
 	return module, library
@@ -158,24 +171,14 @@
 
 // cc_prebuilt_library_static installs a precompiled static library that are
 // listed in the srcs property in the device's directory.
-func prebuiltStaticLibraryFactory() android.Module {
+func PrebuiltStaticLibraryFactory() android.Module {
 	module, _ := NewPrebuiltStaticLibrary(android.HostAndDeviceSupported)
 	return module.Init()
 }
 
 func NewPrebuiltStaticLibrary(hod android.HostOrDeviceSupported) (*Module, *libraryDecorator) {
-	module, library := NewLibrary(hod)
+	module, library := NewPrebuiltLibrary(hod)
 	library.BuildOnlyStatic()
-	module.compiler = nil
-
-	prebuilt := &prebuiltLibraryLinker{
-		libraryDecorator: library,
-	}
-	module.linker = prebuilt
-
-	module.AddProperties(&prebuilt.properties)
-
-	android.InitPrebuiltModule(module, &prebuilt.properties.Srcs)
 	return module, library
 }
 
diff --git a/cc/prebuilt_test.go b/cc/prebuilt_test.go
index 98d78e8..3d809fc 100644
--- a/cc/prebuilt_test.go
+++ b/cc/prebuilt_test.go
@@ -68,18 +68,11 @@
 		"libe.a":  nil,
 	}
 
-	config := android.TestArchConfig(buildDir, nil)
+	config := TestConfig(buildDir, android.Android, nil, bp, fs)
 
-	ctx := CreateTestContext(bp, fs, android.Android)
+	ctx := CreateTestContext()
 
-	ctx.RegisterModuleType("cc_prebuilt_library_shared", android.ModuleFactoryAdaptor(prebuiltSharedLibraryFactory))
-	ctx.RegisterModuleType("cc_prebuilt_library_static", android.ModuleFactoryAdaptor(prebuiltStaticLibraryFactory))
-	ctx.RegisterModuleType("cc_prebuilt_binary", android.ModuleFactoryAdaptor(prebuiltBinaryFactory))
-
-	ctx.PreArchMutators(android.RegisterPrebuiltsPreArchMutators)
-	ctx.PostDepsMutators(android.RegisterPrebuiltsPostDepsMutators)
-
-	ctx.Register()
+	ctx.Register(config)
 
 	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
 	android.FailIfErrored(t, errs)
@@ -87,15 +80,15 @@
 	android.FailIfErrored(t, errs)
 
 	// Verify that all the modules exist and that their dependencies were connected correctly
-	liba := ctx.ModuleForTests("liba", "android_arm64_armv8-a_core_shared").Module()
-	libb := ctx.ModuleForTests("libb", "android_arm64_armv8-a_core_static").Module()
-	libd := ctx.ModuleForTests("libd", "android_arm64_armv8-a_core_shared").Module()
-	libe := ctx.ModuleForTests("libe", "android_arm64_armv8-a_core_static").Module()
+	liba := ctx.ModuleForTests("liba", "android_arm64_armv8-a_shared").Module()
+	libb := ctx.ModuleForTests("libb", "android_arm64_armv8-a_static").Module()
+	libd := ctx.ModuleForTests("libd", "android_arm64_armv8-a_shared").Module()
+	libe := ctx.ModuleForTests("libe", "android_arm64_armv8-a_static").Module()
 
-	prebuiltLiba := ctx.ModuleForTests("prebuilt_liba", "android_arm64_armv8-a_core_shared").Module()
-	prebuiltLibb := ctx.ModuleForTests("prebuilt_libb", "android_arm64_armv8-a_core_static").Module()
-	prebuiltLibd := ctx.ModuleForTests("prebuilt_libd", "android_arm64_armv8-a_core_shared").Module()
-	prebuiltLibe := ctx.ModuleForTests("prebuilt_libe", "android_arm64_armv8-a_core_static").Module()
+	prebuiltLiba := ctx.ModuleForTests("prebuilt_liba", "android_arm64_armv8-a_shared").Module()
+	prebuiltLibb := ctx.ModuleForTests("prebuilt_libb", "android_arm64_armv8-a_static").Module()
+	prebuiltLibd := ctx.ModuleForTests("prebuilt_libd", "android_arm64_armv8-a_shared").Module()
+	prebuiltLibe := ctx.ModuleForTests("prebuilt_libe", "android_arm64_armv8-a_static").Module()
 
 	hasDep := func(m android.Module, wantDep android.Module) bool {
 		t.Helper()
diff --git a/cc/proto.go b/cc/proto.go
index f818edc..ae988ec 100644
--- a/cc/proto.go
+++ b/cc/proto.go
@@ -114,13 +114,13 @@
 }
 
 func protoFlags(ctx ModuleContext, flags Flags, p *android.ProtoProperties) Flags {
-	flags.CFlags = append(flags.CFlags, "-DGOOGLE_PROTOBUF_NO_RTTI")
+	flags.Local.CFlags = append(flags.Local.CFlags, "-DGOOGLE_PROTOBUF_NO_RTTI")
 
 	flags.proto = android.GetProtoFlags(ctx, p)
 	if flags.proto.CanonicalPathFromRoot {
-		flags.GlobalFlags = append(flags.GlobalFlags, "-I"+flags.proto.SubDir.String())
+		flags.Local.CommonFlags = append(flags.Local.CommonFlags, "-I"+flags.proto.SubDir.String())
 	}
-	flags.GlobalFlags = append(flags.GlobalFlags, "-I"+flags.proto.Dir.String())
+	flags.Local.CommonFlags = append(flags.Local.CommonFlags, "-I"+flags.proto.Dir.String())
 
 	if String(p.Proto.Plugin) == "" {
 		var plugin string
diff --git a/cc/proto_test.go b/cc/proto_test.go
index 4f0de78..f8bbd26 100644
--- a/cc/proto_test.go
+++ b/cc/proto_test.go
@@ -29,7 +29,7 @@
 			srcs: ["a.proto"],
 		}`)
 
-		proto := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_core_shared").Output("proto/a.pb.cc")
+		proto := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Output("proto/a.pb.cc")
 
 		if cmd := proto.RuleParams.Command; !strings.Contains(cmd, "--cpp_out=") {
 			t.Errorf("expected '--cpp_out' in %q", cmd)
@@ -53,7 +53,7 @@
 
 		buildOS := android.BuildOs.String()
 
-		proto := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_core_shared").Output("proto/a.pb.cc")
+		proto := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Output("proto/a.pb.cc")
 		foobar := ctx.ModuleForTests("protoc-gen-foobar", buildOS+"_x86_64")
 
 		cmd := proto.RuleParams.Command
diff --git a/cc/rs.go b/cc/rs.go
index fbc6bfb..9149e17 100644
--- a/cc/rs.go
+++ b/cc/rs.go
@@ -29,7 +29,7 @@
 			// Use RenderScript prebuilts for unbundled builds but not PDK builds
 			return filepath.Join("prebuilts/sdk/tools", runtime.GOOS, "bin/llvm-rs-cc")
 		} else {
-			return pctx.HostBinToolPath(ctx, "llvm-rs-cc").String()
+			return ctx.Config().HostToolPath(ctx, "llvm-rs-cc").String()
 		}
 	})
 }
@@ -51,7 +51,7 @@
 		"depFiles", "outDir", "rsFlags", "stampFile")
 )
 
-// Takes a path to a .rs or .fs file, and returns a path to a generated ScriptC_*.cpp file
+// Takes a path to a .rscript or .fs file, and returns a path to a generated ScriptC_*.cpp file
 // This has to match the logic in llvm-rs-cc in DetermineOutputFile.
 func rsGeneratedCppFile(ctx android.ModuleContext, rsFile android.Path) android.WritablePath {
 	fileName := strings.TrimSuffix(rsFile.Base(), rsFile.Ext())
@@ -123,7 +123,7 @@
 	rootRsIncludeDirs := android.PathsForSource(ctx, properties.Renderscript.Include_dirs)
 	flags.rsFlags = append(flags.rsFlags, includeDirsToFlags(rootRsIncludeDirs))
 
-	flags.GlobalFlags = append(flags.GlobalFlags,
+	flags.Local.CommonFlags = append(flags.Local.CommonFlags,
 		"-I"+android.PathForModuleGen(ctx, "rs").String(),
 		"-Iframeworks/rs",
 		"-Iframeworks/rs/cpp",
diff --git a/cc/sabi.go b/cc/sabi.go
index ae7b31d..8cef170 100644
--- a/cc/sabi.go
+++ b/cc/sabi.go
@@ -70,20 +70,22 @@
 func (sabimod *sabi) flags(ctx ModuleContext, flags Flags) Flags {
 	// Assuming that the cflags which clang LibTooling tools cannot
 	// understand have not been converted to ninja variables yet.
-	flags.ToolingCFlags = filterOutWithPrefix(flags.CFlags, config.ClangLibToolingUnknownCflags)
-	flags.ToolingCppFlags = filterOutWithPrefix(flags.CppFlags, config.ClangLibToolingUnknownCflags)
+	flags.Local.ToolingCFlags = filterOutWithPrefix(flags.Local.CFlags, config.ClangLibToolingUnknownCflags)
+	flags.Global.ToolingCFlags = filterOutWithPrefix(flags.Global.CFlags, config.ClangLibToolingUnknownCflags)
+	flags.Local.ToolingCppFlags = filterOutWithPrefix(flags.Local.CppFlags, config.ClangLibToolingUnknownCflags)
+	flags.Global.ToolingCppFlags = filterOutWithPrefix(flags.Global.CppFlags, config.ClangLibToolingUnknownCflags)
 
 	return flags
 }
 
 func sabiDepsMutator(mctx android.TopDownMutatorContext) {
 	if c, ok := mctx.Module().(*Module); ok &&
-		((c.isVndk() && c.useVndk()) || inList(c.Name(), *llndkLibraries(mctx.Config())) ||
+		((c.IsVndk() && c.UseVndk()) || c.isLlndk(mctx.Config()) ||
 			(c.sabi != nil && c.sabi.Properties.CreateSAbiDumps)) {
 		mctx.VisitDirectDeps(func(m android.Module) {
 			tag := mctx.OtherModuleDependencyTag(m)
 			switch tag {
-			case staticDepTag, staticExportDepTag, lateStaticDepTag, wholeStaticDepTag:
+			case StaticDepTag, staticExportDepTag, lateStaticDepTag, wholeStaticDepTag:
 
 				cc, _ := m.(*Module)
 				if cc == nil {
@@ -94,3 +96,9 @@
 		})
 	}
 }
+
+func addLsdumpPath(lsdumpPath string) {
+	sabiLock.Lock()
+	lsdumpPaths = append(lsdumpPaths, lsdumpPath)
+	sabiLock.Unlock()
+}
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 0af0659..c4aeb96 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -31,17 +31,24 @@
 	// understand also need to be added to ClangLibToolingUnknownCflags in
 	// cc/config/clang.go
 
-	asanCflags  = []string{"-fno-omit-frame-pointer"}
+	asanCflags = []string{
+		"-fno-omit-frame-pointer",
+		"-fno-experimental-new-pass-manager",
+	}
 	asanLdflags = []string{"-Wl,-u,__asan_preinit"}
-	asanLibs    = []string{"libasan"}
 
-	// TODO(pcc): Stop passing -hwasan-allow-ifunc here once it has been made
-	// the default.
 	hwasanCflags = []string{"-fno-omit-frame-pointer", "-Wno-frame-larger-than=",
-		"-mllvm", "-hwasan-create-frame-descriptions=0",
-		"-mllvm", "-hwasan-allow-ifunc",
 		"-fsanitize-hwaddress-abi=platform",
-		"-fno-experimental-new-pass-manager"}
+		"-fno-experimental-new-pass-manager",
+		// The following improves debug location information
+		// availability at the cost of its accuracy. It increases
+		// the likelihood of a stack variable's frame offset
+		// to be recorded in the debug info, which is important
+		// for the quality of hwasan reports. The downside is a
+		// higher number of "optimized out" stack variables.
+		// b/112437883.
+		"-mllvm", "-instcombine-lower-dbg-declare=0",
+	}
 
 	cfiCflags = []string{"-flto", "-fsanitize-cfi-cross-dso",
 		"-fsanitize-blacklist=external/compiler-rt/lib/cfi/cfi_blacklist.txt"}
@@ -58,7 +65,8 @@
 
 	minimalRuntimeFlags = []string{"-fsanitize-minimal-runtime", "-fno-sanitize-trap=integer,undefined",
 		"-fno-sanitize-recover=integer,undefined"}
-	hwasanGlobalOptions = []string{"heap_history_size=1023,stack_history_size=512"}
+	hwasanGlobalOptions = []string{"heap_history_size=1023", "stack_history_size=512",
+		"export_memory_stats=0", "max_malloc_fill_size=0"}
 )
 
 type sanitizerType int
@@ -125,6 +133,10 @@
 	}
 }
 
+func (t sanitizerType) incompatibleWithCfi() bool {
+	return t == asan || t == fuzzer || t == hwasan
+}
+
 type SanitizeProperties struct {
 	// enable AddressSanitizer, ThreadSanitizer, or UndefinedBehaviorSanitizer
 	Sanitize struct {
@@ -391,7 +403,6 @@
 
 	if ctx.Device() {
 		if Bool(sanitize.Properties.Sanitize.Address) {
-			deps.StaticLibs = append(deps.StaticLibs, asanLibs...)
 			// Compiling asan and having libc_scudo in the same
 			// executable will cause the executable to crash.
 			// Remove libc_scudo since it is only used to override
@@ -424,8 +435,9 @@
 	minimalRuntimePath := "${config.ClangAsanLibDir}/" + minimalRuntimeLib
 
 	if ctx.Device() && sanitize.Properties.MinimalRuntimeDep {
-		flags.LdFlags = append(flags.LdFlags, minimalRuntimePath)
-		flags.LdFlags = append(flags.LdFlags, "-Wl,--exclude-libs,"+minimalRuntimeLib)
+		flags.Local.LdFlags = append(flags.Local.LdFlags,
+			minimalRuntimePath,
+			"-Wl,--exclude-libs,"+minimalRuntimeLib)
 	}
 	if !sanitize.Properties.SanitizerEnabled && !sanitize.Properties.UbsanRuntimeDep {
 		return flags
@@ -437,15 +449,15 @@
 			// TODO: put in flags?
 			flags.RequiredInstructionSet = "arm"
 		}
-		flags.CFlags = append(flags.CFlags, asanCflags...)
-		flags.LdFlags = append(flags.LdFlags, asanLdflags...)
+		flags.Local.CFlags = append(flags.Local.CFlags, asanCflags...)
+		flags.Local.LdFlags = append(flags.Local.LdFlags, asanLdflags...)
 
 		if ctx.Host() {
 			// -nodefaultlibs (provided with libc++) prevents the driver from linking
 			// libraries needed with -fsanitize=address. http://b/18650275 (WAI)
-			flags.LdFlags = append(flags.LdFlags, "-Wl,--no-as-needed")
+			flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--no-as-needed")
 		} else {
-			flags.CFlags = append(flags.CFlags, "-mllvm", "-asan-globals=0")
+			flags.Local.CFlags = append(flags.Local.CFlags, "-mllvm", "-asan-globals=0")
 			if ctx.bootstrap() {
 				flags.DynamicLinker = "/system/bin/bootstrap/linker_asan"
 			} else {
@@ -458,16 +470,39 @@
 	}
 
 	if Bool(sanitize.Properties.Sanitize.Hwaddress) {
-		flags.CFlags = append(flags.CFlags, hwasanCflags...)
+		flags.Local.CFlags = append(flags.Local.CFlags, hwasanCflags...)
 	}
 
 	if Bool(sanitize.Properties.Sanitize.Fuzzer) {
-		flags.CFlags = append(flags.CFlags, "-fsanitize=fuzzer-no-link")
-		flags.LdFlags = append(flags.LdFlags, "-fsanitize=fuzzer-no-link")
+		flags.Local.CFlags = append(flags.Local.CFlags, "-fsanitize=fuzzer-no-link")
 
 		// TODO(b/131771163): LTO and Fuzzer support is mutually incompatible.
-		_, flags.LdFlags = removeFromList("-flto", flags.LdFlags)
-		flags.LdFlags = append(flags.LdFlags, "-fno-lto")
+		_, flags.Local.LdFlags = removeFromList("-flto", flags.Local.LdFlags)
+		_, flags.Local.CFlags = removeFromList("-flto", flags.Local.CFlags)
+		flags.Local.LdFlags = append(flags.Local.LdFlags, "-fno-lto")
+		flags.Local.CFlags = append(flags.Local.CFlags, "-fno-lto")
+
+		// TODO(b/142430592): Upstream linker scripts for sanitizer runtime libraries
+		// discard the sancov_lowest_stack symbol, because it's emulated TLS (and thus
+		// doesn't match the linker script due to the "__emutls_v." prefix).
+		flags.Local.LdFlags = append(flags.Local.LdFlags, "-fno-sanitize-coverage=stack-depth")
+		flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize-coverage=stack-depth")
+
+		// TODO(b/133876586): Experimental PM breaks sanitizer coverage.
+		flags.Local.CFlags = append(flags.Local.CFlags, "-fno-experimental-new-pass-manager")
+
+		// Disable fortify for fuzzing builds. Generally, we'll be building with
+		// UBSan or ASan here and the fortify checks pollute the stack traces.
+		flags.Local.CFlags = append(flags.Local.CFlags, "-U_FORTIFY_SOURCE")
+
+		// Build fuzzer-sanitized libraries with an $ORIGIN DT_RUNPATH. Android's
+		// linker uses DT_RUNPATH, not DT_RPATH. When we deploy cc_fuzz targets and
+		// their libraries to /data/fuzz/<arch>/lib, any transient shared library gets
+		// the DT_RUNPATH from the shared library above it, and not the executable,
+		// meaning that the lookup falls back to the system. Adding the $ORIGIN to the
+		// DT_RUNPATH here means that transient shared libraries can be found
+		// colocated with their parents.
+		flags.Local.LdFlags = append(flags.Local.LdFlags, `-Wl,-rpath,\$$ORIGIN`)
 	}
 
 	if Bool(sanitize.Properties.Sanitize.Cfi) {
@@ -477,75 +512,75 @@
 			flags.RequiredInstructionSet = "thumb"
 		}
 
-		flags.CFlags = append(flags.CFlags, cfiCflags...)
-		flags.AsFlags = append(flags.AsFlags, cfiAsflags...)
+		flags.Local.CFlags = append(flags.Local.CFlags, cfiCflags...)
+		flags.Local.AsFlags = append(flags.Local.AsFlags, cfiAsflags...)
 		// Only append the default visibility flag if -fvisibility has not already been set
 		// to hidden.
-		if !inList("-fvisibility=hidden", flags.CFlags) {
-			flags.CFlags = append(flags.CFlags, "-fvisibility=default")
+		if !inList("-fvisibility=hidden", flags.Local.CFlags) {
+			flags.Local.CFlags = append(flags.Local.CFlags, "-fvisibility=default")
 		}
-		flags.LdFlags = append(flags.LdFlags, cfiLdflags...)
+		flags.Local.LdFlags = append(flags.Local.LdFlags, cfiLdflags...)
 
 		if ctx.staticBinary() {
-			_, flags.CFlags = removeFromList("-fsanitize-cfi-cross-dso", flags.CFlags)
-			_, flags.LdFlags = removeFromList("-fsanitize-cfi-cross-dso", flags.LdFlags)
+			_, flags.Local.CFlags = removeFromList("-fsanitize-cfi-cross-dso", flags.Local.CFlags)
+			_, flags.Local.LdFlags = removeFromList("-fsanitize-cfi-cross-dso", flags.Local.LdFlags)
 		}
 	}
 
 	if Bool(sanitize.Properties.Sanitize.Integer_overflow) {
-		flags.CFlags = append(flags.CFlags, intOverflowCflags...)
+		flags.Local.CFlags = append(flags.Local.CFlags, intOverflowCflags...)
 	}
 
 	if len(sanitize.Properties.Sanitizers) > 0 {
 		sanitizeArg := "-fsanitize=" + strings.Join(sanitize.Properties.Sanitizers, ",")
 
-		flags.CFlags = append(flags.CFlags, sanitizeArg)
-		flags.AsFlags = append(flags.AsFlags, sanitizeArg)
+		flags.Local.CFlags = append(flags.Local.CFlags, sanitizeArg)
+		flags.Local.AsFlags = append(flags.Local.AsFlags, sanitizeArg)
 		if ctx.Host() {
 			// Host sanitizers only link symbols in the final executable, so
 			// there will always be undefined symbols in intermediate libraries.
-			_, flags.LdFlags = removeFromList("-Wl,--no-undefined", flags.LdFlags)
-			flags.LdFlags = append(flags.LdFlags, sanitizeArg)
+			_, flags.Global.LdFlags = removeFromList("-Wl,--no-undefined", flags.Global.LdFlags)
+			flags.Local.LdFlags = append(flags.Local.LdFlags, sanitizeArg)
 		} else {
 			if enableMinimalRuntime(sanitize) {
-				flags.CFlags = append(flags.CFlags, strings.Join(minimalRuntimeFlags, " "))
+				flags.Local.CFlags = append(flags.Local.CFlags, strings.Join(minimalRuntimeFlags, " "))
 				flags.libFlags = append([]string{minimalRuntimePath}, flags.libFlags...)
-				flags.LdFlags = append(flags.LdFlags, "-Wl,--exclude-libs,"+minimalRuntimeLib)
+				flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--exclude-libs,"+minimalRuntimeLib)
 			}
 		}
 
 		if Bool(sanitize.Properties.Sanitize.Fuzzer) {
 			// When fuzzing, we wish to crash with diagnostics on any bug.
-			flags.CFlags = append(flags.CFlags, "-fno-sanitize-trap=all", "-fno-sanitize-recover=all")
+			flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize-trap=all", "-fno-sanitize-recover=all")
 		} else if ctx.Host() {
-			flags.CFlags = append(flags.CFlags, "-fno-sanitize-recover=all")
+			flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize-recover=all")
 		} else {
-			flags.CFlags = append(flags.CFlags, "-fsanitize-trap=all", "-ftrap-function=abort")
+			flags.Local.CFlags = append(flags.Local.CFlags, "-fsanitize-trap=all", "-ftrap-function=abort")
 		}
 		// http://b/119329758, Android core does not boot up with this sanitizer yet.
-		if toDisableImplicitIntegerChange(flags.CFlags) {
-			flags.CFlags = append(flags.CFlags, "-fno-sanitize=implicit-integer-sign-change")
+		if toDisableImplicitIntegerChange(flags.Local.CFlags) {
+			flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize=implicit-integer-sign-change")
 		}
 	}
 
 	if len(sanitize.Properties.DiagSanitizers) > 0 {
-		flags.CFlags = append(flags.CFlags, "-fno-sanitize-trap="+strings.Join(sanitize.Properties.DiagSanitizers, ","))
+		flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize-trap="+strings.Join(sanitize.Properties.DiagSanitizers, ","))
 	}
 	// FIXME: enable RTTI if diag + (cfi or vptr)
 
 	if sanitize.Properties.Sanitize.Recover != nil {
-		flags.CFlags = append(flags.CFlags, "-fsanitize-recover="+
+		flags.Local.CFlags = append(flags.Local.CFlags, "-fsanitize-recover="+
 			strings.Join(sanitize.Properties.Sanitize.Recover, ","))
 	}
 
 	if sanitize.Properties.Sanitize.Diag.No_recover != nil {
-		flags.CFlags = append(flags.CFlags, "-fno-sanitize-recover="+
+		flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize-recover="+
 			strings.Join(sanitize.Properties.Sanitize.Diag.No_recover, ","))
 	}
 
 	blacklist := android.OptionalPathForModuleSrc(ctx, sanitize.Properties.Sanitize.Blacklist)
 	if blacklist.Valid() {
-		flags.CFlags = append(flags.CFlags, "-fsanitize-blacklist="+blacklist.String())
+		flags.Local.CFlags = append(flags.Local.CFlags, "-fsanitize-blacklist="+blacklist.String())
 		flags.CFlagsDeps = append(flags.CFlagsDeps, blacklist.Path())
 	}
 
@@ -553,16 +588,18 @@
 }
 
 func (sanitize *sanitize) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
-	// Add a suffix for CFI-enabled static libraries to allow surfacing both to make without a
-	// name conflict.
-	if ret.Class == "STATIC_LIBRARIES" && Bool(sanitize.Properties.Sanitize.Cfi) {
-		ret.SubName += ".cfi"
-	}
-	if ret.Class == "STATIC_LIBRARIES" && Bool(sanitize.Properties.Sanitize.Hwaddress) {
-		ret.SubName += ".hwasan"
-	}
-	if ret.Class == "STATIC_LIBRARIES" && Bool(sanitize.Properties.Sanitize.Scs) {
-		ret.SubName += ".scs"
+	// Add a suffix for cfi/hwasan/scs-enabled static/header libraries to allow surfacing
+	// both the sanitized and non-sanitized variants to make without a name conflict.
+	if ret.Class == "STATIC_LIBRARIES" || ret.Class == "HEADER_LIBRARIES" {
+		if Bool(sanitize.Properties.Sanitize.Cfi) {
+			ret.SubName += ".cfi"
+		}
+		if Bool(sanitize.Properties.Sanitize.Hwaddress) {
+			ret.SubName += ".hwasan"
+		}
+		if Bool(sanitize.Properties.Sanitize.Scs) {
+			ret.SubName += ".scs"
+		}
 	}
 }
 
@@ -657,8 +694,8 @@
 }
 
 func isSanitizableDependencyTag(tag blueprint.DependencyTag) bool {
-	t, ok := tag.(dependencyTag)
-	return ok && t.library || t == reuseObjTag
+	t, ok := tag.(DependencyTag)
+	return ok && t.Library || t == reuseObjTag || t == objDepTag
 }
 
 // Propagate sanitizer requirements down from binaries
@@ -701,8 +738,8 @@
 			if !isSanitizableDependencyTag(mctx.OtherModuleDependencyTag(child)) {
 				return false
 			}
-			if d, ok := child.(*Module); ok && d.static() && d.sanitize != nil {
 
+			if d, ok := child.(*Module); ok && d.static() && d.sanitize != nil {
 				if enableMinimalRuntime(d.sanitize) {
 					// If a static dependency is built with the minimal runtime,
 					// make sure we include the ubsan minimal runtime.
@@ -713,8 +750,17 @@
 					// make sure we include the ubsan runtime.
 					c.sanitize.Properties.UbsanRuntimeDep = true
 				}
+
+				if c.sanitize.Properties.MinimalRuntimeDep &&
+					c.sanitize.Properties.UbsanRuntimeDep {
+					// both flags that this mutator might set are true, so don't bother recursing
+					return false
+				}
+
+				return true
+			} else {
+				return false
 			}
-			return true
 		})
 	}
 }
@@ -818,12 +864,14 @@
 
 		// Determine the runtime library required
 		runtimeLibrary := ""
+		var extraStaticDeps []string
 		toolchain := c.toolchain(mctx)
 		if Bool(c.sanitize.Properties.Sanitize.Address) {
 			runtimeLibrary = config.AddressSanitizerRuntimeLibrary(toolchain)
 		} else if Bool(c.sanitize.Properties.Sanitize.Hwaddress) {
 			if c.staticBinary() {
 				runtimeLibrary = config.HWAddressSanitizerStaticLibrary(toolchain)
+				extraStaticDeps = []string{"libdl"}
 			} else {
 				runtimeLibrary = config.HWAddressSanitizerRuntimeLibrary(toolchain)
 			}
@@ -835,12 +883,13 @@
 			} else {
 				runtimeLibrary = config.ScudoRuntimeLibrary(toolchain)
 			}
-		} else if len(diagSanitizers) > 0 || c.sanitize.Properties.UbsanRuntimeDep {
+		} else if len(diagSanitizers) > 0 || c.sanitize.Properties.UbsanRuntimeDep ||
+			Bool(c.sanitize.Properties.Sanitize.Fuzzer) {
 			runtimeLibrary = config.UndefinedBehaviorSanitizerRuntimeLibrary(toolchain)
 		}
 
 		if mctx.Device() && runtimeLibrary != "" {
-			if inList(runtimeLibrary, *llndkLibraries(mctx.Config())) && !c.static() && c.useVndk() {
+			if isLlndkLibrary(runtimeLibrary, mctx.Config()) && !c.static() && c.UseVndk() {
 				runtimeLibrary = runtimeLibrary + llndkLibrarySuffix
 			}
 
@@ -853,18 +902,16 @@
 			// added to libFlags and LOCAL_SHARED_LIBRARIES by cc.Module
 			if c.staticBinary() {
 				// static executable gets static runtime libs
-				mctx.AddFarVariationDependencies([]blueprint.Variation{
+				mctx.AddFarVariationDependencies(append(mctx.Target().Variations(), []blueprint.Variation{
 					{Mutator: "link", Variation: "static"},
-					{Mutator: "image", Variation: c.imageVariation()},
-					{Mutator: "arch", Variation: mctx.Target().String()},
-				}, staticDepTag, runtimeLibrary)
-			} else if !c.static() {
+					c.ImageVariation(),
+				}...), StaticDepTag, append([]string{runtimeLibrary}, extraStaticDeps...)...)
+			} else if !c.static() && !c.header() {
 				// dynamic executable and shared libs get shared runtime libs
-				mctx.AddFarVariationDependencies([]blueprint.Variation{
+				mctx.AddFarVariationDependencies(append(mctx.Target().Variations(), []blueprint.Variation{
 					{Mutator: "link", Variation: "shared"},
-					{Mutator: "image", Variation: c.imageVariation()},
-					{Mutator: "arch", Variation: mctx.Target().String()},
-				}, earlySharedDepTag, runtimeLibrary)
+					c.ImageVariation(),
+				}...), earlySharedDepTag, runtimeLibrary)
 			}
 			// static lib does not have dependency to the runtime library. The
 			// dependency will be added to the executables or shared libs using
@@ -887,108 +934,69 @@
 				modules := mctx.CreateVariations(t.variationName())
 				modules[0].(*Module).sanitize.SetSanitizer(t, true)
 			} else if c.sanitize.isSanitizerEnabled(t) || c.sanitize.Properties.SanitizeDep {
-				// Save original sanitizer status before we assign values to variant
-				// 0 as that overwrites the original.
 				isSanitizerEnabled := c.sanitize.isSanitizerEnabled(t)
+				if mctx.Device() && t.incompatibleWithCfi() {
+					// TODO: Make sure that cfi mutator runs "after" any of the sanitizers that
+					// are incompatible with cfi
+					c.sanitize.SetSanitizer(cfi, false)
+				}
+				if c.static() || c.header() || t == asan || t == fuzzer {
+					// Static and header libs are split into non-sanitized and sanitized variants.
+					// Shared libs are not split. However, for asan and fuzzer, we split even for shared
+					// libs because a library sanitized for asan/fuzzer can't be linked from a library
+					// that isn't sanitized for asan/fuzzer.
+					//
+					// Note for defaultVariation: since we don't split for shared libs but for static/header
+					// libs, it is possible for the sanitized variant of a static/header lib to depend
+					// on non-sanitized variant of a shared lib. Such unfulfilled variation causes an
+					// error when the module is split. defaultVariation is the name of the variation that
+					// will be used when such a dangling dependency occurs during the split of the current
+					// module. By setting it to the name of the sanitized variation, the dangling dependency
+					// is redirected to the sanitized variant of the dependent module.
+					defaultVariation := t.variationName()
+					mctx.SetDefaultDependencyVariation(&defaultVariation)
+					modules := mctx.CreateVariations("", t.variationName())
+					modules[0].(*Module).sanitize.SetSanitizer(t, false)
+					modules[1].(*Module).sanitize.SetSanitizer(t, true)
+					modules[0].(*Module).sanitize.Properties.SanitizeDep = false
+					modules[1].(*Module).sanitize.Properties.SanitizeDep = false
 
-				modules := mctx.CreateVariations("", t.variationName())
-				modules[0].(*Module).sanitize.SetSanitizer(t, false)
-				modules[1].(*Module).sanitize.SetSanitizer(t, true)
+					// For cfi/scs/hwasan, we can export both sanitized and un-sanitized variants
+					// to Make, because the sanitized version has a different suffix in name.
+					// For other types of sanitizers, suppress the variation that is disabled.
+					if t != cfi && t != scs && t != hwasan {
+						if isSanitizerEnabled {
+							modules[0].(*Module).Properties.PreventInstall = true
+							modules[0].(*Module).Properties.HideFromMake = true
+						} else {
+							modules[1].(*Module).Properties.PreventInstall = true
+							modules[1].(*Module).Properties.HideFromMake = true
+						}
+					}
 
-				modules[0].(*Module).sanitize.Properties.SanitizeDep = false
-				modules[1].(*Module).sanitize.Properties.SanitizeDep = false
-
-				// We don't need both variants active for anything but CFI-enabled
-				// target static libraries, so suppress the appropriate variant in
-				// all other cases.
-				if t == cfi {
+					// Export the static lib name to make
 					if c.static() {
-						if !mctx.Device() {
-							if isSanitizerEnabled {
-								modules[0].(*Module).Properties.PreventInstall = true
-								modules[0].(*Module).Properties.HideFromMake = true
+						if t == cfi {
+							appendStringSync(c.Name(), cfiStaticLibs(mctx.Config()), &cfiStaticLibsMutex)
+						} else if t == hwasan {
+							if c.UseVndk() {
+								appendStringSync(c.Name(), hwasanVendorStaticLibs(mctx.Config()),
+									&hwasanStaticLibsMutex)
 							} else {
-								modules[1].(*Module).Properties.PreventInstall = true
-								modules[1].(*Module).Properties.HideFromMake = true
+								appendStringSync(c.Name(), hwasanStaticLibs(mctx.Config()),
+									&hwasanStaticLibsMutex)
 							}
-						} else {
-							cfiStaticLibs := cfiStaticLibs(mctx.Config())
+						}
+					}
+				} else {
+					// Shared libs are not split. Only the sanitized variant is created.
+					modules := mctx.CreateVariations(t.variationName())
+					modules[0].(*Module).sanitize.SetSanitizer(t, true)
+					modules[0].(*Module).sanitize.Properties.SanitizeDep = false
 
-							cfiStaticLibsMutex.Lock()
-							*cfiStaticLibs = append(*cfiStaticLibs, c.Name())
-							cfiStaticLibsMutex.Unlock()
-						}
-					} else {
-						modules[0].(*Module).Properties.PreventInstall = true
-						modules[0].(*Module).Properties.HideFromMake = true
-					}
-				} else if t == asan {
-					if mctx.Device() {
-						// CFI and ASAN are currently mutually exclusive so disable
-						// CFI if this is an ASAN variant.
-						modules[1].(*Module).sanitize.Properties.InSanitizerDir = true
-						modules[1].(*Module).sanitize.SetSanitizer(cfi, false)
-					}
-					if isSanitizerEnabled {
-						modules[0].(*Module).Properties.PreventInstall = true
-						modules[0].(*Module).Properties.HideFromMake = true
-					} else {
-						modules[1].(*Module).Properties.PreventInstall = true
-						modules[1].(*Module).Properties.HideFromMake = true
-					}
-				} else if t == scs {
-					// We don't currently link any static libraries built with make into
-					// libraries built with SCS, so we don't need logic for propagating
-					// SCSness of dependencies into make.
-					if !c.static() {
-						if isSanitizerEnabled {
-							modules[0].(*Module).Properties.PreventInstall = true
-							modules[0].(*Module).Properties.HideFromMake = true
-						} else {
-							modules[1].(*Module).Properties.PreventInstall = true
-							modules[1].(*Module).Properties.HideFromMake = true
-						}
-					}
-				} else if t == fuzzer {
-					// TODO(b/131771163): CFI and fuzzer support are mutually incompatible
-					// as CFI pulls in LTO.
-					if mctx.Device() {
-						modules[1].(*Module).sanitize.SetSanitizer(cfi, false)
-					}
-					if isSanitizerEnabled {
-						modules[0].(*Module).Properties.PreventInstall = true
-						modules[0].(*Module).Properties.HideFromMake = true
-					} else {
-						modules[1].(*Module).Properties.PreventInstall = true
-						modules[1].(*Module).Properties.HideFromMake = true
-					}
-				} else if t == hwasan {
-					if mctx.Device() {
-						// CFI and HWASAN are currently mutually exclusive so disable
-						// CFI if this is an HWASAN variant.
-						modules[1].(*Module).sanitize.SetSanitizer(cfi, false)
-					}
-
-					if c.static() {
-						if c.useVndk() {
-							hwasanVendorStaticLibs := hwasanVendorStaticLibs(mctx.Config())
-							hwasanStaticLibsMutex.Lock()
-							*hwasanVendorStaticLibs = append(*hwasanVendorStaticLibs, c.Name())
-							hwasanStaticLibsMutex.Unlock()
-						} else {
-							hwasanStaticLibs := hwasanStaticLibs(mctx.Config())
-							hwasanStaticLibsMutex.Lock()
-							*hwasanStaticLibs = append(*hwasanStaticLibs, c.Name())
-							hwasanStaticLibsMutex.Unlock()
-						}
-					} else {
-						if isSanitizerEnabled {
-							modules[0].(*Module).Properties.PreventInstall = true
-							modules[0].(*Module).Properties.HideFromMake = true
-						} else {
-							modules[1].(*Module).Properties.PreventInstall = true
-							modules[1].(*Module).Properties.HideFromMake = true
-						}
+					// locate the asan libraries under /data/asan
+					if mctx.Device() && t == asan && isSanitizerEnabled {
+						modules[0].(*Module).sanitize.Properties.InSanitizerDir = true
 					}
 				}
 			}
@@ -1024,6 +1032,12 @@
 	}).(*[]string)
 }
 
+func appendStringSync(item string, list *[]string, mutex *sync.Mutex) {
+	mutex.Lock()
+	*list = append(*list, item)
+	mutex.Unlock()
+}
+
 func enableMinimalRuntime(sanitize *sanitize) bool {
 	if !Bool(sanitize.Properties.Sanitize.Address) &&
 		!Bool(sanitize.Properties.Sanitize.Hwaddress) &&
diff --git a/cc/stl.go b/cc/stl.go
index 1a5dd79..5ccd44a 100644
--- a/cc/stl.go
+++ b/cc/stl.go
@@ -20,13 +20,13 @@
 	"strconv"
 )
 
-func getNdkStlFamily(m *Module) string {
+func getNdkStlFamily(m LinkableInterface) string {
 	family, _ := getNdkStlFamilyAndLinkType(m)
 	return family
 }
 
-func getNdkStlFamilyAndLinkType(m *Module) (string, string) {
-	stl := m.stl.Properties.SelectedStl
+func getNdkStlFamilyAndLinkType(m LinkableInterface) (string, string) {
+	stl := m.SelectedStl()
 	switch stl {
 	case "ndk_libc++_shared":
 		return "libc++", "shared"
@@ -161,12 +161,21 @@
 		} else {
 			deps.StaticLibs = append(deps.StaticLibs, stl.Properties.SelectedStl)
 		}
+		if ctx.Device() && !ctx.useSdk() {
+			// __cxa_demangle is not a part of libc++.so on the device since
+			// it's large and most processes don't need it. Statically link
+			// libc++demangle into every process so that users still have it if
+			// needed, but the linker won't include this unless it is actually
+			// called.
+			// http://b/138245375
+			deps.StaticLibs = append(deps.StaticLibs, "libc++demangle")
+		}
 		if ctx.toolchain().Bionic() {
 			if ctx.Arch().ArchType == android.Arm {
 				deps.StaticLibs = append(deps.StaticLibs, "libunwind_llvm")
 			}
 			if ctx.staticBinary() {
-				deps.StaticLibs = append(deps.StaticLibs, "libm", "libc", "libdl")
+				deps.StaticLibs = append(deps.StaticLibs, "libm", "libc")
 			}
 		}
 	case "":
@@ -198,8 +207,6 @@
 func (stl *stl) flags(ctx ModuleContext, flags Flags) Flags {
 	switch stl.Properties.SelectedStl {
 	case "libc++", "libc++_static":
-		flags.CFlags = append(flags.CFlags, "-D_USING_LIBCXX")
-
 		if ctx.Darwin() {
 			// libc++'s headers are annotated with availability macros that
 			// indicate which version of Mac OS was the first to ship with a
@@ -208,25 +215,25 @@
 			// these availability attributes are meaningless for us but cause
 			// build breaks when we try to use code that would not be available
 			// in the system's dylib.
-			flags.CppFlags = append(flags.CppFlags,
+			flags.Local.CppFlags = append(flags.Local.CppFlags,
 				"-D_LIBCPP_DISABLE_AVAILABILITY")
 		}
 
 		if !ctx.toolchain().Bionic() {
-			flags.CppFlags = append(flags.CppFlags, "-nostdinc++")
-			flags.LdFlags = append(flags.LdFlags, "-nodefaultlibs")
-			if ctx.staticBinary() {
-				flags.LdFlags = append(flags.LdFlags, hostStaticGccLibs[ctx.Os()]...)
-			} else {
-				flags.LdFlags = append(flags.LdFlags, hostDynamicGccLibs[ctx.Os()]...)
-			}
+			flags.Local.CppFlags = append(flags.Local.CppFlags, "-nostdinc++")
+			flags.extraLibFlags = append(flags.extraLibFlags, "-nostdlib++")
 			if ctx.Windows() {
+				if stl.Properties.SelectedStl == "libc++_static" {
+					// These are transitively needed by libc++_static.
+					flags.extraLibFlags = append(flags.extraLibFlags,
+						"-lmsvcrt", "-lucrt")
+				}
 				// Use SjLj exceptions for 32-bit.  libgcc_eh implements SjLj
 				// exception model for 32-bit.
 				if ctx.Arch().ArchType == android.X86 {
-					flags.CppFlags = append(flags.CppFlags, "-fsjlj-exceptions")
+					flags.Local.CppFlags = append(flags.Local.CppFlags, "-fsjlj-exceptions")
 				}
-				flags.CppFlags = append(flags.CppFlags,
+				flags.Local.CppFlags = append(flags.Local.CppFlags,
 					// Disable visiblity annotations since we're using static
 					// libc++.
 					"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -236,29 +243,24 @@
 			}
 		} else {
 			if ctx.Arch().ArchType == android.Arm {
-				flags.LdFlags = append(flags.LdFlags, "-Wl,--exclude-libs,libunwind_llvm.a")
+				flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--exclude-libs,libunwind_llvm.a")
 			}
 		}
 	case "libstdc++":
 		// Nothing
 	case "ndk_system":
 		ndkSrcRoot := android.PathForSource(ctx, "prebuilts/ndk/current/sources/cxx-stl/system/include")
-		flags.CFlags = append(flags.CFlags, "-isystem "+ndkSrcRoot.String())
+		flags.Local.CFlags = append(flags.Local.CFlags, "-isystem "+ndkSrcRoot.String())
 	case "ndk_libc++_shared", "ndk_libc++_static":
 		if ctx.Arch().ArchType == android.Arm {
 			// Make sure the _Unwind_XXX symbols are not re-exported.
-			flags.LdFlags = append(flags.LdFlags, "-Wl,--exclude-libs,libunwind.a")
+			flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--exclude-libs,libunwind.a")
 		}
 	case "":
 		// None or error.
 		if !ctx.toolchain().Bionic() {
-			flags.CppFlags = append(flags.CppFlags, "-nostdinc++")
-			flags.LdFlags = append(flags.LdFlags, "-nodefaultlibs")
-			if ctx.staticBinary() {
-				flags.LdFlags = append(flags.LdFlags, hostStaticGccLibs[ctx.Os()]...)
-			} else {
-				flags.LdFlags = append(flags.LdFlags, hostDynamicGccLibs[ctx.Os()]...)
-			}
+			flags.Local.CppFlags = append(flags.Local.CppFlags, "-nostdinc++")
+			flags.extraLibFlags = append(flags.extraLibFlags, "-nostdlib++")
 		}
 	default:
 		panic(fmt.Errorf("Unknown stl: %q", stl.Properties.SelectedStl))
@@ -266,22 +268,3 @@
 
 	return flags
 }
-
-var hostDynamicGccLibs, hostStaticGccLibs map[android.OsType][]string
-
-func init() {
-	hostDynamicGccLibs = map[android.OsType][]string{
-		android.Fuchsia: []string{"-lc", "-lunwind"},
-		android.Linux:   []string{"-lgcc_s", "-lgcc", "-lc", "-lgcc_s", "-lgcc"},
-		android.Darwin:  []string{"-lc", "-lSystem"},
-		android.Windows: []string{"-Wl,--start-group", "-lmingw32", "-lgcc", "-lgcc_eh",
-			"-lmoldname", "-lmingwex", "-lmsvcrt", "-lucrt", "-lpthread",
-			"-ladvapi32", "-lshell32", "-luser32", "-lkernel32", "-lpsapi",
-			"-Wl,--end-group"},
-	}
-	hostStaticGccLibs = map[android.OsType][]string{
-		android.Linux:   []string{"-Wl,--start-group", "-lgcc", "-lgcc_eh", "-lc", "-Wl,--end-group"},
-		android.Darwin:  []string{"NO_STATIC_HOST_BINARIES_ON_DARWIN"},
-		android.Windows: []string{"NO_STATIC_HOST_BINARIES_ON_WINDOWS"},
-	}
-}
diff --git a/cc/strip.go b/cc/strip.go
index f3e3374..7e560ec 100644
--- a/cc/strip.go
+++ b/cc/strip.go
@@ -27,7 +27,6 @@
 		Keep_symbols                 *bool    `android:"arch_variant"`
 		Keep_symbols_list            []string `android:"arch_variant"`
 		Keep_symbols_and_debug_frame *bool    `android:"arch_variant"`
-		Use_gnu_strip                *bool    `android:"arch_variant"`
 	} `android:"arch_variant"`
 }
 
@@ -54,9 +53,6 @@
 		} else if !Bool(stripper.StripProperties.Strip.All) {
 			flags.stripKeepMiniDebugInfo = true
 		}
-		if Bool(stripper.StripProperties.Strip.Use_gnu_strip) {
-			flags.stripUseGnuStrip = true
-		}
 		if ctx.Config().Debuggable() && !flags.stripKeepMiniDebugInfo && !isStaticLib {
 			flags.stripAddGnuDebuglink = true
 		}
diff --git a/cc/sysprop.go b/cc/sysprop.go
index 656f79f..6cac7fb 100644
--- a/cc/sysprop.go
+++ b/cc/sysprop.go
@@ -21,6 +21,7 @@
 )
 
 type syspropLibraryInterface interface {
+	BaseModuleName() string
 	CcModuleName() string
 }
 
@@ -42,6 +43,6 @@
 		syspropImplLibrariesLock.Lock()
 		defer syspropImplLibrariesLock.Unlock()
 
-		syspropImplLibraries[mctx.ModuleName()] = m.CcModuleName()
+		syspropImplLibraries[m.BaseModuleName()] = m.CcModuleName()
 	}
 }
diff --git a/cc/test.go b/cc/test.go
index c735fd9..05e6fe5 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -16,6 +16,7 @@
 
 import (
 	"path/filepath"
+	"strconv"
 	"strings"
 
 	"android/soong/android"
@@ -68,6 +69,22 @@
 	// Add RootTargetPreparer to auto generated test config. This guarantees the test to run
 	// with root permission.
 	Require_root *bool
+
+	// Add RunCommandTargetPreparer to stop framework before the test and start it after the test.
+	Disable_framework *bool
+
+	// Add MinApiLevelModuleController to auto generated test config. If the device property of
+	// "ro.product.first_api_level" < Test_min_api_level, then skip this module.
+	Test_min_api_level *int64
+
+	// Add MinApiLevelModuleController to auto generated test config. If the device property of
+	// "ro.build.version.sdk" < Test_min_sdk_version, then skip this module.
+	Test_min_sdk_version *int64
+
+	// 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
 }
 
 func init() {
@@ -121,7 +138,9 @@
 type testPerSrc interface {
 	testPerSrc() bool
 	srcs() []string
+	isAllTestsVariation() bool
 	setSrc(string, string)
+	unsetSrc()
 }
 
 func (test *testBinary) testPerSrc() bool {
@@ -132,45 +151,61 @@
 	return test.baseCompiler.Properties.Srcs
 }
 
+func (test *testBinary) isAllTestsVariation() bool {
+	stem := test.binaryDecorator.Properties.Stem
+	return stem != nil && *stem == ""
+}
+
 func (test *testBinary) setSrc(name, src string) {
 	test.baseCompiler.Properties.Srcs = []string{src}
 	test.binaryDecorator.Properties.Stem = StringPtr(name)
 }
 
+func (test *testBinary) unsetSrc() {
+	test.baseCompiler.Properties.Srcs = nil
+	test.binaryDecorator.Properties.Stem = StringPtr("")
+}
+
 var _ testPerSrc = (*testBinary)(nil)
 
-func testPerSrcMutator(mctx android.BottomUpMutatorContext) {
+func TestPerSrcMutator(mctx android.BottomUpMutatorContext) {
 	if m, ok := mctx.Module().(*Module); ok {
 		if test, ok := m.linker.(testPerSrc); ok {
-			if test.testPerSrc() && len(test.srcs()) > 0 {
-				if duplicate, found := checkDuplicate(test.srcs()); found {
+			numTests := len(test.srcs())
+			if test.testPerSrc() && numTests > 0 {
+				if duplicate, found := android.CheckDuplicate(test.srcs()); found {
 					mctx.PropertyErrorf("srcs", "found a duplicate entry %q", duplicate)
 					return
 				}
-				testNames := make([]string, len(test.srcs()))
+				testNames := make([]string, numTests)
 				for i, src := range test.srcs() {
 					testNames[i] = strings.TrimSuffix(filepath.Base(src), filepath.Ext(src))
 				}
+				// In addition to creating one variation per test source file,
+				// create an additional "all tests" variation named "", and have it
+				// depends on all other test_per_src variations. This is useful to
+				// create subsequent dependencies of a given module on all
+				// test_per_src variations created above: by depending on
+				// variation "", that module will transitively depend on all the
+				// other test_per_src variations without the need to know their
+				// name or even their number.
+				testNames = append(testNames, "")
 				tests := mctx.CreateLocalVariations(testNames...)
+				all_tests := tests[numTests]
+				all_tests.(*Module).linker.(testPerSrc).unsetSrc()
+				// Prevent the "all tests" variation from being installable nor
+				// exporting to Make, as it won't create any output file.
+				all_tests.(*Module).Properties.PreventInstall = true
+				all_tests.(*Module).Properties.HideFromMake = true
 				for i, src := range test.srcs() {
 					tests[i].(*Module).linker.(testPerSrc).setSrc(testNames[i], src)
+					mctx.AddInterVariantDependency(testPerSrcDepTag, all_tests, tests[i])
 				}
 			}
 		}
 	}
 }
 
-func checkDuplicate(values []string) (duplicate string, found bool) {
-	seen := make(map[string]string)
-	for _, v := range values {
-		if duplicate, found = seen[v]; found {
-			return
-		}
-		seen[v] = v
-	}
-	return
-}
-
 type testDecorator struct {
 	Properties TestProperties
 	linker     *baseLinker
@@ -185,20 +220,20 @@
 		return flags
 	}
 
-	flags.CFlags = append(flags.CFlags, "-DGTEST_HAS_STD_STRING")
+	flags.Local.CFlags = append(flags.Local.CFlags, "-DGTEST_HAS_STD_STRING")
 	if ctx.Host() {
-		flags.CFlags = append(flags.CFlags, "-O0", "-g")
+		flags.Local.CFlags = append(flags.Local.CFlags, "-O0", "-g")
 
 		switch ctx.Os() {
 		case android.Windows:
-			flags.CFlags = append(flags.CFlags, "-DGTEST_OS_WINDOWS")
+			flags.Local.CFlags = append(flags.Local.CFlags, "-DGTEST_OS_WINDOWS")
 		case android.Linux:
-			flags.CFlags = append(flags.CFlags, "-DGTEST_OS_LINUX")
+			flags.Local.CFlags = append(flags.Local.CFlags, "-DGTEST_OS_LINUX")
 		case android.Darwin:
-			flags.CFlags = append(flags.CFlags, "-DGTEST_OS_MAC")
+			flags.Local.CFlags = append(flags.Local.CFlags, "-DGTEST_OS_MAC")
 		}
 	} else {
-		flags.CFlags = append(flags.CFlags, "-DGTEST_OS_LINUX_ANDROID")
+		flags.Local.CFlags = append(flags.Local.CFlags, "-DGTEST_OS_LINUX_ANDROID")
 	}
 
 	return flags
@@ -210,6 +245,11 @@
 			deps.StaticLibs = append(deps.StaticLibs, "libgtest_main_ndk_c++", "libgtest_ndk_c++")
 		} else if BoolDefault(test.Properties.Isolated, false) {
 			deps.StaticLibs = append(deps.StaticLibs, "libgtest_isolated_main")
+			// The isolated library requires liblog, but adding it
+			// as a static library means unit tests cannot override
+			// liblog functions. Instead make it a shared library
+			// dependency.
+			deps.SharedLibs = append(deps.SharedLibs, "liblog")
 		} else {
 			deps.StaticLibs = append(deps.StaticLibs, "libgtest_main", "libgtest")
 		}
@@ -277,9 +317,21 @@
 
 func (test *testBinary) install(ctx ModuleContext, file android.Path) {
 	test.data = android.PathsForModuleSrc(ctx, test.Properties.Data)
+	var api_level_prop string
 	var configs []tradefed.Config
+	var min_level string
 	if Bool(test.Properties.Require_root) {
-		configs = append(configs, tradefed.Preparer{"com.android.tradefed.targetprep.RootTargetPreparer"})
+		configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.RootTargetPreparer", nil})
+	} else {
+		var options []tradefed.Option
+		options = append(options, tradefed.Option{"force-root", "false"})
+		configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.RootTargetPreparer", options})
+	}
+	if Bool(test.Properties.Disable_framework) {
+		var options []tradefed.Option
+		options = append(options, tradefed.Option{"run-command", "stop"})
+		options = append(options, tradefed.Option{"teardown-command", "start"})
+		configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.RunCommandTargetPreparer", options})
 	}
 	if Bool(test.testDecorator.Properties.Isolated) {
 		configs = append(configs, tradefed.Option{"not-shardable", "true"})
@@ -287,9 +339,24 @@
 	if test.Properties.Test_options.Run_test_as != nil {
 		configs = append(configs, tradefed.Option{"run-test-as", String(test.Properties.Test_options.Run_test_as)})
 	}
+	if test.Properties.Test_min_api_level != nil && test.Properties.Test_min_sdk_version != nil {
+		ctx.PropertyErrorf("test_min_api_level", "'test_min_api_level' and 'test_min_sdk_version' should not be set at the same time.")
+	} else if test.Properties.Test_min_api_level != nil {
+		api_level_prop = "ro.product.first_api_level"
+		min_level = strconv.FormatInt(int64(*test.Properties.Test_min_api_level), 10)
+	} else if test.Properties.Test_min_sdk_version != nil {
+		api_level_prop = "ro.build.version.sdk"
+		min_level = strconv.FormatInt(int64(*test.Properties.Test_min_sdk_version), 10)
+	}
+	if api_level_prop != "" {
+		var options []tradefed.Option
+		options = append(options, tradefed.Option{"min-api-level", min_level})
+		options = append(options, tradefed.Option{"api-level-prop", api_level_prop})
+		configs = append(configs, tradefed.Object{"module_controller", "com.android.tradefed.testtype.suite.module.MinApiLevelModuleController", options})
+	}
 
 	test.testConfig = tradefed.AutoGenNativeTestConfig(ctx, test.Properties.Test_config,
-		test.Properties.Test_config_template, test.Properties.Test_suites, configs)
+		test.Properties.Test_config_template, test.Properties.Test_suites, configs, test.Properties.Auto_gen_config)
 
 	test.binaryDecorator.baseInstaller.dir = "nativetest"
 	test.binaryDecorator.baseInstaller.dir64 = "nativetest64"
@@ -380,6 +447,11 @@
 	// Add RootTargetPreparer to auto generated test config. This guarantees the test to run
 	// with root permission.
 	Require_root *bool
+
+	// 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 benchmarkDecorator struct {
@@ -414,10 +486,10 @@
 	benchmark.data = android.PathsForModuleSrc(ctx, benchmark.Properties.Data)
 	var configs []tradefed.Config
 	if Bool(benchmark.Properties.Require_root) {
-		configs = append(configs, tradefed.Preparer{"com.android.tradefed.targetprep.RootTargetPreparer"})
+		configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.RootTargetPreparer", nil})
 	}
 	benchmark.testConfig = tradefed.AutoGenNativeBenchmarkTestConfig(ctx, benchmark.Properties.Test_config,
-		benchmark.Properties.Test_config_template, benchmark.Properties.Test_suites, configs)
+		benchmark.Properties.Test_config_template, benchmark.Properties.Test_suites, configs, benchmark.Properties.Auto_gen_config)
 
 	benchmark.binaryDecorator.baseInstaller.dir = filepath.Join("benchmarktest", ctx.ModuleName())
 	benchmark.binaryDecorator.baseInstaller.dir64 = filepath.Join("benchmarktest64", ctx.ModuleName())
diff --git a/cc/test_data_test.go b/cc/test_data_test.go
index 21ea765..ae59e2f 100644
--- a/cc/test_data_test.go
+++ b/cc/test_data_test.go
@@ -115,22 +115,17 @@
 	}
 	defer os.RemoveAll(buildDir)
 
-	config := android.TestConfig(buildDir, nil)
-
 	for _, test := range testDataTests {
 		t.Run(test.name, func(t *testing.T) {
-			ctx := android.NewTestContext()
-			ctx.MockFileSystem(map[string][]byte{
-				"Blueprints":     []byte(`subdirs = ["dir"]`),
-				"dir/Blueprints": []byte(test.modules),
+			config := android.TestConfig(buildDir, nil, "", map[string][]byte{
+				"dir/Android.bp": []byte(test.modules),
 				"dir/baz":        nil,
 				"dir/bar/baz":    nil,
 			})
-			ctx.RegisterModuleType("filegroup",
-				android.ModuleFactoryAdaptor(android.FileGroupFactory))
-			ctx.RegisterModuleType("test",
-				android.ModuleFactoryAdaptor(newTest))
-			ctx.Register()
+			ctx := android.NewTestContext()
+			ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
+			ctx.RegisterModuleType("test", newTest)
+			ctx.Register(config)
 
 			_, errs := ctx.ParseBlueprintsFiles("Blueprints")
 			android.FailIfErrored(t, errs)
diff --git a/cc/test_gen_stub_libs.py b/cc/test_gen_stub_libs.py
index 2ee9886..0b45e71 100755
--- a/cc/test_gen_stub_libs.py
+++ b/cc/test_gen_stub_libs.py
@@ -179,17 +179,17 @@
                 gsl.Version('foo', None, ['platform-only'], []), 'arm', 9,
                 False, False))
 
-    def test_omit_vndk(self):
+    def test_omit_llndk(self):
         self.assertTrue(
             gsl.should_omit_version(
-                gsl.Version('foo', None, ['vndk'], []), 'arm', 9, False, False))
+                gsl.Version('foo', None, ['llndk'], []), 'arm', 9, False, False))
 
         self.assertFalse(
             gsl.should_omit_version(
                 gsl.Version('foo', None, [], []), 'arm', 9, True, False))
         self.assertFalse(
             gsl.should_omit_version(
-                gsl.Version('foo', None, ['vndk'], []), 'arm', 9, True, False))
+                gsl.Version('foo', None, ['llndk'], []), 'arm', 9, True, False))
 
     def test_omit_apex(self):
         self.assertTrue(
@@ -231,16 +231,16 @@
 
 
 class OmitSymbolTest(unittest.TestCase):
-    def test_omit_vndk(self):
+    def test_omit_llndk(self):
         self.assertTrue(
             gsl.should_omit_symbol(
-                gsl.Symbol('foo', ['vndk']), 'arm', 9, False, False))
+                gsl.Symbol('foo', ['llndk']), 'arm', 9, False, False))
 
         self.assertFalse(
             gsl.should_omit_symbol(gsl.Symbol('foo', []), 'arm', 9, True, False))
         self.assertFalse(
             gsl.should_omit_symbol(
-                gsl.Symbol('foo', ['vndk']), 'arm', 9, True, False))
+                gsl.Symbol('foo', ['llndk']), 'arm', 9, True, False))
 
     def test_omit_apex(self):
         self.assertTrue(
@@ -441,12 +441,12 @@
 
         self.assertEqual(expected, versions)
 
-    def test_parse_vndk_apex_symbol(self):
+    def test_parse_llndk_apex_symbol(self):
         input_file = io.StringIO(textwrap.dedent("""\
             VERSION_1 {
                 foo;
-                bar; # vndk
-                baz; # vndk apex
+                bar; # llndk
+                baz; # llndk apex
                 qux; # apex
             };
         """))
@@ -459,8 +459,8 @@
 
         expected_symbols = [
             gsl.Symbol('foo', []),
-            gsl.Symbol('bar', ['vndk']),
-            gsl.Symbol('baz', ['vndk', 'apex']),
+            gsl.Symbol('bar', ['llndk']),
+            gsl.Symbol('baz', ['llndk', 'apex']),
             gsl.Symbol('qux', ['apex']),
         ]
         self.assertEqual(expected_symbols, version.symbols)
@@ -517,7 +517,7 @@
         self.assertEqual('', version_file.getvalue())
 
         version = gsl.Version('VERSION_1', None, [], [
-            gsl.Symbol('foo', ['vndk']),
+            gsl.Symbol('foo', ['llndk']),
         ])
         generator.write_version(version)
         self.assertEqual('', src_file.getvalue())
@@ -607,7 +607,7 @@
 
             VERSION_4 { # versioned=9
                 wibble;
-                wizzes; # vndk
+                wizzes; # llndk
                 waggle; # apex
             } VERSION_2;
 
@@ -749,10 +749,10 @@
 
             VERSION_4 { # versioned=9
                 wibble;
-                wizzes; # vndk
+                wizzes; # llndk
                 waggle; # apex
-                bubble; # apex vndk
-                duddle; # vndk apex
+                bubble; # apex llndk
+                duddle; # llndk apex
             } VERSION_2;
 
             VERSION_5 { # versioned=14
diff --git a/cc/testing.go b/cc/testing.go
index df7cb78..bc31077 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -18,6 +18,18 @@
 	"android/soong/android"
 )
 
+func RegisterRequiredBuildComponentsForTest(ctx android.RegistrationContext) {
+	RegisterPrebuiltBuildComponents(ctx)
+	android.RegisterPrebuiltMutators(ctx)
+
+	RegisterCCBuildComponents(ctx)
+	RegisterLibraryBuildComponents(ctx)
+
+	ctx.RegisterModuleType("toolchain_library", ToolchainLibraryFactory)
+	ctx.RegisterModuleType("llndk_library", LlndkLibraryFactory)
+	ctx.RegisterModuleType("cc_object", ObjectFactory)
+}
+
 func GatherRequiredDepsForTest(os android.OsType) string {
 	ret := `
 		toolchain_library {
@@ -63,6 +75,49 @@
 		}
 
 		toolchain_library {
+			name: "libclang_rt.fuzzer-arm-android",
+			vendor_available: true,
+			recovery_available: true,
+			src: "",
+		}
+
+		toolchain_library {
+			name: "libclang_rt.fuzzer-aarch64-android",
+			vendor_available: true,
+			recovery_available: true,
+			src: "",
+		}
+
+		toolchain_library {
+			name: "libclang_rt.fuzzer-i686-android",
+			vendor_available: true,
+			recovery_available: true,
+			src: "",
+		}
+
+		toolchain_library {
+			name: "libclang_rt.fuzzer-x86_64-android",
+			vendor_available: true,
+			recovery_available: true,
+			src: "",
+		}
+
+		toolchain_library {
+			name: "libclang_rt.fuzzer-x86_64",
+			vendor_available: true,
+			recovery_available: true,
+			src: "",
+		}
+
+		// Needed for sanitizer
+		cc_prebuilt_library_shared {
+			name: "libclang_rt.ubsan_standalone-aarch64-android",
+			vendor_available: true,
+			recovery_available: true,
+			srcs: [""],
+		}
+
+		toolchain_library {
 			name: "libgcc",
 			vendor_available: true,
 			recovery_available: true,
@@ -110,6 +165,18 @@
 			symbol_file: "",
 		}
 		cc_library {
+			name: "libft2",
+			no_libcrt: true,
+			nocrt: true,
+			system_shared_libs: [],
+			recovery_available: true,
+		}
+		llndk_library {
+			name: "libft2",
+			symbol_file: "",
+			vendor_available: false,
+		}
+		cc_library {
 			name: "libc++_static",
 			no_libcrt: true,
 			nocrt: true,
@@ -132,6 +199,16 @@
 			},
 		}
 		cc_library {
+			name: "libc++demangle",
+			no_libcrt: true,
+			nocrt: true,
+			system_shared_libs: [],
+			stl: "none",
+			host_supported: false,
+			vendor_available: true,
+			recovery_available: true,
+		}
+		cc_library {
 			name: "libunwind_llvm",
 			no_libcrt: true,
 			nocrt: true,
@@ -145,6 +222,7 @@
 			name: "crtbegin_so",
 			recovery_available: true,
 			vendor_available: true,
+			stl: "none",
 		}
 
 		cc_object {
@@ -163,6 +241,7 @@
 			name: "crtend_so",
 			recovery_available: true,
 			vendor_available: true,
+			stl: "none",
 		}
 
 		cc_object {
@@ -190,45 +269,21 @@
 	return ret
 }
 
-func CreateTestContext(bp string, fs map[string][]byte,
-	os android.OsType) *android.TestContext {
-
-	ctx := android.NewTestArchContext()
-	ctx.RegisterModuleType("cc_binary", android.ModuleFactoryAdaptor(BinaryFactory))
-	ctx.RegisterModuleType("cc_binary_host", android.ModuleFactoryAdaptor(binaryHostFactory))
-	ctx.RegisterModuleType("cc_library", android.ModuleFactoryAdaptor(LibraryFactory))
-	ctx.RegisterModuleType("cc_library_shared", android.ModuleFactoryAdaptor(LibrarySharedFactory))
-	ctx.RegisterModuleType("cc_library_static", android.ModuleFactoryAdaptor(LibraryStaticFactory))
-	ctx.RegisterModuleType("cc_library_headers", android.ModuleFactoryAdaptor(LibraryHeaderFactory))
-	ctx.RegisterModuleType("cc_test", android.ModuleFactoryAdaptor(TestFactory))
-	ctx.RegisterModuleType("toolchain_library", android.ModuleFactoryAdaptor(ToolchainLibraryFactory))
-	ctx.RegisterModuleType("llndk_library", android.ModuleFactoryAdaptor(LlndkLibraryFactory))
-	ctx.RegisterModuleType("llndk_headers", android.ModuleFactoryAdaptor(llndkHeadersFactory))
-	ctx.RegisterModuleType("vendor_public_library", android.ModuleFactoryAdaptor(vendorPublicLibraryFactory))
-	ctx.RegisterModuleType("cc_object", android.ModuleFactoryAdaptor(ObjectFactory))
-	ctx.RegisterModuleType("filegroup", android.ModuleFactoryAdaptor(android.FileGroupFactory))
-	ctx.RegisterModuleType("vndk_prebuilt_shared", android.ModuleFactoryAdaptor(vndkPrebuiltSharedFactory))
-	ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.BottomUp("image", ImageMutator).Parallel()
-		ctx.BottomUp("link", LinkageMutator).Parallel()
-		ctx.BottomUp("vndk", VndkMutator).Parallel()
-		ctx.BottomUp("version", VersionMutator).Parallel()
-		ctx.BottomUp("begin", BeginMutator).Parallel()
-	})
-	ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.TopDown("double_loadable", checkDoubleLoadableLibraries).Parallel()
-	})
-	ctx.RegisterSingletonType("vndk-snapshot", android.SingletonFactoryAdaptor(VndkSnapshotSingleton))
+func TestConfig(buildDir string, os android.OsType, env map[string]string,
+	bp string, fs map[string][]byte) android.Config {
 
 	// add some modules that are required by the compiler and/or linker
 	bp = bp + GatherRequiredDepsForTest(os)
 
 	mockFS := map[string][]byte{
-		"Android.bp":  []byte(bp),
 		"foo.c":       nil,
+		"foo.lds":     nil,
 		"bar.c":       nil,
+		"baz.c":       nil,
+		"baz.o":       nil,
 		"a.proto":     nil,
 		"b.aidl":      nil,
+		"sub/c.aidl":  nil,
 		"my_include":  nil,
 		"foo.map.txt": nil,
 		"liba.so":     nil,
@@ -238,7 +293,31 @@
 		mockFS[k] = v
 	}
 
-	ctx.MockFileSystem(mockFS)
+	var config android.Config
+	if os == android.Fuchsia {
+		config = android.TestArchConfigFuchsia(buildDir, env, bp, mockFS)
+	} else {
+		config = android.TestArchConfig(buildDir, env, bp, mockFS)
+	}
+
+	return config
+}
+
+func CreateTestContext() *android.TestContext {
+	ctx := android.NewTestArchContext()
+	ctx.RegisterModuleType("cc_binary", BinaryFactory)
+	ctx.RegisterModuleType("cc_binary_host", binaryHostFactory)
+	ctx.RegisterModuleType("cc_fuzz", FuzzFactory)
+	ctx.RegisterModuleType("cc_test", TestFactory)
+	ctx.RegisterModuleType("llndk_headers", llndkHeadersFactory)
+	ctx.RegisterModuleType("ndk_library", NdkLibraryFactory)
+	ctx.RegisterModuleType("vendor_public_library", vendorPublicLibraryFactory)
+	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
+	ctx.RegisterModuleType("vndk_prebuilt_shared", VndkPrebuiltSharedFactory)
+	ctx.RegisterModuleType("vndk_libraries_txt", VndkLibrariesTxtFactory)
+	RegisterRequiredBuildComponentsForTest(ctx)
+	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
+	ctx.RegisterSingletonType("vndk-snapshot", VndkSnapshotSingleton)
 
 	return ctx
 }
diff --git a/cc/toolchain_library.go b/cc/toolchain_library.go
index fef4508..dfc6f76 100644
--- a/cc/toolchain_library.go
+++ b/cc/toolchain_library.go
@@ -29,6 +29,9 @@
 type toolchainLibraryProperties struct {
 	// the prebuilt toolchain library, as a path from the top of the source tree
 	Src *string `android:"arch_variant"`
+
+	// Repack the archive with only the selected objects.
+	Repack_objects_to_keep []string `android:"arch_variant"`
 }
 
 type toolchainLibraryDecorator struct {
@@ -90,6 +93,14 @@
 		return outputFile
 	}
 
+	if library.Properties.Repack_objects_to_keep != nil {
+		fileName := ctx.ModuleName() + staticLibraryExtension
+		outputFile := android.PathForModuleOut(ctx, fileName)
+		TransformArchiveRepack(ctx, srcPath, outputFile, library.Properties.Repack_objects_to_keep)
+
+		return outputFile
+	}
+
 	return srcPath
 }
 
diff --git a/cc/util.go b/cc/util.go
index 2e1bb25..60070bb 100644
--- a/cc/util.go
+++ b/cc/util.go
@@ -55,29 +55,42 @@
 
 func flagsToBuilderFlags(in Flags) builderFlags {
 	return builderFlags{
-		globalFlags:     strings.Join(in.GlobalFlags, " "),
-		arFlags:         strings.Join(in.ArFlags, " "),
-		asFlags:         strings.Join(in.AsFlags, " "),
-		cFlags:          strings.Join(in.CFlags, " "),
-		toolingCFlags:   strings.Join(in.ToolingCFlags, " "),
-		toolingCppFlags: strings.Join(in.ToolingCppFlags, " "),
-		conlyFlags:      strings.Join(in.ConlyFlags, " "),
-		cppFlags:        strings.Join(in.CppFlags, " "),
-		aidlFlags:       strings.Join(in.aidlFlags, " "),
-		rsFlags:         strings.Join(in.rsFlags, " "),
-		ldFlags:         strings.Join(in.LdFlags, " "),
-		libFlags:        strings.Join(in.libFlags, " "),
-		tidyFlags:       strings.Join(in.TidyFlags, " "),
-		sAbiFlags:       strings.Join(in.SAbiFlags, " "),
-		yasmFlags:       strings.Join(in.YasmFlags, " "),
-		toolchain:       in.Toolchain,
-		coverage:        in.Coverage,
-		tidy:            in.Tidy,
-		sAbiDump:        in.SAbiDump,
+		globalCommonFlags:     strings.Join(in.Global.CommonFlags, " "),
+		globalAsFlags:         strings.Join(in.Global.AsFlags, " "),
+		globalYasmFlags:       strings.Join(in.Global.YasmFlags, " "),
+		globalCFlags:          strings.Join(in.Global.CFlags, " "),
+		globalToolingCFlags:   strings.Join(in.Global.ToolingCFlags, " "),
+		globalToolingCppFlags: strings.Join(in.Global.ToolingCppFlags, " "),
+		globalConlyFlags:      strings.Join(in.Global.ConlyFlags, " "),
+		globalCppFlags:        strings.Join(in.Global.CppFlags, " "),
+		globalLdFlags:         strings.Join(in.Global.LdFlags, " "),
+
+		localCommonFlags:     strings.Join(in.Local.CommonFlags, " "),
+		localAsFlags:         strings.Join(in.Local.AsFlags, " "),
+		localYasmFlags:       strings.Join(in.Local.YasmFlags, " "),
+		localCFlags:          strings.Join(in.Local.CFlags, " "),
+		localToolingCFlags:   strings.Join(in.Local.ToolingCFlags, " "),
+		localToolingCppFlags: strings.Join(in.Local.ToolingCppFlags, " "),
+		localConlyFlags:      strings.Join(in.Local.ConlyFlags, " "),
+		localCppFlags:        strings.Join(in.Local.CppFlags, " "),
+		localLdFlags:         strings.Join(in.Local.LdFlags, " "),
+
+		aidlFlags:     strings.Join(in.aidlFlags, " "),
+		rsFlags:       strings.Join(in.rsFlags, " "),
+		libFlags:      strings.Join(in.libFlags, " "),
+		extraLibFlags: strings.Join(in.extraLibFlags, " "),
+		tidyFlags:     strings.Join(in.TidyFlags, " "),
+		sAbiFlags:     strings.Join(in.SAbiFlags, " "),
+		toolchain:     in.Toolchain,
+		coverage:      in.Coverage,
+		tidy:          in.Tidy,
+		sAbiDump:      in.SAbiDump,
+		emitXrefs:     in.EmitXrefs,
 
 		systemIncludeFlags: strings.Join(in.SystemIncludeFlags, " "),
 
-		groupStaticLibs: in.GroupStaticLibs,
+		assemblerWithCpp: in.AssemblerWithCpp,
+		groupStaticLibs:  in.GroupStaticLibs,
 
 		proto:            in.proto,
 		protoC:           in.protoC,
@@ -101,32 +114,6 @@
 	return list
 }
 
-var shlibVersionPattern = regexp.MustCompile("(?:\\.\\d+(?:svn)?)+")
-
-// splitFileExt splits a file name into root, suffix and ext. root stands for the file name without
-// the file extension and the version number (e.g. "libexample"). suffix stands for the
-// concatenation of the file extension and the version number (e.g. ".so.1.0"). ext stands for the
-// file extension after the version numbers are trimmed (e.g. ".so").
-func splitFileExt(name string) (string, string, string) {
-	// Extract and trim the shared lib version number if the file name ends with dot digits.
-	suffix := ""
-	matches := shlibVersionPattern.FindAllStringIndex(name, -1)
-	if len(matches) > 0 {
-		lastMatch := matches[len(matches)-1]
-		if lastMatch[1] == len(name) {
-			suffix = name[lastMatch[0]:lastMatch[1]]
-			name = name[0:lastMatch[0]]
-		}
-	}
-
-	// Extract the file name root and the file extension.
-	ext := filepath.Ext(name)
-	root := strings.TrimSuffix(name, ext)
-	suffix = ext + suffix
-
-	return root, suffix, ext
-}
-
 // linkDirOnDevice/linkName -> target
 func makeSymlinkCmd(linkDirOnDevice string, linkName string, target string) string {
 	dir := filepath.Join("$(PRODUCT_OUT)", linkDirOnDevice)
diff --git a/cc/util_test.go b/cc/util_test.go
deleted file mode 100644
index 7c718ea..0000000
--- a/cc/util_test.go
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright 2018 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 cc
-
-import (
-	"testing"
-)
-
-func TestSplitFileExt(t *testing.T) {
-	t.Run("soname with version", func(t *testing.T) {
-		root, suffix, ext := splitFileExt("libtest.so.1.0.30")
-		expected := "libtest"
-		if root != expected {
-			t.Errorf("root should be %q but got %q", expected, root)
-		}
-		expected = ".so.1.0.30"
-		if suffix != expected {
-			t.Errorf("suffix should be %q but got %q", expected, suffix)
-		}
-		expected = ".so"
-		if ext != expected {
-			t.Errorf("ext should be %q but got %q", expected, ext)
-		}
-	})
-
-	t.Run("soname with svn version", func(t *testing.T) {
-		root, suffix, ext := splitFileExt("libtest.so.1svn")
-		expected := "libtest"
-		if root != expected {
-			t.Errorf("root should be %q but got %q", expected, root)
-		}
-		expected = ".so.1svn"
-		if suffix != expected {
-			t.Errorf("suffix should be %q but got %q", expected, suffix)
-		}
-		expected = ".so"
-		if ext != expected {
-			t.Errorf("ext should be %q but got %q", expected, ext)
-		}
-	})
-
-	t.Run("version numbers in the middle should be ignored", func(t *testing.T) {
-		root, suffix, ext := splitFileExt("libtest.1.0.30.so")
-		expected := "libtest.1.0.30"
-		if root != expected {
-			t.Errorf("root should be %q but got %q", expected, root)
-		}
-		expected = ".so"
-		if suffix != expected {
-			t.Errorf("suffix should be %q but got %q", expected, suffix)
-		}
-		expected = ".so"
-		if ext != expected {
-			t.Errorf("ext should be %q but got %q", expected, ext)
-		}
-	})
-
-	t.Run("no known file extension", func(t *testing.T) {
-		root, suffix, ext := splitFileExt("test.exe")
-		expected := "test"
-		if root != expected {
-			t.Errorf("root should be %q but got %q", expected, root)
-		}
-		expected = ".exe"
-		if suffix != expected {
-			t.Errorf("suffix should be %q but got %q", expected, suffix)
-		}
-		if ext != expected {
-			t.Errorf("ext should be %q but got %q", expected, ext)
-		}
-	})
-}
diff --git a/cc/vendor_public_library.go b/cc/vendor_public_library.go
index f0de267..e9d1c73 100644
--- a/cc/vendor_public_library.go
+++ b/cc/vendor_public_library.go
@@ -124,7 +124,7 @@
 	objs Objects) android.Path {
 	if !Bool(stub.Properties.Unversioned) {
 		linkerScriptFlag := "-Wl,--version-script," + stub.versionScriptPath.String()
-		flags.LdFlags = append(flags.LdFlags, linkerScriptFlag)
+		flags.Local.LdFlags = append(flags.Local.LdFlags, linkerScriptFlag)
 		flags.LdFlagsDeps = append(flags.LdFlagsDeps, stub.versionScriptPath)
 	}
 	return stub.libraryDecorator.link(ctx, flags, deps, objs)
diff --git a/cc/vndk.go b/cc/vndk.go
index f9f3764..872a473 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -15,6 +15,7 @@
 package cc
 
 import (
+	"encoding/json"
 	"errors"
 	"fmt"
 	"path/filepath"
@@ -26,12 +27,39 @@
 	"android/soong/cc/config"
 )
 
+const (
+	llndkLibrariesTxt                = "llndk.libraries.txt"
+	vndkCoreLibrariesTxt             = "vndkcore.libraries.txt"
+	vndkSpLibrariesTxt               = "vndksp.libraries.txt"
+	vndkPrivateLibrariesTxt          = "vndkprivate.libraries.txt"
+	vndkUsingCoreVariantLibrariesTxt = "vndkcorevariant.libraries.txt"
+)
+
+func VndkLibrariesTxtModules(vndkVersion string) []string {
+	if vndkVersion == "current" {
+		return []string{
+			llndkLibrariesTxt,
+			vndkCoreLibrariesTxt,
+			vndkSpLibrariesTxt,
+			vndkPrivateLibrariesTxt,
+		}
+	}
+	// Snapshot vndks have their own *.libraries.VER.txt files.
+	// Note that snapshots don't have "vndkcorevariant.libraries.VER.txt"
+	return []string{
+		insertVndkVersion(llndkLibrariesTxt, vndkVersion),
+		insertVndkVersion(vndkCoreLibrariesTxt, vndkVersion),
+		insertVndkVersion(vndkSpLibrariesTxt, vndkVersion),
+		insertVndkVersion(vndkPrivateLibrariesTxt, vndkVersion),
+	}
+}
+
 type VndkProperties struct {
 	Vndk struct {
 		// declared as a VNDK or VNDK-SP module. The vendor variant
 		// will be installed in /system instead of /vendor partition.
 		//
-		// `vendor_vailable` must be explicitly set to either true or
+		// `vendor_available` must be explicitly set to either true or
 		// false together with `vndk: {enabled: true}`.
 		Enabled *bool
 
@@ -49,10 +77,6 @@
 
 		// Extending another module
 		Extends *string
-
-		// for vndk_prebuilt_shared, this is set by "version" property.
-		// Otherwise, this is set as PLATFORM_VNDK_VERSION.
-		Version string `blueprint:"mutated"`
 	}
 }
 
@@ -102,21 +126,21 @@
 	return "native:vendor:vndkspext"
 }
 
-func (vndk *vndkdep) vndkCheckLinkType(ctx android.ModuleContext, to *Module, tag dependencyTag) {
+func (vndk *vndkdep) vndkCheckLinkType(ctx android.ModuleContext, to *Module, tag DependencyTag) {
 	if to.linker == nil {
 		return
 	}
 	if !vndk.isVndk() {
-		// Non-VNDK modules (those installed to /vendor) can't depend on modules marked with
-		// vendor_available: false.
+		// Non-VNDK modules (those installed to /vendor, /product, or /system/product) can't depend
+		// on modules marked with vendor_available: false.
 		violation := false
 		if lib, ok := to.linker.(*llndkStubDecorator); ok && !Bool(lib.Properties.Vendor_available) {
 			violation = true
 		} else {
 			if _, ok := to.linker.(libraryInterface); ok && to.VendorProperties.Vendor_available != nil && !Bool(to.VendorProperties.Vendor_available) {
 				// Vendor_available == nil && !Bool(Vendor_available) should be okay since
-				// it means a vendor-only library which is a valid dependency for non-VNDK
-				// modules.
+				// it means a vendor-only, or product-only library which is a valid dependency
+				// for non-VNDK modules.
 				violation = true
 			}
 		}
@@ -129,7 +153,7 @@
 		// Other (static and LL-NDK) libraries are allowed to link.
 		return
 	}
-	if !to.Properties.UseVndk {
+	if !to.UseVndk() {
 		ctx.ModuleErrorf("(%s) should not link to %q which is not a vendor-available library",
 			vndk.typeName(), to.Name())
 		return
@@ -202,139 +226,148 @@
 	vndkSpLibrariesKey               = android.NewOnceKey("vndkSpLibrarires")
 	llndkLibrariesKey                = android.NewOnceKey("llndkLibrarires")
 	vndkPrivateLibrariesKey          = android.NewOnceKey("vndkPrivateLibrarires")
-	vndkUsingCoreVariantLibrariesKey = android.NewOnceKey("vndkUsingCoreVariantLibrarires")
-	modulePathsKey                   = android.NewOnceKey("modulePaths")
-	vndkSnapshotOutputsKey           = android.NewOnceKey("vndkSnapshotOutputs")
+	vndkUsingCoreVariantLibrariesKey = android.NewOnceKey("vndkUsingCoreVariantLibraries")
+	vndkMustUseVendorVariantListKey  = android.NewOnceKey("vndkMustUseVendorVariantListKey")
 	vndkLibrariesLock                sync.Mutex
+
+	headerExts = []string{".h", ".hh", ".hpp", ".hxx", ".h++", ".inl", ".inc", ".ipp", ".h.generic"}
 )
 
-type vndkSnapshotOutputPaths struct {
-	configs         android.Paths
-	notices         android.Paths
-	vndkCoreLibs    android.Paths
-	vndkCoreLibs2nd android.Paths
-	vndkSpLibs      android.Paths
-	vndkSpLibs2nd   android.Paths
-}
-
-func vndkCoreLibraries(config android.Config) *[]string {
+func vndkCoreLibraries(config android.Config) map[string]string {
 	return config.Once(vndkCoreLibrariesKey, func() interface{} {
-		return &[]string{}
-	}).(*[]string)
-}
-
-func vndkSpLibraries(config android.Config) *[]string {
-	return config.Once(vndkSpLibrariesKey, func() interface{} {
-		return &[]string{}
-	}).(*[]string)
-}
-
-func llndkLibraries(config android.Config) *[]string {
-	return config.Once(llndkLibrariesKey, func() interface{} {
-		return &[]string{}
-	}).(*[]string)
-}
-
-func vndkPrivateLibraries(config android.Config) *[]string {
-	return config.Once(vndkPrivateLibrariesKey, func() interface{} {
-		return &[]string{}
-	}).(*[]string)
-}
-
-func vndkUsingCoreVariantLibraries(config android.Config) *[]string {
-	return config.Once(vndkUsingCoreVariantLibrariesKey, func() interface{} {
-		return &[]string{}
-	}).(*[]string)
-}
-
-func modulePaths(config android.Config) map[string]string {
-	return config.Once(modulePathsKey, func() interface{} {
 		return make(map[string]string)
 	}).(map[string]string)
 }
 
-func vndkSnapshotOutputs(config android.Config) *vndkSnapshotOutputPaths {
-	return config.Once(vndkSnapshotOutputsKey, func() interface{} {
-		return &vndkSnapshotOutputPaths{}
-	}).(*vndkSnapshotOutputPaths)
+func vndkSpLibraries(config android.Config) map[string]string {
+	return config.Once(vndkSpLibrariesKey, func() interface{} {
+		return make(map[string]string)
+	}).(map[string]string)
+}
+
+func isLlndkLibrary(baseModuleName string, config android.Config) bool {
+	_, ok := llndkLibraries(config)[baseModuleName]
+	return ok
+}
+
+func llndkLibraries(config android.Config) map[string]string {
+	return config.Once(llndkLibrariesKey, func() interface{} {
+		return make(map[string]string)
+	}).(map[string]string)
+}
+
+func isVndkPrivateLibrary(baseModuleName string, config android.Config) bool {
+	_, ok := vndkPrivateLibraries(config)[baseModuleName]
+	return ok
+}
+
+func vndkPrivateLibraries(config android.Config) map[string]string {
+	return config.Once(vndkPrivateLibrariesKey, func() interface{} {
+		return make(map[string]string)
+	}).(map[string]string)
+}
+
+func vndkUsingCoreVariantLibraries(config android.Config) map[string]string {
+	return config.Once(vndkUsingCoreVariantLibrariesKey, func() interface{} {
+		return make(map[string]string)
+	}).(map[string]string)
+}
+
+func vndkMustUseVendorVariantList(cfg android.Config) []string {
+	return cfg.Once(vndkMustUseVendorVariantListKey, func() interface{} {
+		return config.VndkMustUseVendorVariantList
+	}).([]string)
+}
+
+// test may call this to override global configuration(config.VndkMustUseVendorVariantList)
+// when it is called, it must be before the first call to vndkMustUseVendorVariantList()
+func setVndkMustUseVendorVariantListForTest(config android.Config, mustUseVendorVariantList []string) {
+	config.Once(vndkMustUseVendorVariantListKey, func() interface{} {
+		return mustUseVendorVariantList
+	})
 }
 
 func processLlndkLibrary(mctx android.BottomUpMutatorContext, m *Module) {
 	lib := m.linker.(*llndkStubDecorator)
-	name := strings.TrimSuffix(m.Name(), llndkLibrarySuffix)
+	name := m.BaseModuleName()
+	filename := m.BaseModuleName() + ".so"
 
 	vndkLibrariesLock.Lock()
 	defer vndkLibrariesLock.Unlock()
 
-	llndkLibraries := llndkLibraries(mctx.Config())
-	if !inList(name, *llndkLibraries) {
-		*llndkLibraries = append(*llndkLibraries, name)
-		sort.Strings(*llndkLibraries)
-	}
+	llndkLibraries(mctx.Config())[name] = filename
 	if !Bool(lib.Properties.Vendor_available) {
-		vndkPrivateLibraries := vndkPrivateLibraries(mctx.Config())
-		if !inList(name, *vndkPrivateLibraries) {
-			*vndkPrivateLibraries = append(*vndkPrivateLibraries, name)
-			sort.Strings(*vndkPrivateLibraries)
-		}
+		vndkPrivateLibraries(mctx.Config())[name] = filename
 	}
 }
 
 func processVndkLibrary(mctx android.BottomUpMutatorContext, m *Module) {
-	name := strings.TrimPrefix(m.Name(), "prebuilt_")
+	name := m.BaseModuleName()
+	filename, err := getVndkFileName(m)
+	if err != nil {
+		panic(err)
+	}
 
 	vndkLibrariesLock.Lock()
 	defer vndkLibrariesLock.Unlock()
 
-	modulePaths := modulePaths(mctx.Config())
-	if mctx.DeviceConfig().VndkUseCoreVariant() && !inList(name, config.VndkMustUseVendorVariantList) {
-		vndkUsingCoreVariantLibraries := vndkUsingCoreVariantLibraries(mctx.Config())
-		if !inList(name, *vndkUsingCoreVariantLibraries) {
-			*vndkUsingCoreVariantLibraries = append(*vndkUsingCoreVariantLibraries, name)
-			sort.Strings(*vndkUsingCoreVariantLibraries)
-		}
+	if inList(name, vndkMustUseVendorVariantList(mctx.Config())) {
+		m.Properties.MustUseVendorVariant = true
 	}
+	if mctx.DeviceConfig().VndkUseCoreVariant() && !m.Properties.MustUseVendorVariant {
+		vndkUsingCoreVariantLibraries(mctx.Config())[name] = filename
+	}
+
 	if m.vndkdep.isVndkSp() {
-		vndkSpLibraries := vndkSpLibraries(mctx.Config())
-		if !inList(name, *vndkSpLibraries) {
-			*vndkSpLibraries = append(*vndkSpLibraries, name)
-			sort.Strings(*vndkSpLibraries)
-			modulePaths[name] = mctx.ModuleDir()
-		}
+		vndkSpLibraries(mctx.Config())[name] = filename
 	} else {
-		vndkCoreLibraries := vndkCoreLibraries(mctx.Config())
-		if !inList(name, *vndkCoreLibraries) {
-			*vndkCoreLibraries = append(*vndkCoreLibraries, name)
-			sort.Strings(*vndkCoreLibraries)
-			modulePaths[name] = mctx.ModuleDir()
-		}
+		vndkCoreLibraries(mctx.Config())[name] = filename
 	}
 	if !Bool(m.VendorProperties.Vendor_available) {
-		vndkPrivateLibraries := vndkPrivateLibraries(mctx.Config())
-		if !inList(name, *vndkPrivateLibraries) {
-			*vndkPrivateLibraries = append(*vndkPrivateLibraries, name)
-			sort.Strings(*vndkPrivateLibraries)
-		}
+		vndkPrivateLibraries(mctx.Config())[name] = filename
 	}
 }
 
+func IsForVndkApex(mctx android.BottomUpMutatorContext, m *Module) bool {
+	if !m.Enabled() {
+		return false
+	}
+
+	if !mctx.Device() {
+		return false
+	}
+
+	if m.Target().NativeBridge == android.NativeBridgeEnabled {
+		return false
+	}
+
+	// prebuilt vndk modules should match with device
+	// TODO(b/142675459): Use enabled: to select target device in vndk_prebuilt_shared
+	// When b/142675459 is landed, remove following check
+	if p, ok := m.linker.(*vndkPrebuiltLibraryDecorator); ok && !p.matchesWithDevice(mctx.DeviceConfig()) {
+		return false
+	}
+
+	if lib, ok := m.linker.(libraryInterface); ok {
+		useCoreVariant := m.VndkVersion() == mctx.DeviceConfig().PlatformVndkVersion() &&
+			mctx.DeviceConfig().VndkUseCoreVariant() && !m.MustUseVendorVariant()
+		return lib.shared() && m.UseVndk() && m.IsVndk() && !m.isVndkExt() && !useCoreVariant
+	}
+	return false
+}
+
 // gather list of vndk-core, vndk-sp, and ll-ndk libs
 func VndkMutator(mctx android.BottomUpMutatorContext) {
 	m, ok := mctx.Module().(*Module)
 	if !ok {
 		return
 	}
-
 	if !m.Enabled() {
 		return
 	}
-
-	if m.isVndk() {
-		if lib, ok := m.linker.(*vndkPrebuiltLibraryDecorator); ok {
-			m.vndkdep.Properties.Vndk.Version = lib.version()
-		} else {
-			m.vndkdep.Properties.Vndk.Version = mctx.DeviceConfig().PlatformVndkVersion()
-		}
+	if m.Target().NativeBridge == android.NativeBridgeEnabled {
+		// Skip native_bridge modules
+		return
 	}
 
 	if _, ok := m.linker.(*llndkStubDecorator); ok {
@@ -345,8 +378,8 @@
 	lib, is_lib := m.linker.(*libraryDecorator)
 	prebuilt_lib, is_prebuilt_lib := m.linker.(*prebuiltLibraryLinker)
 
-	if (is_lib && lib.shared()) || (is_prebuilt_lib && prebuilt_lib.shared()) {
-		if m.vndkdep.isVndk() && !m.vndkdep.isVndkExt() {
+	if (is_lib && lib.buildShared()) || (is_prebuilt_lib && prebuilt_lib.buildShared()) {
+		if m.vndkdep != nil && m.vndkdep.isVndk() && !m.vndkdep.isVndkExt() {
 			processVndkLibrary(mctx, m)
 			return
 		}
@@ -354,46 +387,121 @@
 }
 
 func init() {
+	android.RegisterModuleType("vndk_libraries_txt", VndkLibrariesTxtFactory)
 	android.RegisterSingletonType("vndk-snapshot", VndkSnapshotSingleton)
-	android.RegisterMakeVarsProvider(pctx, func(ctx android.MakeVarsContext) {
-		outputs := vndkSnapshotOutputs(ctx.Config())
+}
 
-		ctx.Strict("SOONG_VNDK_SNAPSHOT_CONFIGS", strings.Join(outputs.configs.Strings(), " "))
-		ctx.Strict("SOONG_VNDK_SNAPSHOT_NOTICES", strings.Join(outputs.notices.Strings(), " "))
-		ctx.Strict("SOONG_VNDK_SNAPSHOT_CORE_LIBS", strings.Join(outputs.vndkCoreLibs.Strings(), " "))
-		ctx.Strict("SOONG_VNDK_SNAPSHOT_SP_LIBS", strings.Join(outputs.vndkSpLibs.Strings(), " "))
-		ctx.Strict("SOONG_VNDK_SNAPSHOT_CORE_LIBS_2ND", strings.Join(outputs.vndkCoreLibs2nd.Strings(), " "))
-		ctx.Strict("SOONG_VNDK_SNAPSHOT_SP_LIBS_2ND", strings.Join(outputs.vndkSpLibs2nd.Strings(), " "))
+type vndkLibrariesTxt struct {
+	android.ModuleBase
+	outputFile android.OutputPath
+}
+
+var _ android.PrebuiltEtcModule = &vndkLibrariesTxt{}
+var _ android.OutputFileProducer = &vndkLibrariesTxt{}
+
+// vndk_libraries_txt is a special kind of module type in that it name is one of
+// - llndk.libraries.txt
+// - vndkcore.libraries.txt
+// - vndksp.libraries.txt
+// - vndkprivate.libraries.txt
+// - vndkcorevariant.libraries.txt
+// A module behaves like a prebuilt_etc but its content is generated by soong.
+// By being a soong module, these files can be referenced by other soong modules.
+// For example, apex_vndk can depend on these files as prebuilt.
+func VndkLibrariesTxtFactory() android.Module {
+	m := &vndkLibrariesTxt{}
+	android.InitAndroidModule(m)
+	return m
+}
+
+func insertVndkVersion(filename string, vndkVersion string) string {
+	if index := strings.LastIndex(filename, "."); index != -1 {
+		return filename[:index] + "." + vndkVersion + filename[index:]
+	}
+	return filename
+}
+
+func (txt *vndkLibrariesTxt) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	var list []string
+	switch txt.Name() {
+	case llndkLibrariesTxt:
+		for _, filename := range android.SortedStringMapValues(llndkLibraries(ctx.Config())) {
+			if strings.HasPrefix(filename, "libclang_rt.hwasan-") {
+				continue
+			}
+			list = append(list, filename)
+		}
+	case vndkCoreLibrariesTxt:
+		list = android.SortedStringMapValues(vndkCoreLibraries(ctx.Config()))
+	case vndkSpLibrariesTxt:
+		list = android.SortedStringMapValues(vndkSpLibraries(ctx.Config()))
+	case vndkPrivateLibrariesTxt:
+		list = android.SortedStringMapValues(vndkPrivateLibraries(ctx.Config()))
+	case vndkUsingCoreVariantLibrariesTxt:
+		list = android.SortedStringMapValues(vndkUsingCoreVariantLibraries(ctx.Config()))
+	default:
+		ctx.ModuleErrorf("name(%s) is unknown.", txt.Name())
+		return
+	}
+
+	var filename string
+	if txt.Name() != vndkUsingCoreVariantLibrariesTxt {
+		filename = insertVndkVersion(txt.Name(), ctx.DeviceConfig().PlatformVndkVersion())
+	} else {
+		filename = txt.Name()
+	}
+
+	txt.outputFile = android.PathForModuleOut(ctx, filename).OutputPath
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        android.WriteFile,
+		Output:      txt.outputFile,
+		Description: "Writing " + txt.outputFile.String(),
+		Args: map[string]string{
+			"content": strings.Join(list, "\\n"),
+		},
 	})
+
+	installPath := android.PathForModuleInstall(ctx, "etc")
+	ctx.InstallFile(installPath, filename, txt.outputFile)
+}
+
+func (txt *vndkLibrariesTxt) AndroidMkEntries() []android.AndroidMkEntries {
+	return []android.AndroidMkEntries{android.AndroidMkEntries{
+		Class:      "ETC",
+		OutputFile: android.OptionalPathForPath(txt.outputFile),
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(entries *android.AndroidMkEntries) {
+				entries.SetString("LOCAL_MODULE_STEM", txt.outputFile.Base())
+			},
+		},
+	}}
+}
+
+func (txt *vndkLibrariesTxt) OutputFile() android.OutputPath {
+	return txt.outputFile
+}
+
+func (txt *vndkLibrariesTxt) OutputFiles(tag string) (android.Paths, error) {
+	return android.Paths{txt.outputFile}, nil
+}
+
+func (txt *vndkLibrariesTxt) SubDir() string {
+	return ""
 }
 
 func VndkSnapshotSingleton() android.Singleton {
 	return &vndkSnapshotSingleton{}
 }
 
-type vndkSnapshotSingleton struct{}
-
-func installVndkSnapshotLib(ctx android.SingletonContext, name string, module *Module, dir string) android.Path {
-	if !module.outputFile.Valid() {
-		panic(fmt.Errorf("module %s has no outputFile\n", name))
-	}
-
-	out := android.PathForOutput(ctx, dir, name+".so")
-
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        android.Cp,
-		Input:       module.outputFile.Path(),
-		Output:      out,
-		Description: "vndk snapshot " + dir + "/" + name + ".so",
-		Args: map[string]string{
-			"cpFlags": "-f -L",
-		},
-	})
-
-	return out
+type vndkSnapshotSingleton struct {
+	vndkLibrariesFile   android.OutputPath
+	vndkSnapshotZipFile android.OptionalPath
 }
 
 func (c *vndkSnapshotSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+	// build these files even if PlatformVndkVersion or BoardVndkVersion is not set
+	c.buildVndkLibrariesTxtFiles(ctx)
+
 	// BOARD_VNDK_VERSION must be set to 'current' in order to generate a VNDK snapshot.
 	if ctx.DeviceConfig().VndkVersion() != "current" {
 		return
@@ -407,155 +515,413 @@
 		return
 	}
 
-	outputs := vndkSnapshotOutputs(ctx.Config())
+	var snapshotOutputs android.Paths
+
+	/*
+		VNDK snapshot zipped artifacts directory structure:
+		{SNAPSHOT_ARCH}/
+			arch-{TARGET_ARCH}-{TARGET_ARCH_VARIANT}/
+				shared/
+					vndk-core/
+						(VNDK-core libraries, e.g. libbinder.so)
+					vndk-sp/
+						(VNDK-SP libraries, e.g. libc++.so)
+			arch-{TARGET_2ND_ARCH}-{TARGET_2ND_ARCH_VARIANT}/
+				shared/
+					vndk-core/
+						(VNDK-core libraries, e.g. libbinder.so)
+					vndk-sp/
+						(VNDK-SP libraries, e.g. libc++.so)
+			binder32/
+				(This directory is newly introduced in v28 (Android P) to hold
+				prebuilts built for 32-bit binder interface.)
+				arch-{TARGET_ARCH}-{TARGE_ARCH_VARIANT}/
+					...
+			configs/
+				(various *.txt configuration files)
+			include/
+				(header files of same directory structure with source tree)
+			NOTICE_FILES/
+				(notice files of libraries, e.g. libcutils.so.txt)
+	*/
 
 	snapshotDir := "vndk-snapshot"
+	snapshotArchDir := filepath.Join(snapshotDir, ctx.DeviceConfig().DeviceArch())
 
-	var vndkLibPath, vndkLib2ndPath string
-
-	snapshotVariantPath := filepath.Join(snapshotDir, ctx.DeviceConfig().DeviceArch())
-	if ctx.DeviceConfig().BinderBitness() == "32" {
-		vndkLibPath = filepath.Join(snapshotVariantPath, "binder32", fmt.Sprintf(
-			"arch-%s-%s", ctx.DeviceConfig().DeviceArch(), ctx.DeviceConfig().DeviceArchVariant()))
-		vndkLib2ndPath = filepath.Join(snapshotVariantPath, "binder32", fmt.Sprintf(
-			"arch-%s-%s", ctx.DeviceConfig().DeviceSecondaryArch(), ctx.DeviceConfig().DeviceSecondaryArchVariant()))
-	} else {
-		vndkLibPath = filepath.Join(snapshotVariantPath, fmt.Sprintf(
-			"arch-%s-%s", ctx.DeviceConfig().DeviceArch(), ctx.DeviceConfig().DeviceArchVariant()))
-		vndkLib2ndPath = filepath.Join(snapshotVariantPath, fmt.Sprintf(
-			"arch-%s-%s", ctx.DeviceConfig().DeviceSecondaryArch(), ctx.DeviceConfig().DeviceSecondaryArchVariant()))
+	targetArchDirMap := make(map[android.ArchType]string)
+	for _, target := range ctx.Config().Targets[android.Android] {
+		dir := snapshotArchDir
+		if ctx.DeviceConfig().BinderBitness() == "32" {
+			dir = filepath.Join(dir, "binder32")
+		}
+		arch := "arch-" + target.Arch.ArchType.String()
+		if target.Arch.ArchVariant != "" {
+			arch += "-" + target.Arch.ArchVariant
+		}
+		dir = filepath.Join(dir, arch)
+		targetArchDirMap[target.Arch.ArchType] = dir
 	}
+	configsDir := filepath.Join(snapshotArchDir, "configs")
+	noticeDir := filepath.Join(snapshotArchDir, "NOTICE_FILES")
+	includeDir := filepath.Join(snapshotArchDir, "include")
 
-	vndkCoreLibPath := filepath.Join(vndkLibPath, "shared", "vndk-core")
-	vndkSpLibPath := filepath.Join(vndkLibPath, "shared", "vndk-sp")
-	vndkCoreLib2ndPath := filepath.Join(vndkLib2ndPath, "shared", "vndk-core")
-	vndkSpLib2ndPath := filepath.Join(vndkLib2ndPath, "shared", "vndk-sp")
-	noticePath := filepath.Join(snapshotVariantPath, "NOTICE_FILES")
+	// set of include paths exported by VNDK libraries
+	exportedIncludes := make(map[string]bool)
+
+	// generated header files among exported headers.
+	var generatedHeaders android.Paths
+
+	// set of notice files copied.
 	noticeBuilt := make(map[string]bool)
 
-	tryBuildNotice := func(m *Module) {
-		name := ctx.ModuleName(m)
+	// paths of VNDK modules for GPL license checking
+	modulePaths := make(map[string]string)
 
-		if _, ok := noticeBuilt[name]; ok {
-			return
-		}
+	// actual module names of .so files
+	// e.g. moduleNames["libprotobuf-cpp-full-3.9.1.so"] = "libprotobuf-cpp-full"
+	moduleNames := make(map[string]string)
 
-		noticeBuilt[name] = true
-
-		if m.NoticeFile().Valid() {
-			out := android.PathForOutput(ctx, noticePath, name+".so.txt")
-			ctx.Build(pctx, android.BuildParams{
-				Rule:        android.Cp,
-				Input:       m.NoticeFile().Path(),
-				Output:      out,
-				Description: "vndk snapshot notice " + name + ".so.txt",
-				Args: map[string]string{
-					"cpFlags": "-f -L",
-				},
-			})
-			outputs.notices = append(outputs.notices, out)
-		}
+	installSnapshotFileFromPath := func(path android.Path, out string) android.OutputPath {
+		outPath := android.PathForOutput(ctx, out)
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        android.Cp,
+			Input:       path,
+			Output:      outPath,
+			Description: "vndk snapshot " + out,
+			Args: map[string]string{
+				"cpFlags": "-f -L",
+			},
+		})
+		return outPath
 	}
 
-	vndkCoreLibraries := vndkCoreLibraries(ctx.Config())
-	vndkSpLibraries := vndkSpLibraries(ctx.Config())
-	vndkPrivateLibraries := vndkPrivateLibraries(ctx.Config())
+	installSnapshotFileFromContent := func(content, out string) android.OutputPath {
+		outPath := android.PathForOutput(ctx, out)
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        android.WriteFile,
+			Output:      outPath,
+			Description: "vndk snapshot " + out,
+			Args: map[string]string{
+				"content": content,
+			},
+		})
+		return outPath
+	}
+
+	type vndkSnapshotLibraryInterface interface {
+		exportedFlagsProducer
+		libraryInterface
+	}
+
+	var _ vndkSnapshotLibraryInterface = (*prebuiltLibraryLinker)(nil)
+	var _ vndkSnapshotLibraryInterface = (*libraryDecorator)(nil)
+
+	installVndkSnapshotLib := func(m *Module, l vndkSnapshotLibraryInterface, vndkType string) (android.Paths, bool) {
+		targetArchDir, ok := targetArchDirMap[m.Target().Arch.ArchType]
+		if !ok {
+			return nil, false
+		}
+
+		var ret android.Paths
+
+		libPath := m.outputFile.Path()
+		stem := libPath.Base()
+		snapshotLibOut := filepath.Join(targetArchDir, "shared", vndkType, stem)
+		ret = append(ret, installSnapshotFileFromPath(libPath, snapshotLibOut))
+
+		moduleNames[stem] = ctx.ModuleName(m)
+		modulePaths[stem] = ctx.ModuleDir(m)
+
+		if m.NoticeFile().Valid() {
+			noticeName := stem + ".txt"
+			// skip already copied notice file
+			if _, ok := noticeBuilt[noticeName]; !ok {
+				noticeBuilt[noticeName] = true
+				ret = append(ret, installSnapshotFileFromPath(
+					m.NoticeFile().Path(), filepath.Join(noticeDir, noticeName)))
+			}
+		}
+
+		if ctx.Config().VndkSnapshotBuildArtifacts() {
+			prop := struct {
+				ExportedDirs        []string `json:",omitempty"`
+				ExportedSystemDirs  []string `json:",omitempty"`
+				ExportedFlags       []string `json:",omitempty"`
+				RelativeInstallPath string   `json:",omitempty"`
+			}{}
+			prop.ExportedFlags = l.exportedFlags()
+			prop.ExportedDirs = l.exportedDirs().Strings()
+			prop.ExportedSystemDirs = l.exportedSystemDirs().Strings()
+			prop.RelativeInstallPath = m.RelativeInstallPath()
+
+			propOut := snapshotLibOut + ".json"
+
+			j, err := json.Marshal(prop)
+			if err != nil {
+				ctx.Errorf("json marshal to %q failed: %#v", propOut, err)
+				return nil, false
+			}
+			ret = append(ret, installSnapshotFileFromContent(string(j), propOut))
+		}
+		return ret, true
+	}
+
+	isVndkSnapshotLibrary := func(m *Module) (i vndkSnapshotLibraryInterface, vndkType string, isVndkSnapshotLib bool) {
+		if m.Target().NativeBridge == android.NativeBridgeEnabled {
+			return nil, "", false
+		}
+		if !m.UseVndk() || !m.IsForPlatform() || !m.installable() || !m.inVendor() {
+			return nil, "", false
+		}
+		l, ok := m.linker.(vndkSnapshotLibraryInterface)
+		if !ok || !l.shared() {
+			return nil, "", false
+		}
+		if m.VndkVersion() == ctx.DeviceConfig().PlatformVndkVersion() && m.IsVndk() && !m.isVndkExt() {
+			if m.isVndkSp() {
+				return l, "vndk-sp", true
+			} else {
+				return l, "vndk-core", true
+			}
+		}
+
+		return nil, "", false
+	}
 
 	ctx.VisitAllModules(func(module android.Module) {
 		m, ok := module.(*Module)
-		if !ok || !m.Enabled() || !m.useVndk() || !m.installable() {
+		if !ok || !m.Enabled() {
 			return
 		}
 
-		if m.Target().NativeBridge == android.NativeBridgeEnabled {
+		l, vndkType, ok := isVndkSnapshotLibrary(m)
+		if !ok {
 			return
 		}
 
-		lib, is_lib := m.linker.(*libraryDecorator)
-		prebuilt_lib, is_prebuilt_lib := m.linker.(*prebuiltLibraryLinker)
-
-		if !(is_lib && lib.shared()) && !(is_prebuilt_lib && prebuilt_lib.shared()) {
+		libs, ok := installVndkSnapshotLib(m, l, vndkType)
+		if !ok {
 			return
 		}
 
-		is_2nd := m.Target().Arch.ArchType != ctx.Config().DevicePrimaryArchType()
+		snapshotOutputs = append(snapshotOutputs, libs...)
 
-		name := ctx.ModuleName(module)
+		// We glob headers from include directories inside source tree. So we first gather
+		// all include directories inside our source tree. On the contrast, we manually
+		// collect generated headers from dependencies as they can't globbed.
+		generatedHeaders = append(generatedHeaders, l.exportedGeneratedHeaders()...)
+		for _, dir := range append(l.exportedDirs(), l.exportedSystemDirs()...) {
+			exportedIncludes[dir.String()] = true
+		}
+	})
 
-		if inList(name, *vndkCoreLibraries) {
-			if is_2nd {
-				out := installVndkSnapshotLib(ctx, name, m, vndkCoreLib2ndPath)
-				outputs.vndkCoreLibs2nd = append(outputs.vndkCoreLibs2nd, out)
-			} else {
-				out := installVndkSnapshotLib(ctx, name, m, vndkCoreLibPath)
-				outputs.vndkCoreLibs = append(outputs.vndkCoreLibs, out)
+	if ctx.Config().VndkSnapshotBuildArtifacts() {
+		globbedHeaders := make(map[string]bool)
+
+		for _, dir := range android.SortedStringKeys(exportedIncludes) {
+			// Skip if dir is for generated headers
+			if strings.HasPrefix(dir, android.PathForOutput(ctx).String()) {
+				continue
 			}
-			tryBuildNotice(m)
-		} else if inList(name, *vndkSpLibraries) {
-			if is_2nd {
-				out := installVndkSnapshotLib(ctx, name, m, vndkSpLib2ndPath)
-				outputs.vndkSpLibs2nd = append(outputs.vndkSpLibs2nd, out)
-			} else {
-				out := installVndkSnapshotLib(ctx, name, m, vndkSpLibPath)
-				outputs.vndkSpLibs = append(outputs.vndkSpLibs, out)
+			exts := headerExts
+			// Glob all files under this special directory, because of C++ headers.
+			if strings.HasPrefix(dir, "external/libcxx/include") {
+				exts = []string{""}
 			}
-			tryBuildNotice(m)
+			for _, ext := range exts {
+				glob, err := ctx.GlobWithDeps(dir+"/**/*"+ext, nil)
+				if err != nil {
+					ctx.Errorf("%#v\n", err)
+					return
+				}
+				for _, header := range glob {
+					if strings.HasSuffix(header, "/") {
+						continue
+					}
+					globbedHeaders[header] = true
+				}
+			}
 		}
-	})
 
-	configsPath := filepath.Join(snapshotVariantPath, "configs")
-	vndkCoreTxt := android.PathForOutput(ctx, configsPath, "vndkcore.libraries.txt")
-	vndkPrivateTxt := android.PathForOutput(ctx, configsPath, "vndkprivate.libraries.txt")
-	modulePathTxt := android.PathForOutput(ctx, configsPath, "module_paths.txt")
-
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        android.WriteFile,
-		Output:      vndkCoreTxt,
-		Description: "vndk snapshot vndkcore.libraries.txt",
-		Args: map[string]string{
-			"content": android.JoinWithSuffix(*vndkCoreLibraries, ".so", "\\n"),
-		},
-	})
-	outputs.configs = append(outputs.configs, vndkCoreTxt)
-
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        android.WriteFile,
-		Output:      vndkPrivateTxt,
-		Description: "vndk snapshot vndkprivate.libraries.txt",
-		Args: map[string]string{
-			"content": android.JoinWithSuffix(*vndkPrivateLibraries, ".so", "\\n"),
-		},
-	})
-	outputs.configs = append(outputs.configs, vndkPrivateTxt)
-
-	var modulePathTxtBuilder strings.Builder
-
-	modulePaths := modulePaths(ctx.Config())
-	var libs []string
-	for lib := range modulePaths {
-		libs = append(libs, lib)
-	}
-	sort.Strings(libs)
-
-	first := true
-	for _, lib := range libs {
-		if first {
-			first = false
-		} else {
-			modulePathTxtBuilder.WriteString("\\n")
+		for _, header := range android.SortedStringKeys(globbedHeaders) {
+			snapshotOutputs = append(snapshotOutputs, installSnapshotFileFromPath(
+				android.PathForSource(ctx, header), filepath.Join(includeDir, header)))
 		}
-		modulePathTxtBuilder.WriteString(lib)
-		modulePathTxtBuilder.WriteString(".so ")
-		modulePathTxtBuilder.WriteString(modulePaths[lib])
+
+		isHeader := func(path string) bool {
+			for _, ext := range headerExts {
+				if strings.HasSuffix(path, ext) {
+					return true
+				}
+			}
+			return false
+		}
+
+		// For generated headers, manually install one by one, rather than glob
+		for _, path := range android.PathsToDirectorySortedPaths(android.FirstUniquePaths(generatedHeaders)) {
+			header := path.String()
+
+			if !isHeader(header) {
+				continue
+			}
+
+			snapshotOutputs = append(snapshotOutputs, installSnapshotFileFromPath(
+				path, filepath.Join(includeDir, header)))
+		}
 	}
 
+	// install *.libraries.txt except vndkcorevariant.libraries.txt
+	ctx.VisitAllModules(func(module android.Module) {
+		m, ok := module.(*vndkLibrariesTxt)
+		if !ok || !m.Enabled() || m.Name() == vndkUsingCoreVariantLibrariesTxt {
+			return
+		}
+		snapshotOutputs = append(snapshotOutputs, installSnapshotFileFromPath(m.OutputFile(), filepath.Join(configsDir, m.Name())))
+	})
+
+	/*
+		Dump a map to a list file as:
+
+		{key1} {value1}
+		{key2} {value2}
+		...
+	*/
+	installMapListFile := func(m map[string]string, path string) android.OutputPath {
+		var txtBuilder strings.Builder
+		for idx, k := range android.SortedStringKeys(m) {
+			if idx > 0 {
+				txtBuilder.WriteString("\\n")
+			}
+			txtBuilder.WriteString(k)
+			txtBuilder.WriteString(" ")
+			txtBuilder.WriteString(m[k])
+		}
+		return installSnapshotFileFromContent(txtBuilder.String(), path)
+	}
+
+	/*
+		module_paths.txt contains paths on which VNDK modules are defined.
+		e.g.,
+			libbase.so system/core/base
+			libc.so bionic/libc
+			...
+	*/
+	snapshotOutputs = append(snapshotOutputs, installMapListFile(modulePaths, filepath.Join(configsDir, "module_paths.txt")))
+
+	/*
+		module_names.txt contains names as which VNDK modules are defined,
+		because output filename and module name can be different with stem and suffix properties.
+
+		e.g.,
+			libcutils.so libcutils
+			libprotobuf-cpp-full-3.9.2.so libprotobuf-cpp-full
+			...
+	*/
+	snapshotOutputs = append(snapshotOutputs, installMapListFile(moduleNames, filepath.Join(configsDir, "module_names.txt")))
+
+	// All artifacts are ready. Sort them to normalize ninja and then zip.
+	sort.Slice(snapshotOutputs, func(i, j int) bool {
+		return snapshotOutputs[i].String() < snapshotOutputs[j].String()
+	})
+
+	zipPath := android.PathForOutput(ctx, snapshotDir, "android-vndk-"+ctx.DeviceConfig().DeviceArch()+".zip")
+	zipRule := android.NewRuleBuilder()
+
+	// If output files are too many, soong_zip command can exceed ARG_MAX.
+	// So first dump file lists into a single list file, and then feed it to Soong
+	snapshotOutputList := android.PathForOutput(ctx, snapshotDir, "android-vndk-"+ctx.DeviceConfig().DeviceArch()+"_list")
+	zipRule.Command().
+		Text("( xargs").
+		FlagWithRspFileInputList("-n1 echo < ", snapshotOutputs).
+		FlagWithOutput("| tr -d \\' > ", snapshotOutputList).
+		Text(")")
+
+	zipRule.Temporary(snapshotOutputList)
+
+	zipRule.Command().
+		BuiltTool(ctx, "soong_zip").
+		FlagWithOutput("-o ", zipPath).
+		FlagWithArg("-C ", android.PathForOutput(ctx, snapshotDir).String()).
+		FlagWithInput("-l ", snapshotOutputList)
+
+	zipRule.Build(pctx, ctx, zipPath.String(), "vndk snapshot "+zipPath.String())
+	c.vndkSnapshotZipFile = android.OptionalPathForPath(zipPath)
+}
+
+func getVndkFileName(m *Module) (string, error) {
+	if library, ok := m.linker.(*libraryDecorator); ok {
+		return library.getLibNameHelper(m.BaseModuleName(), true) + ".so", nil
+	}
+	if prebuilt, ok := m.linker.(*prebuiltLibraryLinker); ok {
+		return prebuilt.libraryDecorator.getLibNameHelper(m.BaseModuleName(), true) + ".so", nil
+	}
+	return "", fmt.Errorf("VNDK library should have libraryDecorator or prebuiltLibraryLinker as linker: %T", m.linker)
+}
+
+func (c *vndkSnapshotSingleton) buildVndkLibrariesTxtFiles(ctx android.SingletonContext) {
+	llndk := android.SortedStringMapValues(llndkLibraries(ctx.Config()))
+	vndkcore := android.SortedStringMapValues(vndkCoreLibraries(ctx.Config()))
+	vndksp := android.SortedStringMapValues(vndkSpLibraries(ctx.Config()))
+	vndkprivate := android.SortedStringMapValues(vndkPrivateLibraries(ctx.Config()))
+
+	// Build list of vndk libs as merged & tagged & filter-out(libclang_rt):
+	// Since each target have different set of libclang_rt.* files,
+	// keep the common set of files in vndk.libraries.txt
+	var merged []string
+	filterOutLibClangRt := func(libList []string) (filtered []string) {
+		for _, lib := range libList {
+			if !strings.HasPrefix(lib, "libclang_rt.") {
+				filtered = append(filtered, lib)
+			}
+		}
+		return
+	}
+	merged = append(merged, addPrefix(filterOutLibClangRt(llndk), "LLNDK: ")...)
+	merged = append(merged, addPrefix(vndksp, "VNDK-SP: ")...)
+	merged = append(merged, addPrefix(filterOutLibClangRt(vndkcore), "VNDK-core: ")...)
+	merged = append(merged, addPrefix(vndkprivate, "VNDK-private: ")...)
+	c.vndkLibrariesFile = android.PathForOutput(ctx, "vndk", "vndk.libraries.txt")
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        android.WriteFile,
-		Output:      modulePathTxt,
-		Description: "vndk snapshot module_paths.txt",
+		Output:      c.vndkLibrariesFile,
+		Description: "Writing " + c.vndkLibrariesFile.String(),
 		Args: map[string]string{
-			"content": modulePathTxtBuilder.String(),
+			"content": strings.Join(merged, "\\n"),
 		},
 	})
-	outputs.configs = append(outputs.configs, modulePathTxt)
+}
+
+func (c *vndkSnapshotSingleton) MakeVars(ctx android.MakeVarsContext) {
+	// Make uses LLNDK_MOVED_TO_APEX_LIBRARIES to avoid installing libraries on /system if
+	// they been moved to an apex.
+	movedToApexLlndkLibraries := []string{}
+	for lib := range llndkLibraries(ctx.Config()) {
+		// Skip bionic libs, they are handled in different manner
+		if android.DirectlyInAnyApex(&notOnHostContext{}, lib) && !isBionic(lib) {
+			movedToApexLlndkLibraries = append(movedToApexLlndkLibraries, lib)
+		}
+	}
+	ctx.Strict("LLNDK_MOVED_TO_APEX_LIBRARIES", strings.Join(movedToApexLlndkLibraries, " "))
+
+	// Make uses LLNDK_LIBRARIES to determine which libraries to install.
+	// HWASAN is only part of the LL-NDK in builds in which libc depends on HWASAN.
+	// Therefore, by removing the library here, we cause it to only be installed if libc
+	// depends on it.
+	installedLlndkLibraries := []string{}
+	for lib := range llndkLibraries(ctx.Config()) {
+		if strings.HasPrefix(lib, "libclang_rt.hwasan-") {
+			continue
+		}
+		installedLlndkLibraries = append(installedLlndkLibraries, lib)
+	}
+	sort.Strings(installedLlndkLibraries)
+	ctx.Strict("LLNDK_LIBRARIES", strings.Join(installedLlndkLibraries, " "))
+
+	ctx.Strict("VNDK_CORE_LIBRARIES", strings.Join(android.SortedStringKeys(vndkCoreLibraries(ctx.Config())), " "))
+	ctx.Strict("VNDK_SAMEPROCESS_LIBRARIES", strings.Join(android.SortedStringKeys(vndkSpLibraries(ctx.Config())), " "))
+	ctx.Strict("VNDK_PRIVATE_LIBRARIES", strings.Join(android.SortedStringKeys(vndkPrivateLibraries(ctx.Config())), " "))
+	ctx.Strict("VNDK_USING_CORE_VARIANT_LIBRARIES", strings.Join(android.SortedStringKeys(vndkUsingCoreVariantLibraries(ctx.Config())), " "))
+
+	ctx.Strict("VNDK_LIBRARIES_FILE", c.vndkLibrariesFile.String())
+	ctx.Strict("SOONG_VNDK_SNAPSHOT_ZIP", c.vndkSnapshotZipFile.String())
 }
diff --git a/cc/vndk_prebuilt.go b/cc/vndk_prebuilt.go
index 0ecf566..c941c46 100644
--- a/cc/vndk_prebuilt.go
+++ b/cc/vndk_prebuilt.go
@@ -61,6 +61,13 @@
 	// Prebuilt files for each arch.
 	Srcs []string `android:"arch_variant"`
 
+	// list of directories relative to the Blueprints file that will be added to the include
+	// path (using -isystem) for any module that links against this module.
+	Export_system_include_dirs []string `android:"arch_variant"`
+
+	// list of flags that will be used for any module that links against this module.
+	Export_flags []string `android:"arch_variant"`
+
 	// Check the prebuilt ELF files (e.g. DT_SONAME, DT_NEEDED, resolution of undefined symbols,
 	// etc).
 	Check_elf_files *bool
@@ -122,13 +129,43 @@
 
 func (p *vndkPrebuiltLibraryDecorator) link(ctx ModuleContext,
 	flags Flags, deps PathDeps, objs Objects) android.Path {
+
+	if !p.matchesWithDevice(ctx.DeviceConfig()) {
+		ctx.Module().SkipInstall()
+		return nil
+	}
+
 	if len(p.properties.Srcs) > 0 && p.shared() {
+		p.libraryDecorator.exportIncludes(ctx)
+		p.libraryDecorator.reexportSystemDirs(
+			android.PathsForModuleSrc(ctx, p.properties.Export_system_include_dirs)...)
+		p.libraryDecorator.reexportFlags(p.properties.Export_flags...)
 		// current VNDK prebuilts are only shared libs.
 		return p.singleSourcePath(ctx)
 	}
+
+	ctx.Module().SkipInstall()
 	return nil
 }
 
+func (p *vndkPrebuiltLibraryDecorator) matchesWithDevice(config android.DeviceConfig) bool {
+	arches := config.Arches()
+	if len(arches) == 0 || arches[0].ArchType.String() != p.arch() {
+		return false
+	}
+	if config.BinderBitness() != p.binderBit() {
+		return false
+	}
+	if len(p.properties.Srcs) == 0 {
+		return false
+	}
+	return true
+}
+
+func (p *vndkPrebuiltLibraryDecorator) nativeCoverage() bool {
+	return false
+}
+
 func (p *vndkPrebuiltLibraryDecorator) install(ctx ModuleContext, file android.Path) {
 	arches := ctx.DeviceConfig().Arches()
 	if len(arches) == 0 || arches[0].ArchType.String() != p.arch() {
@@ -153,13 +190,19 @@
 	module.stl = nil
 	module.sanitize = nil
 	library.StripProperties.Strip.None = BoolPtr(true)
-	module.Properties.UseVndk = true
 
 	prebuilt := &vndkPrebuiltLibraryDecorator{
 		libraryDecorator: library,
 	}
 
 	prebuilt.properties.Check_elf_files = BoolPtr(false)
+	prebuilt.baseLinker.Properties.No_libcrt = BoolPtr(true)
+	prebuilt.baseLinker.Properties.Nocrt = BoolPtr(true)
+
+	// Prevent default system libs (libc, libm, and libdl) from being linked
+	if prebuilt.baseLinker.Properties.System_shared_libs == nil {
+		prebuilt.baseLinker.Properties.System_shared_libs = []string{}
+	}
 
 	module.compiler = nil
 	module.linker = prebuilt
@@ -192,11 +235,11 @@
 //            },
 //        },
 //    }
-func vndkPrebuiltSharedFactory() android.Module {
+func VndkPrebuiltSharedFactory() android.Module {
 	module := vndkPrebuiltSharedLibrary()
 	return module.Init()
 }
 
 func init() {
-	android.RegisterModuleType("vndk_prebuilt_shared", vndkPrebuiltSharedFactory)
+	android.RegisterModuleType("vndk_prebuilt_shared", VndkPrebuiltSharedFactory)
 }
diff --git a/cc/xom.go b/cc/xom.go
index 9337990..ce817aa 100644
--- a/cc/xom.go
+++ b/cc/xom.go
@@ -68,7 +68,10 @@
 	if !disableXom || (xom.Properties.Xom != nil && *xom.Properties.Xom) {
 		// XOM is only supported on AArch64 when using lld.
 		if ctx.Arch().ArchType == android.Arm64 && ctx.useClangLld(ctx) {
-			flags.LdFlags = append(flags.LdFlags, "-Wl,-execute-only")
+			flags.Local.LdFlags = append(flags.Local.LdFlags,
+				"-Wl,--execute-only",
+				"-Wl,-z,separate-code",
+			)
 		}
 	}
 
diff --git a/cmd/dep_fixer/Android.bp b/cmd/dep_fixer/Android.bp
index d2d1113..97364d5 100644
--- a/cmd/dep_fixer/Android.bp
+++ b/cmd/dep_fixer/Android.bp
@@ -14,10 +14,6 @@
 
 blueprint_go_binary {
     name: "dep_fixer",
-    deps: ["androidmk-parser"],
-    srcs: [
-        "main.go",
-        "deps.go",
-    ],
-    testSrcs: ["deps_test.go"],
+    deps: ["soong-makedeps"],
+    srcs: ["main.go"],
 }
diff --git a/cmd/dep_fixer/main.go b/cmd/dep_fixer/main.go
index f94cf2f..d1bd139 100644
--- a/cmd/dep_fixer/main.go
+++ b/cmd/dep_fixer/main.go
@@ -25,6 +25,8 @@
 	"io/ioutil"
 	"log"
 	"os"
+
+	"android/soong/makedeps"
 )
 
 func main() {
@@ -39,7 +41,7 @@
 		log.Fatal("Expected at least one input file as an argument")
 	}
 
-	var mergedDeps *Deps
+	var mergedDeps *makedeps.Deps
 	var firstInput []byte
 
 	for i, arg := range flag.Args() {
@@ -48,7 +50,7 @@
 			log.Fatalf("Error opening %q: %v", arg, err)
 		}
 
-		deps, err := Parse(arg, bytes.NewBuffer(append([]byte(nil), input...)))
+		deps, err := makedeps.Parse(arg, bytes.NewBuffer(append([]byte(nil), input...)))
 		if err != nil {
 			log.Fatalf("Failed to parse: %v", err)
 		}
diff --git a/cmd/diff_target_files/target_files.go b/cmd/diff_target_files/target_files.go
index 8705ca7..0fa04e8 100644
--- a/cmd/diff_target_files/target_files.go
+++ b/cmd/diff_target_files/target_files.go
@@ -28,7 +28,7 @@
 	"ODM/",
 	"OEM/",
 	"PRODUCT/",
-	"PRODUCT_SERVICES/",
+	"SYSTEM_EXT/",
 	"ROOT/",
 	"SYSTEM/",
 	"SYSTEM_OTHER/",
diff --git a/cmd/javac_wrapper/javac_wrapper.go b/cmd/javac_wrapper/javac_wrapper.go
index 7a448ba..4679906 100644
--- a/cmd/javac_wrapper/javac_wrapper.go
+++ b/cmd/javac_wrapper/javac_wrapper.go
@@ -31,6 +31,7 @@
 	"os"
 	"os/exec"
 	"regexp"
+	"strconv"
 	"syscall"
 )
 
@@ -80,10 +81,11 @@
 
 	pw.Close()
 
+	proc := processor{}
 	// Process subprocess stdout asynchronously
 	errCh := make(chan error)
 	go func() {
-		errCh <- process(pr, out)
+		errCh <- proc.process(pr, out)
 	}()
 
 	// Wait for subprocess to finish
@@ -117,14 +119,18 @@
 	return 0, nil
 }
 
-func process(r io.Reader, w io.Writer) error {
+type processor struct {
+	silencedWarnings int
+}
+
+func (proc *processor) process(r io.Reader, w io.Writer) error {
 	scanner := bufio.NewScanner(r)
 	// Some javac wrappers output the entire list of java files being
 	// compiled on a single line, which can be very large, set the maximum
 	// buffer size to 2MB.
 	scanner.Buffer(nil, 2*1024*1024)
 	for scanner.Scan() {
-		processLine(w, scanner.Text())
+		proc.processLine(w, scanner.Text())
 	}
 	err := scanner.Err()
 	if err != nil {
@@ -133,12 +139,32 @@
 	return nil
 }
 
-func processLine(w io.Writer, line string) {
+func (proc *processor) processLine(w io.Writer, line string) {
+	for _, f := range warningFilters {
+		if f.MatchString(line) {
+			proc.silencedWarnings++
+			return
+		}
+	}
 	for _, f := range filters {
 		if f.MatchString(line) {
 			return
 		}
 	}
+	if match := warningCount.FindStringSubmatch(line); match != nil {
+		c, err := strconv.Atoi(match[1])
+		if err == nil {
+			c -= proc.silencedWarnings
+			if c == 0 {
+				return
+			} else {
+				line = fmt.Sprintf("%d warning", c)
+				if c > 1 {
+					line += "s"
+				}
+			}
+		}
+	}
 	for _, p := range colorPatterns {
 		var matched bool
 		if line, matched = applyColor(line, p.color, p.re); matched {
@@ -170,12 +196,17 @@
 	{markerRe, green},
 }
 
+var warningCount = regexp.MustCompile(`^([0-9]+) warning(s)?$`)
+
+var warningFilters = []*regexp.Regexp{
+	regexp.MustCompile(`bootstrap class path not set in conjunction with -source`),
+}
+
 var filters = []*regexp.Regexp{
 	regexp.MustCompile(`Note: (Some input files|.*\.java) uses? or overrides? a deprecated API.`),
 	regexp.MustCompile(`Note: Recompile with -Xlint:deprecation for details.`),
 	regexp.MustCompile(`Note: (Some input files|.*\.java) uses? unchecked or unsafe operations.`),
 	regexp.MustCompile(`Note: Recompile with -Xlint:unchecked for details.`),
-	regexp.MustCompile(`bootstrap class path not set in conjunction with -source`),
 
 	regexp.MustCompile(`javadoc: warning - The old Doclet and Taglet APIs in the packages`),
 	regexp.MustCompile(`com.sun.javadoc, com.sun.tools.doclets and their implementations`),
diff --git a/cmd/javac_wrapper/javac_wrapper_test.go b/cmd/javac_wrapper/javac_wrapper_test.go
index ad657e7..ad23001 100644
--- a/cmd/javac_wrapper/javac_wrapper_test.go
+++ b/cmd/javac_wrapper/javac_wrapper_test.go
@@ -75,13 +75,29 @@
 `,
 		out: "\n",
 	},
+	{
+		in: `
+warning: [options] bootstrap class path not set in conjunction with -source 1.9\n
+1 warning
+`,
+		out: "\n",
+	},
+	{
+		in: `
+warning: foo
+warning: [options] bootstrap class path not set in conjunction with -source 1.9\n
+2 warnings
+`,
+		out: "\n\x1b[1m\x1b[35mwarning:\x1b[0m\x1b[1m foo\x1b[0m\n1 warning\n",
+	},
 }
 
 func TestJavacColorize(t *testing.T) {
 	for i, test := range testCases {
 		t.Run(strconv.Itoa(i), func(t *testing.T) {
 			buf := new(bytes.Buffer)
-			err := process(bytes.NewReader([]byte(test.in)), buf)
+			proc := processor{}
+			err := proc.process(bytes.NewReader([]byte(test.in)), buf)
 			if err != nil {
 				t.Errorf("error: %q", err)
 			}
diff --git a/cmd/merge_zips/Android.bp b/cmd/merge_zips/Android.bp
index ab658fd..f70c86e 100644
--- a/cmd/merge_zips/Android.bp
+++ b/cmd/merge_zips/Android.bp
@@ -18,6 +18,7 @@
       "android-archive-zip",
       "blueprint-pathtools",
       "soong-jar",
+      "soong-zip",
     ],
     srcs: [
         "merge_zips.go",
diff --git a/cmd/merge_zips/merge_zips.go b/cmd/merge_zips/merge_zips.go
index 68fe259..a9be612 100644
--- a/cmd/merge_zips/merge_zips.go
+++ b/cmd/merge_zips/merge_zips.go
@@ -30,8 +30,566 @@
 
 	"android/soong/jar"
 	"android/soong/third_party/zip"
+	soongZip "android/soong/zip"
 )
 
+// Input zip: we can open it, close it, and obtain an array of entries
+type InputZip interface {
+	Name() string
+	Open() error
+	Close() error
+	Entries() []*zip.File
+	IsOpen() bool
+}
+
+// An entry that can be written to the output zip
+type ZipEntryContents interface {
+	String() string
+	IsDir() bool
+	CRC32() uint32
+	Size() uint64
+	WriteToZip(dest string, zw *zip.Writer) error
+}
+
+// a ZipEntryFromZip is a ZipEntryContents that pulls its content from another zip
+// identified by the input zip and the index of the entry in its entries array
+type ZipEntryFromZip struct {
+	inputZip InputZip
+	index    int
+	name     string
+	isDir    bool
+	crc32    uint32
+	size     uint64
+}
+
+func NewZipEntryFromZip(inputZip InputZip, entryIndex int) *ZipEntryFromZip {
+	fi := inputZip.Entries()[entryIndex]
+	newEntry := ZipEntryFromZip{inputZip: inputZip,
+		index: entryIndex,
+		name:  fi.Name,
+		isDir: fi.FileInfo().IsDir(),
+		crc32: fi.CRC32,
+		size:  fi.UncompressedSize64,
+	}
+	return &newEntry
+}
+
+func (ze ZipEntryFromZip) String() string {
+	return fmt.Sprintf("%s!%s", ze.inputZip.Name(), ze.name)
+}
+
+func (ze ZipEntryFromZip) IsDir() bool {
+	return ze.isDir
+}
+
+func (ze ZipEntryFromZip) CRC32() uint32 {
+	return ze.crc32
+}
+
+func (ze ZipEntryFromZip) Size() uint64 {
+	return ze.size
+}
+
+func (ze ZipEntryFromZip) WriteToZip(dest string, zw *zip.Writer) error {
+	if err := ze.inputZip.Open(); err != nil {
+		return err
+	}
+	return zw.CopyFrom(ze.inputZip.Entries()[ze.index], dest)
+}
+
+// a ZipEntryFromBuffer is a ZipEntryContents that pulls its content from a []byte
+type ZipEntryFromBuffer struct {
+	fh      *zip.FileHeader
+	content []byte
+}
+
+func (be ZipEntryFromBuffer) String() string {
+	return "internal buffer"
+}
+
+func (be ZipEntryFromBuffer) IsDir() bool {
+	return be.fh.FileInfo().IsDir()
+}
+
+func (be ZipEntryFromBuffer) CRC32() uint32 {
+	return crc32.ChecksumIEEE(be.content)
+}
+
+func (be ZipEntryFromBuffer) Size() uint64 {
+	return uint64(len(be.content))
+}
+
+func (be ZipEntryFromBuffer) WriteToZip(dest string, zw *zip.Writer) error {
+	w, err := zw.CreateHeader(be.fh)
+	if err != nil {
+		return err
+	}
+
+	if !be.IsDir() {
+		_, err = w.Write(be.content)
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+// Processing state.
+type OutputZip struct {
+	outputWriter     *zip.Writer
+	stripDirEntries  bool
+	emulateJar       bool
+	sortEntries      bool
+	ignoreDuplicates bool
+	excludeDirs      []string
+	excludeFiles     []string
+	sourceByDest     map[string]ZipEntryContents
+}
+
+func NewOutputZip(outputWriter *zip.Writer, sortEntries, emulateJar, stripDirEntries, ignoreDuplicates bool) *OutputZip {
+	return &OutputZip{
+		outputWriter:     outputWriter,
+		stripDirEntries:  stripDirEntries,
+		emulateJar:       emulateJar,
+		sortEntries:      sortEntries,
+		sourceByDest:     make(map[string]ZipEntryContents, 0),
+		ignoreDuplicates: ignoreDuplicates,
+	}
+}
+
+func (oz *OutputZip) setExcludeDirs(excludeDirs []string) {
+	oz.excludeDirs = make([]string, len(excludeDirs))
+	for i, dir := range excludeDirs {
+		oz.excludeDirs[i] = filepath.Clean(dir)
+	}
+}
+
+func (oz *OutputZip) setExcludeFiles(excludeFiles []string) {
+	oz.excludeFiles = excludeFiles
+}
+
+// Adds an entry with given name whose source is given ZipEntryContents. Returns old ZipEntryContents
+// if entry with given name already exists.
+func (oz *OutputZip) addZipEntry(name string, source ZipEntryContents) (ZipEntryContents, error) {
+	if existingSource, exists := oz.sourceByDest[name]; exists {
+		return existingSource, nil
+	}
+	oz.sourceByDest[name] = source
+	// Delay writing an entry if entries need to be rearranged.
+	if oz.emulateJar || oz.sortEntries {
+		return nil, nil
+	}
+	return nil, source.WriteToZip(name, oz.outputWriter)
+}
+
+// Adds an entry for the manifest (META-INF/MANIFEST.MF from the given file
+func (oz *OutputZip) addManifest(manifestPath string) error {
+	if !oz.stripDirEntries {
+		if _, err := oz.addZipEntry(jar.MetaDir, ZipEntryFromBuffer{jar.MetaDirFileHeader(), nil}); err != nil {
+			return err
+		}
+	}
+	contents, err := ioutil.ReadFile(manifestPath)
+	if err == nil {
+		fh, buf, err := jar.ManifestFileContents(contents)
+		if err == nil {
+			_, err = oz.addZipEntry(jar.ManifestFile, ZipEntryFromBuffer{fh, buf})
+		}
+	}
+	return err
+}
+
+// Adds an entry with given name and contents read from given file
+func (oz *OutputZip) addZipEntryFromFile(name string, path string) error {
+	buf, err := ioutil.ReadFile(path)
+	if err == nil {
+		fh := &zip.FileHeader{
+			Name:               name,
+			Method:             zip.Store,
+			UncompressedSize64: uint64(len(buf)),
+		}
+		fh.SetMode(0700)
+		fh.SetModTime(jar.DefaultTime)
+		_, err = oz.addZipEntry(name, ZipEntryFromBuffer{fh, buf})
+	}
+	return err
+}
+
+func (oz *OutputZip) addEmptyEntry(entry string) error {
+	var emptyBuf []byte
+	fh := &zip.FileHeader{
+		Name:               entry,
+		Method:             zip.Store,
+		UncompressedSize64: uint64(len(emptyBuf)),
+	}
+	fh.SetMode(0700)
+	fh.SetModTime(jar.DefaultTime)
+	_, err := oz.addZipEntry(entry, ZipEntryFromBuffer{fh, emptyBuf})
+	return err
+}
+
+// Returns true if given entry is to be excluded
+func (oz *OutputZip) isEntryExcluded(name string) bool {
+	for _, dir := range oz.excludeDirs {
+		dir = filepath.Clean(dir)
+		patterns := []string{
+			dir + "/",      // the directory itself
+			dir + "/**/*",  // files recursively in the directory
+			dir + "/**/*/", // directories recursively in the directory
+		}
+
+		for _, pattern := range patterns {
+			match, err := pathtools.Match(pattern, name)
+			if err != nil {
+				panic(fmt.Errorf("%s: %s", err.Error(), pattern))
+			}
+			if match {
+				if oz.emulateJar {
+					// When merging jar files, don't strip META-INF/MANIFEST.MF even if stripping META-INF is
+					// requested.
+					// TODO(ccross): which files does this affect?
+					if name != jar.MetaDir && name != jar.ManifestFile {
+						return true
+					}
+				}
+				return true
+			}
+		}
+	}
+
+	for _, pattern := range oz.excludeFiles {
+		match, err := pathtools.Match(pattern, name)
+		if err != nil {
+			panic(fmt.Errorf("%s: %s", err.Error(), pattern))
+		}
+		if match {
+			return true
+		}
+	}
+	return false
+}
+
+// Creates a zip entry whose contents is an entry from the given input zip.
+func (oz *OutputZip) copyEntry(inputZip InputZip, index int) error {
+	entry := NewZipEntryFromZip(inputZip, index)
+	if oz.stripDirEntries && entry.IsDir() {
+		return nil
+	}
+	existingEntry, err := oz.addZipEntry(entry.name, entry)
+	if err != nil {
+		return err
+	}
+	if existingEntry == nil {
+		return nil
+	}
+
+	// File types should match
+	if existingEntry.IsDir() != entry.IsDir() {
+		return fmt.Errorf("Directory/file mismatch at %v from %v and %v\n",
+			entry.name, existingEntry, entry)
+	}
+
+	if oz.ignoreDuplicates ||
+		// Skip manifest and module info files that are not from the first input file
+		(oz.emulateJar && entry.name == jar.ManifestFile || entry.name == jar.ModuleInfoClass) ||
+		// Identical entries
+		(existingEntry.CRC32() == entry.CRC32() && existingEntry.Size() == entry.Size()) ||
+		// Directory entries
+		entry.IsDir() {
+		return nil
+	}
+
+	return fmt.Errorf("Duplicate path %v found in %v and %v\n", entry.name, existingEntry, inputZip.Name())
+}
+
+func (oz *OutputZip) entriesArray() []string {
+	entries := make([]string, len(oz.sourceByDest))
+	i := 0
+	for entry := range oz.sourceByDest {
+		entries[i] = entry
+		i++
+	}
+	return entries
+}
+
+func (oz *OutputZip) jarSorted() []string {
+	entries := oz.entriesArray()
+	sort.SliceStable(entries, func(i, j int) bool { return jar.EntryNamesLess(entries[i], entries[j]) })
+	return entries
+}
+
+func (oz *OutputZip) alphanumericSorted() []string {
+	entries := oz.entriesArray()
+	sort.Strings(entries)
+	return entries
+}
+
+func (oz *OutputZip) writeEntries(entries []string) error {
+	for _, entry := range entries {
+		source, _ := oz.sourceByDest[entry]
+		if err := source.WriteToZip(entry, oz.outputWriter); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func (oz *OutputZip) getUninitializedPythonPackages(inputZips []InputZip) ([]string, error) {
+	// the runfiles packages needs to be populated with "__init__.py".
+	// the runfiles dirs have been treated as packages.
+	allPackages := make(map[string]bool)
+	initedPackages := make(map[string]bool)
+	getPackage := func(path string) string {
+		ret := filepath.Dir(path)
+		// filepath.Dir("abc") -> "." and filepath.Dir("/abc") -> "/".
+		if ret == "." || ret == "/" {
+			return ""
+		}
+		return ret
+	}
+
+	// put existing __init__.py files to a set first. This set is used for preventing
+	// generated __init__.py files from overwriting existing ones.
+	for _, inputZip := range inputZips {
+		if err := inputZip.Open(); err != nil {
+			return nil, err
+		}
+		for _, file := range inputZip.Entries() {
+			pyPkg := getPackage(file.Name)
+			if filepath.Base(file.Name) == "__init__.py" {
+				if _, found := initedPackages[pyPkg]; found {
+					panic(fmt.Errorf("found __init__.py path duplicates during pars merging: %q", file.Name))
+				}
+				initedPackages[pyPkg] = true
+			}
+			for pyPkg != "" {
+				if _, found := allPackages[pyPkg]; found {
+					break
+				}
+				allPackages[pyPkg] = true
+				pyPkg = getPackage(pyPkg)
+			}
+		}
+	}
+	noInitPackages := make([]string, 0)
+	for pyPkg := range allPackages {
+		if _, found := initedPackages[pyPkg]; !found {
+			noInitPackages = append(noInitPackages, pyPkg)
+		}
+	}
+	return noInitPackages, nil
+}
+
+// An InputZip owned by the InputZipsManager. Opened ManagedInputZip's are chained in the open order.
+type ManagedInputZip struct {
+	owner        *InputZipsManager
+	realInputZip InputZip
+	older        *ManagedInputZip
+	newer        *ManagedInputZip
+}
+
+// Maintains the array of ManagedInputZips, keeping track of open input ones. When an InputZip is opened,
+// may close some other InputZip to limit the number of open ones.
+type InputZipsManager struct {
+	inputZips     []*ManagedInputZip
+	nOpenZips     int
+	maxOpenZips   int
+	openInputZips *ManagedInputZip
+}
+
+func (miz *ManagedInputZip) unlink() {
+	olderMiz := miz.older
+	newerMiz := miz.newer
+	if newerMiz.older != miz || olderMiz.newer != miz {
+		panic(fmt.Errorf("removing %p:%#v: broken list between %p:%#v and %p:%#v",
+			miz, miz, newerMiz, newerMiz, olderMiz, olderMiz))
+	}
+	olderMiz.newer = newerMiz
+	newerMiz.older = olderMiz
+	miz.newer = nil
+	miz.older = nil
+}
+
+func (miz *ManagedInputZip) link(olderMiz *ManagedInputZip) {
+	if olderMiz.newer != nil || olderMiz.older != nil {
+		panic(fmt.Errorf("inputZip is already open"))
+	}
+	oldOlderMiz := miz.older
+	if oldOlderMiz.newer != miz {
+		panic(fmt.Errorf("broken list between %p:%#v and %p:%#v", miz, miz, oldOlderMiz, oldOlderMiz))
+	}
+	miz.older = olderMiz
+	olderMiz.older = oldOlderMiz
+	oldOlderMiz.newer = olderMiz
+	olderMiz.newer = miz
+}
+
+func NewInputZipsManager(nInputZips, maxOpenZips int) *InputZipsManager {
+	if maxOpenZips < 3 {
+		panic(fmt.Errorf("open zips limit should be above 3"))
+	}
+	// In the dummy element .older points to the most recently opened InputZip, and .newer points to the oldest.
+	head := new(ManagedInputZip)
+	head.older = head
+	head.newer = head
+	return &InputZipsManager{
+		inputZips:     make([]*ManagedInputZip, 0, nInputZips),
+		maxOpenZips:   maxOpenZips,
+		openInputZips: head,
+	}
+}
+
+// InputZip factory
+func (izm *InputZipsManager) Manage(inz InputZip) InputZip {
+	iz := &ManagedInputZip{owner: izm, realInputZip: inz}
+	izm.inputZips = append(izm.inputZips, iz)
+	return iz
+}
+
+// Opens or reopens ManagedInputZip.
+func (izm *InputZipsManager) reopen(miz *ManagedInputZip) error {
+	if miz.realInputZip.IsOpen() {
+		if miz != izm.openInputZips {
+			miz.unlink()
+			izm.openInputZips.link(miz)
+		}
+		return nil
+	}
+	if izm.nOpenZips >= izm.maxOpenZips {
+		if err := izm.close(izm.openInputZips.older); err != nil {
+			return err
+		}
+	}
+	if err := miz.realInputZip.Open(); err != nil {
+		return err
+	}
+	izm.openInputZips.link(miz)
+	izm.nOpenZips++
+	return nil
+}
+
+func (izm *InputZipsManager) close(miz *ManagedInputZip) error {
+	if miz.IsOpen() {
+		err := miz.realInputZip.Close()
+		izm.nOpenZips--
+		miz.unlink()
+		return err
+	}
+	return nil
+}
+
+// Checks that openInputZips deque is valid
+func (izm *InputZipsManager) checkOpenZipsDeque() {
+	nReallyOpen := 0
+	el := izm.openInputZips
+	for {
+		elNext := el.older
+		if elNext.newer != el {
+			panic(fmt.Errorf("Element:\n  %p: %v\nNext:\n  %p %v", el, el, elNext, elNext))
+		}
+		if elNext == izm.openInputZips {
+			break
+		}
+		el = elNext
+		if !el.IsOpen() {
+			panic(fmt.Errorf("Found unopened element"))
+		}
+		nReallyOpen++
+		if nReallyOpen > izm.nOpenZips {
+			panic(fmt.Errorf("found %d open zips, should be %d", nReallyOpen, izm.nOpenZips))
+		}
+	}
+	if nReallyOpen > izm.nOpenZips {
+		panic(fmt.Errorf("found %d open zips, should be %d", nReallyOpen, izm.nOpenZips))
+	}
+}
+
+func (miz *ManagedInputZip) Name() string {
+	return miz.realInputZip.Name()
+}
+
+func (miz *ManagedInputZip) Open() error {
+	return miz.owner.reopen(miz)
+}
+
+func (miz *ManagedInputZip) Close() error {
+	return miz.owner.close(miz)
+}
+
+func (miz *ManagedInputZip) IsOpen() bool {
+	return miz.realInputZip.IsOpen()
+}
+
+func (miz *ManagedInputZip) Entries() []*zip.File {
+	if !miz.IsOpen() {
+		panic(fmt.Errorf("%s: is not open", miz.Name()))
+	}
+	return miz.realInputZip.Entries()
+}
+
+// Actual processing.
+func mergeZips(inputZips []InputZip, writer *zip.Writer, manifest, pyMain string,
+	sortEntries, emulateJar, emulatePar, stripDirEntries, ignoreDuplicates bool,
+	excludeFiles, excludeDirs []string, zipsToNotStrip map[string]bool) error {
+
+	out := NewOutputZip(writer, sortEntries, emulateJar, stripDirEntries, ignoreDuplicates)
+	out.setExcludeFiles(excludeFiles)
+	out.setExcludeDirs(excludeDirs)
+	if manifest != "" {
+		if err := out.addManifest(manifest); err != nil {
+			return err
+		}
+	}
+	if pyMain != "" {
+		if err := out.addZipEntryFromFile("__main__.py", pyMain); err != nil {
+			return err
+		}
+	}
+
+	if emulatePar {
+		noInitPackages, err := out.getUninitializedPythonPackages(inputZips)
+		if err != nil {
+			return err
+		}
+		for _, uninitializedPyPackage := range noInitPackages {
+			if err = out.addEmptyEntry(filepath.Join(uninitializedPyPackage, "__init__.py")); err != nil {
+				return err
+			}
+		}
+	}
+
+	// Finally, add entries from all the input zips.
+	for _, inputZip := range inputZips {
+		_, copyFully := zipsToNotStrip[inputZip.Name()]
+		if err := inputZip.Open(); err != nil {
+			return err
+		}
+
+		for i, entry := range inputZip.Entries() {
+			if copyFully || !out.isEntryExcluded(entry.Name) {
+				if err := out.copyEntry(inputZip, i); err != nil {
+					return err
+				}
+			}
+		}
+		// Unless we need to rearrange the entries, the input zip can now be closed.
+		if !(emulateJar || sortEntries) {
+			if err := inputZip.Close(); err != nil {
+				return err
+			}
+		}
+	}
+
+	if emulateJar {
+		return out.writeEntries(out.jarSorted())
+	} else if sortEntries {
+		return out.writeEntries(out.alphanumericSorted())
+	}
+	return nil
+}
+
+// Process command line
 type fileList []string
 
 func (f *fileList) String() string {
@@ -50,9 +608,8 @@
 	return `""`
 }
 
-func (s zipsToNotStripSet) Set(zip_path string) error {
-	s[zip_path] = true
-
+func (s zipsToNotStripSet) Set(path string) error {
+	s[path] = true
 	return nil
 }
 
@@ -60,8 +617,8 @@
 	sortEntries      = flag.Bool("s", false, "sort entries (defaults to the order from the input zip files)")
 	emulateJar       = flag.Bool("j", false, "sort zip entries using jar ordering (META-INF first)")
 	emulatePar       = flag.Bool("p", false, "merge zip entries based on par format")
-	stripDirs        fileList
-	stripFiles       fileList
+	excludeDirs      fileList
+	excludeFiles     fileList
 	zipsToNotStrip   = make(zipsToNotStripSet)
 	stripDirEntries  = flag.Bool("D", false, "strip directory entries from the output zip file")
 	manifest         = flag.String("m", "", "manifest file to insert in jar")
@@ -71,14 +628,52 @@
 )
 
 func init() {
-	flag.Var(&stripDirs, "stripDir", "directories to be excluded from the output zip, accepts wildcards")
-	flag.Var(&stripFiles, "stripFile", "files to be excluded from the output zip, accepts wildcards")
+	flag.Var(&excludeDirs, "stripDir", "directories to be excluded from the output zip, accepts wildcards")
+	flag.Var(&excludeFiles, "stripFile", "files to be excluded from the output zip, accepts wildcards")
 	flag.Var(&zipsToNotStrip, "zipToNotStrip", "the input zip file which is not applicable for stripping")
 }
 
+type FileInputZip struct {
+	name   string
+	reader *zip.ReadCloser
+}
+
+func (fiz *FileInputZip) Name() string {
+	return fiz.name
+}
+
+func (fiz *FileInputZip) Close() error {
+	if fiz.IsOpen() {
+		reader := fiz.reader
+		fiz.reader = nil
+		return reader.Close()
+	}
+	return nil
+}
+
+func (fiz *FileInputZip) Entries() []*zip.File {
+	if !fiz.IsOpen() {
+		panic(fmt.Errorf("%s: is not open", fiz.Name()))
+	}
+	return fiz.reader.File
+}
+
+func (fiz *FileInputZip) IsOpen() bool {
+	return fiz.reader != nil
+}
+
+func (fiz *FileInputZip) Open() error {
+	if fiz.IsOpen() {
+		return nil
+	}
+	var err error
+	fiz.reader, err = zip.OpenReader(fiz.Name())
+	return err
+}
+
 func main() {
 	flag.Usage = func() {
-		fmt.Fprintln(os.Stderr, "usage: merge_zips [-jpsD] [-m manifest] [--prefix script] [-pm __main__.py] output [inputs...]")
+		fmt.Fprintln(os.Stderr, "usage: merge_zips [-jpsD] [-m manifest] [--prefix script] [-pm __main__.py] OutputZip [inputs...]")
 		flag.PrintDefaults()
 	}
 
@@ -90,16 +685,28 @@
 		os.Exit(1)
 	}
 	outputPath := args[0]
-	inputs := args[1:]
+	inputs := make([]string, 0)
+	for _, input := range args[1:] {
+		if input[0] == '@' {
+			bytes, err := ioutil.ReadFile(input[1:])
+			if err != nil {
+				log.Fatal(err)
+			}
+			inputs = append(inputs, soongZip.ReadRespFile(bytes)...)
+			continue
+		}
+		inputs = append(inputs, input)
+		continue
+	}
 
 	log.SetFlags(log.Lshortfile)
 
 	// make writer
-	output, err := os.Create(outputPath)
+	outputZip, err := os.Create(outputPath)
 	if err != nil {
 		log.Fatal(err)
 	}
-	defer output.Close()
+	defer outputZip.Close()
 
 	var offset int64
 	if *prefix != "" {
@@ -107,13 +714,13 @@
 		if err != nil {
 			log.Fatal(err)
 		}
-		offset, err = io.Copy(output, prefixFile)
+		offset, err = io.Copy(outputZip, prefixFile)
 		if err != nil {
 			log.Fatal(err)
 		}
 	}
 
-	writer := zip.NewWriter(output)
+	writer := zip.NewWriter(outputZip)
 	defer func() {
 		err := writer.Close()
 		if err != nil {
@@ -122,18 +729,6 @@
 	}()
 	writer.SetOffset(offset)
 
-	// make readers
-	readers := []namedZipReader{}
-	for _, input := range inputs {
-		reader, err := zip.OpenReader(input)
-		if err != nil {
-			log.Fatal(err)
-		}
-		defer reader.Close()
-		namedReader := namedZipReader{path: input, reader: &reader.Reader}
-		readers = append(readers, namedReader)
-	}
-
 	if *manifest != "" && !*emulateJar {
 		log.Fatal(errors.New("must specify -j when specifying a manifest via -m"))
 	}
@@ -143,344 +738,15 @@
 	}
 
 	// do merge
-	err = mergeZips(readers, writer, *manifest, *pyMain, *sortEntries, *emulateJar, *emulatePar,
-		*stripDirEntries, *ignoreDuplicates, []string(stripFiles), []string(stripDirs), map[string]bool(zipsToNotStrip))
+	inputZipsManager := NewInputZipsManager(len(inputs), 1000)
+	inputZips := make([]InputZip, len(inputs))
+	for i, input := range inputs {
+		inputZips[i] = inputZipsManager.Manage(&FileInputZip{name: input})
+	}
+	err = mergeZips(inputZips, writer, *manifest, *pyMain, *sortEntries, *emulateJar, *emulatePar,
+		*stripDirEntries, *ignoreDuplicates, []string(excludeFiles), []string(excludeDirs),
+		map[string]bool(zipsToNotStrip))
 	if err != nil {
 		log.Fatal(err)
 	}
 }
-
-// a namedZipReader reads a .zip file and can say which file it's reading
-type namedZipReader struct {
-	path   string
-	reader *zip.Reader
-}
-
-// a zipEntryPath refers to a file contained in a zip
-type zipEntryPath struct {
-	zipName   string
-	entryName string
-}
-
-func (p zipEntryPath) String() string {
-	return p.zipName + "/" + p.entryName
-}
-
-// a zipEntry is a zipSource that pulls its content from another zip
-type zipEntry struct {
-	path    zipEntryPath
-	content *zip.File
-}
-
-func (ze zipEntry) String() string {
-	return ze.path.String()
-}
-
-func (ze zipEntry) IsDir() bool {
-	return ze.content.FileInfo().IsDir()
-}
-
-func (ze zipEntry) CRC32() uint32 {
-	return ze.content.FileHeader.CRC32
-}
-
-func (ze zipEntry) Size() uint64 {
-	return ze.content.FileHeader.UncompressedSize64
-}
-
-func (ze zipEntry) WriteToZip(dest string, zw *zip.Writer) error {
-	return zw.CopyFrom(ze.content, dest)
-}
-
-// a bufferEntry is a zipSource that pulls its content from a []byte
-type bufferEntry struct {
-	fh      *zip.FileHeader
-	content []byte
-}
-
-func (be bufferEntry) String() string {
-	return "internal buffer"
-}
-
-func (be bufferEntry) IsDir() bool {
-	return be.fh.FileInfo().IsDir()
-}
-
-func (be bufferEntry) CRC32() uint32 {
-	return crc32.ChecksumIEEE(be.content)
-}
-
-func (be bufferEntry) Size() uint64 {
-	return uint64(len(be.content))
-}
-
-func (be bufferEntry) WriteToZip(dest string, zw *zip.Writer) error {
-	w, err := zw.CreateHeader(be.fh)
-	if err != nil {
-		return err
-	}
-
-	if !be.IsDir() {
-		_, err = w.Write(be.content)
-		if err != nil {
-			return err
-		}
-	}
-
-	return nil
-}
-
-type zipSource interface {
-	String() string
-	IsDir() bool
-	CRC32() uint32
-	Size() uint64
-	WriteToZip(dest string, zw *zip.Writer) error
-}
-
-// a fileMapping specifies to copy a zip entry from one place to another
-type fileMapping struct {
-	dest   string
-	source zipSource
-}
-
-func mergeZips(readers []namedZipReader, writer *zip.Writer, manifest, pyMain string,
-	sortEntries, emulateJar, emulatePar, stripDirEntries, ignoreDuplicates bool,
-	stripFiles, stripDirs []string, zipsToNotStrip map[string]bool) error {
-
-	sourceByDest := make(map[string]zipSource, 0)
-	orderedMappings := []fileMapping{}
-
-	// if dest already exists returns a non-null zipSource for the existing source
-	addMapping := func(dest string, source zipSource) zipSource {
-		mapKey := filepath.Clean(dest)
-		if existingSource, exists := sourceByDest[mapKey]; exists {
-			return existingSource
-		}
-
-		sourceByDest[mapKey] = source
-		orderedMappings = append(orderedMappings, fileMapping{source: source, dest: dest})
-		return nil
-	}
-
-	if manifest != "" {
-		if !stripDirEntries {
-			dirHeader := jar.MetaDirFileHeader()
-			dirSource := bufferEntry{dirHeader, nil}
-			addMapping(jar.MetaDir, dirSource)
-		}
-
-		contents, err := ioutil.ReadFile(manifest)
-		if err != nil {
-			return err
-		}
-
-		fh, buf, err := jar.ManifestFileContents(contents)
-		if err != nil {
-			return err
-		}
-
-		fileSource := bufferEntry{fh, buf}
-		addMapping(jar.ManifestFile, fileSource)
-	}
-
-	if pyMain != "" {
-		buf, err := ioutil.ReadFile(pyMain)
-		if err != nil {
-			return err
-		}
-		fh := &zip.FileHeader{
-			Name:               "__main__.py",
-			Method:             zip.Store,
-			UncompressedSize64: uint64(len(buf)),
-		}
-		fh.SetMode(0700)
-		fh.SetModTime(jar.DefaultTime)
-		fileSource := bufferEntry{fh, buf}
-		addMapping("__main__.py", fileSource)
-	}
-
-	if emulatePar {
-		// the runfiles packages needs to be populated with "__init__.py".
-		newPyPkgs := []string{}
-		// the runfiles dirs have been treated as packages.
-		existingPyPkgSet := make(map[string]bool)
-		// put existing __init__.py files to a set first. This set is used for preventing
-		// generated __init__.py files from overwriting existing ones.
-		for _, namedReader := range readers {
-			for _, file := range namedReader.reader.File {
-				if filepath.Base(file.Name) != "__init__.py" {
-					continue
-				}
-				pyPkg := pathBeforeLastSlash(file.Name)
-				if _, found := existingPyPkgSet[pyPkg]; found {
-					panic(fmt.Errorf("found __init__.py path duplicates during pars merging: %q.", file.Name))
-				} else {
-					existingPyPkgSet[pyPkg] = true
-				}
-			}
-		}
-		for _, namedReader := range readers {
-			for _, file := range namedReader.reader.File {
-				var parentPath string /* the path after trimming last "/" */
-				if filepath.Base(file.Name) == "__init__.py" {
-					// for existing __init__.py files, we should trim last "/" for twice.
-					// eg. a/b/c/__init__.py ---> a/b
-					parentPath = pathBeforeLastSlash(pathBeforeLastSlash(file.Name))
-				} else {
-					parentPath = pathBeforeLastSlash(file.Name)
-				}
-				populateNewPyPkgs(parentPath, existingPyPkgSet, &newPyPkgs)
-			}
-		}
-		for _, pkg := range newPyPkgs {
-			var emptyBuf []byte
-			fh := &zip.FileHeader{
-				Name:               filepath.Join(pkg, "__init__.py"),
-				Method:             zip.Store,
-				UncompressedSize64: uint64(len(emptyBuf)),
-			}
-			fh.SetMode(0700)
-			fh.SetModTime(jar.DefaultTime)
-			fileSource := bufferEntry{fh, emptyBuf}
-			addMapping(filepath.Join(pkg, "__init__.py"), fileSource)
-		}
-	}
-	for _, namedReader := range readers {
-		_, skipStripThisZip := zipsToNotStrip[namedReader.path]
-		for _, file := range namedReader.reader.File {
-			if !skipStripThisZip {
-				if skip, err := shouldStripEntry(emulateJar, stripFiles, stripDirs, file.Name); err != nil {
-					return err
-				} else if skip {
-					continue
-				}
-			}
-
-			if stripDirEntries && file.FileInfo().IsDir() {
-				continue
-			}
-
-			// check for other files or directories destined for the same path
-			dest := file.Name
-
-			// make a new entry to add
-			source := zipEntry{path: zipEntryPath{zipName: namedReader.path, entryName: file.Name}, content: file}
-
-			if existingSource := addMapping(dest, source); existingSource != nil {
-				// handle duplicates
-				if existingSource.IsDir() != source.IsDir() {
-					return fmt.Errorf("Directory/file mismatch at %v from %v and %v\n",
-						dest, existingSource, source)
-				}
-
-				if ignoreDuplicates {
-					continue
-				}
-
-				if emulateJar &&
-					file.Name == jar.ManifestFile || file.Name == jar.ModuleInfoClass {
-					// Skip manifest and module info files that are not from the first input file
-					continue
-				}
-
-				if source.IsDir() {
-					continue
-				}
-
-				if existingSource.CRC32() == source.CRC32() && existingSource.Size() == source.Size() {
-					continue
-				}
-
-				return fmt.Errorf("Duplicate path %v found in %v and %v\n",
-					dest, existingSource, source)
-			}
-		}
-	}
-
-	if emulateJar {
-		jarSort(orderedMappings)
-	} else if sortEntries {
-		alphanumericSort(orderedMappings)
-	}
-
-	for _, entry := range orderedMappings {
-		if err := entry.source.WriteToZip(entry.dest, writer); err != nil {
-			return err
-		}
-	}
-
-	return nil
-}
-
-// Sets the given directory and all its ancestor directories as Python packages.
-func populateNewPyPkgs(pkgPath string, existingPyPkgSet map[string]bool, newPyPkgs *[]string) {
-	for pkgPath != "" {
-		if _, found := existingPyPkgSet[pkgPath]; !found {
-			existingPyPkgSet[pkgPath] = true
-			*newPyPkgs = append(*newPyPkgs, pkgPath)
-			// Gets its ancestor directory by trimming last slash.
-			pkgPath = pathBeforeLastSlash(pkgPath)
-		} else {
-			break
-		}
-	}
-}
-
-func pathBeforeLastSlash(path string) string {
-	ret := filepath.Dir(path)
-	// filepath.Dir("abc") -> "." and filepath.Dir("/abc") -> "/".
-	if ret == "." || ret == "/" {
-		return ""
-	}
-	return ret
-}
-
-func shouldStripEntry(emulateJar bool, stripFiles, stripDirs []string, name string) (bool, error) {
-	for _, dir := range stripDirs {
-		dir = filepath.Clean(dir)
-		patterns := []string{
-			dir + "/",      // the directory itself
-			dir + "/**/*",  // files recursively in the directory
-			dir + "/**/*/", // directories recursively in the directory
-		}
-
-		for _, pattern := range patterns {
-			match, err := pathtools.Match(pattern, name)
-			if err != nil {
-				return false, fmt.Errorf("%s: %s", err.Error(), pattern)
-			} else if match {
-				if emulateJar {
-					// When merging jar files, don't strip META-INF/MANIFEST.MF even if stripping META-INF is
-					// requested.
-					// TODO(ccross): which files does this affect?
-					if name != jar.MetaDir && name != jar.ManifestFile {
-						return true, nil
-					}
-				}
-				return true, nil
-			}
-		}
-	}
-
-	for _, pattern := range stripFiles {
-		if match, err := pathtools.Match(pattern, name); err != nil {
-			return false, fmt.Errorf("%s: %s", err.Error(), pattern)
-		} else if match {
-			return true, nil
-		}
-	}
-	return false, nil
-}
-
-func jarSort(files []fileMapping) {
-	sort.SliceStable(files, func(i, j int) bool {
-		return jar.EntryNamesLess(files[i].dest, files[j].dest)
-	})
-}
-
-func alphanumericSort(files []fileMapping) {
-	sort.SliceStable(files, func(i, j int) bool {
-		return files[i].dest < files[j].dest
-	})
-}
diff --git a/cmd/merge_zips/merge_zips_test.go b/cmd/merge_zips/merge_zips_test.go
index dbde270..cb58436 100644
--- a/cmd/merge_zips/merge_zips_test.go
+++ b/cmd/merge_zips/merge_zips_test.go
@@ -51,6 +51,39 @@
 	moduleInfoFile = testZipEntry{jar.ModuleInfoClass, 0755, []byte("module-info")}
 )
 
+type testInputZip struct {
+	name    string
+	entries []testZipEntry
+	reader  *zip.Reader
+}
+
+func (tiz *testInputZip) Name() string {
+	return tiz.name
+}
+
+func (tiz *testInputZip) Open() error {
+	if tiz.reader == nil {
+		tiz.reader = testZipEntriesToZipReader(tiz.entries)
+	}
+	return nil
+}
+
+func (tiz *testInputZip) Close() error {
+	tiz.reader = nil
+	return nil
+}
+
+func (tiz *testInputZip) Entries() []*zip.File {
+	if tiz.reader == nil {
+		panic(fmt.Errorf("%s: should be open to get entries", tiz.Name()))
+	}
+	return tiz.reader.File
+}
+
+func (tiz *testInputZip) IsOpen() bool {
+	return tiz.reader != nil
+}
+
 func TestMergeZips(t *testing.T) {
 	testCases := []struct {
 		name             string
@@ -207,13 +240,9 @@
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
-			var readers []namedZipReader
+			inputZips := make([]InputZip, len(test.in))
 			for i, in := range test.in {
-				r := testZipEntriesToZipReader(in)
-				readers = append(readers, namedZipReader{
-					path:   "in" + strconv.Itoa(i),
-					reader: r,
-				})
+				inputZips[i] = &testInputZip{name: "in" + strconv.Itoa(i), entries: in}
 			}
 
 			want := testZipEntriesToBuf(test.out)
@@ -221,7 +250,7 @@
 			out := &bytes.Buffer{}
 			writer := zip.NewWriter(out)
 
-			err := mergeZips(readers, writer, "", "",
+			err := mergeZips(inputZips, writer, "", "",
 				test.sort, test.jar, false, test.stripDirEntries, test.ignoreDuplicates,
 				test.stripFiles, test.stripDirs, test.zipsToNotStrip)
 
@@ -304,3 +333,60 @@
 
 	return ret
 }
+
+type DummyInpuZip struct {
+	isOpen bool
+}
+
+func (diz *DummyInpuZip) Name() string {
+	return "dummy"
+}
+
+func (diz *DummyInpuZip) Open() error {
+	diz.isOpen = true
+	return nil
+}
+
+func (diz *DummyInpuZip) Close() error {
+	diz.isOpen = false
+	return nil
+}
+
+func (DummyInpuZip) Entries() []*zip.File {
+	panic("implement me")
+}
+
+func (diz *DummyInpuZip) IsOpen() bool {
+	return diz.isOpen
+}
+
+func TestInputZipsManager(t *testing.T) {
+	const nInputZips = 20
+	const nMaxOpenZips = 10
+	izm := NewInputZipsManager(20, 10)
+	managedZips := make([]InputZip, nInputZips)
+	for i := 0; i < nInputZips; i++ {
+		managedZips[i] = izm.Manage(&DummyInpuZip{})
+	}
+
+	t.Run("InputZipsManager", func(t *testing.T) {
+		for i, iz := range managedZips {
+			if err := iz.Open(); err != nil {
+				t.Fatalf("Step %d: open failed: %s", i, err)
+				return
+			}
+			if izm.nOpenZips > nMaxOpenZips {
+				t.Errorf("Step %d: should be <=%d open zips", i, nMaxOpenZips)
+			}
+		}
+		if !managedZips[nInputZips-1].IsOpen() {
+			t.Error("The last input should stay open")
+		}
+		for _, iz := range managedZips {
+			iz.Close()
+		}
+		if izm.nOpenZips > 0 {
+			t.Error("Some input zips are still open")
+		}
+	})
+}
diff --git a/cmd/multiproduct_kati/Android.bp b/cmd/multiproduct_kati/Android.bp
index 13b3679..d34f8c3 100644
--- a/cmd/multiproduct_kati/Android.bp
+++ b/cmd/multiproduct_kati/Android.bp
@@ -24,4 +24,7 @@
     srcs: [
         "main.go",
     ],
+    testSrcs: [
+        "main_test.go",
+    ],
 }
diff --git a/cmd/multiproduct_kati/main.go b/cmd/multiproduct_kati/main.go
index 1171a65..7329aee 100644
--- a/cmd/multiproduct_kati/main.go
+++ b/cmd/multiproduct_kati/main.go
@@ -37,18 +37,7 @@
 	"android/soong/zip"
 )
 
-// We default to number of cpus / 4, which seems to be the sweet spot for my
-// system. I suspect this is mostly due to memory or disk bandwidth though, and
-// may depend on the size ofthe source tree, so this probably isn't a great
-// default.
-func detectNumJobs() int {
-	if runtime.NumCPU() < 4 {
-		return 1
-	}
-	return runtime.NumCPU() / 4
-}
-
-var numJobs = flag.Int("j", detectNumJobs(), "number of parallel kati jobs")
+var numJobs = flag.Int("j", 0, "number of parallel jobs [0=autodetect]")
 
 var keepArtifacts = flag.Bool("keep", false, "keep archives of artifacts")
 var incremental = flag.Bool("incremental", false, "run in incremental mode (saving intermediates)")
@@ -64,6 +53,9 @@
 var skipProducts = flag.String("skip-products", "", "comma-separated list of products to skip (known failures, etc)")
 var includeProducts = flag.String("products", "", "comma-separated list of products to build")
 
+var shardCount = flag.Int("shard-count", 1, "split the products into multiple shards (to spread the build onto multiple machines, etc)")
+var shard = flag.Int("shard", 1, "1-indexed shard to execute")
+
 const errorLeadingLines = 20
 const errorTrailingLines = 20
 
@@ -158,7 +150,7 @@
 func main() {
 	stdio := terminal.StdioImpl{}
 
-	output := terminal.NewStatusOutput(stdio.Stdout(), "",
+	output := terminal.NewStatusOutput(stdio.Stdout(), "", false,
 		build.OsEnvironment().IsEnvTrue("ANDROID_QUIET_BUILD"))
 
 	log := logger.New(output)
@@ -230,6 +222,21 @@
 		trace.SetOutput(filepath.Join(config.OutDir(), "build.trace"))
 	}
 
+	var jobs = *numJobs
+	if jobs < 1 {
+		jobs = runtime.NumCPU() / 4
+
+		ramGb := int(config.TotalRAM() / 1024 / 1024 / 1024)
+		if ramJobs := ramGb / 20; ramGb > 0 && jobs > ramJobs {
+			jobs = ramJobs
+		}
+
+		if jobs < 1 {
+			jobs = 1
+		}
+	}
+	log.Verbosef("Using %d parallel jobs", jobs)
+
 	setMaxFiles(log)
 
 	finder := build.NewSourceFinder(buildCtx, config)
@@ -278,6 +285,17 @@
 		}
 	}
 
+	if *shard < 1 {
+		log.Fatalf("--shard value must be >= 1, not %d\n", *shard)
+	} else if *shardCount < 1 {
+		log.Fatalf("--shard-count value must be >= 1, not %d\n", *shardCount)
+	} else if *shard > *shardCount {
+		log.Fatalf("--shard (%d) must not be greater than --shard-count (%d)\n", *shard,
+			*shardCount)
+	} else if *shardCount > 1 {
+		finalProductsList = splitList(finalProductsList, *shardCount)[*shard-1]
+	}
+
 	log.Verbose("Got product list: ", finalProductsList)
 
 	s := buildCtx.Status.StartTool()
@@ -304,7 +322,7 @@
 	}()
 
 	var wg sync.WaitGroup
-	for i := 0; i < *numJobs; i++ {
+	for i := 0; i < jobs; i++ {
 		wg.Add(1)
 		go func() {
 			defer wg.Done()
@@ -391,7 +409,7 @@
 		Thread:  mpctx.Tracer.NewThread(product),
 		Status:  &status.Status{},
 	}}
-	ctx.Status.AddOutput(terminal.NewStatusOutput(ctx.Writer, "",
+	ctx.Status.AddOutput(terminal.NewStatusOutput(ctx.Writer, "", false,
 		build.OsEnvironment().IsEnvTrue("ANDROID_QUIET_BUILD")))
 
 	config := build.NewConfig(ctx, flag.Args()...)
@@ -472,3 +490,18 @@
 	// discard writes
 	return len(p), nil
 }
+
+func splitList(list []string, shardCount int) (ret [][]string) {
+	each := len(list) / shardCount
+	extra := len(list) % shardCount
+	for i := 0; i < shardCount; i++ {
+		count := each
+		if extra > 0 {
+			count += 1
+			extra -= 1
+		}
+		ret = append(ret, list[:count])
+		list = list[count:]
+	}
+	return
+}
diff --git a/cmd/multiproduct_kati/main_test.go b/cmd/multiproduct_kati/main_test.go
new file mode 100644
index 0000000..263a124
--- /dev/null
+++ b/cmd/multiproduct_kati/main_test.go
@@ -0,0 +1,93 @@
+// 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 main
+
+import (
+	"fmt"
+	"reflect"
+	"testing"
+)
+
+func TestSplitList(t *testing.T) {
+	testcases := []struct {
+		inputCount int
+		shardCount int
+		want       [][]string
+	}{
+		{
+			inputCount: 1,
+			shardCount: 1,
+			want:       [][]string{{"1"}},
+		},
+		{
+			inputCount: 1,
+			shardCount: 2,
+			want:       [][]string{{"1"}, {}},
+		},
+		{
+			inputCount: 4,
+			shardCount: 2,
+			want:       [][]string{{"1", "2"}, {"3", "4"}},
+		},
+		{
+			inputCount: 19,
+			shardCount: 10,
+			want: [][]string{
+				{"1", "2"},
+				{"3", "4"},
+				{"5", "6"},
+				{"7", "8"},
+				{"9", "10"},
+				{"11", "12"},
+				{"13", "14"},
+				{"15", "16"},
+				{"17", "18"},
+				{"19"},
+			},
+		},
+		{
+			inputCount: 15,
+			shardCount: 10,
+			want: [][]string{
+				{"1", "2"},
+				{"3", "4"},
+				{"5", "6"},
+				{"7", "8"},
+				{"9", "10"},
+				{"11"},
+				{"12"},
+				{"13"},
+				{"14"},
+				{"15"},
+			},
+		},
+	}
+
+	for _, tc := range testcases {
+		t.Run(fmt.Sprintf("%d/%d", tc.inputCount, tc.shardCount), func(t *testing.T) {
+			input := []string{}
+			for i := 1; i <= tc.inputCount; i++ {
+				input = append(input, fmt.Sprintf("%d", i))
+			}
+
+			got := splitList(input, tc.shardCount)
+
+			if !reflect.DeepEqual(got, tc.want) {
+				t.Errorf("unexpected result for splitList([]string{...%d...}, %d):\nwant: %v\n got: %v\n",
+					tc.inputCount, tc.shardCount, tc.want, got)
+			}
+		})
+	}
+}
diff --git a/cmd/pom2bp/pom2bp.go b/cmd/pom2bp/pom2bp.go
index c858c40..191b919 100644
--- a/cmd/pom2bp/pom2bp.go
+++ b/cmd/pom2bp/pom2bp.go
@@ -124,6 +124,25 @@
 
 var hostModuleNames = HostModuleNames{}
 
+type HostAndDeviceModuleNames map[string]bool
+
+func (n HostAndDeviceModuleNames) IsHostAndDeviceModule(groupId string, artifactId string) bool {
+	_, found := n[groupId+":"+artifactId]
+
+	return found
+}
+
+func (n HostAndDeviceModuleNames) String() string {
+	return ""
+}
+
+func (n HostAndDeviceModuleNames) Set(v string) error {
+	n[v] = true
+	return nil
+}
+
+var hostAndDeviceModuleNames = HostAndDeviceModuleNames{}
+
 var sdkVersion string
 var useVersion string
 var staticDeps bool
@@ -190,10 +209,14 @@
 	return !p.IsHostModule()
 }
 
+func (p Pom) IsHostAndDeviceModule() bool {
+	return hostAndDeviceModuleNames.IsHostAndDeviceModule(p.GroupId, p.ArtifactId)
+}
+
 func (p Pom) ModuleType() string {
 	if p.IsAar() {
 		return "android_library"
-	} else if p.IsHostModule() {
+	} else if p.IsHostModule() && !p.IsHostAndDeviceModule() {
 		return "java_library_host"
 	} else {
 		return "java_library_static"
@@ -203,7 +226,7 @@
 func (p Pom) ImportModuleType() string {
 	if p.IsAar() {
 		return "android_library_import"
-	} else if p.IsHostModule() {
+	} else if p.IsHostModule() && !p.IsHostAndDeviceModule() {
 		return "java_import_host"
 	} else {
 		return "java_import"
@@ -340,6 +363,9 @@
     {{- if .Jetifier}}
     jetifier: true,
     {{- end}}
+    {{- if .IsHostAndDeviceModule}}
+    host_supported: true,
+    {{- end}}
     {{- if .IsAar}}
     min_sdk_version: "{{.MinSdkVersion}}",
     static_libs: [
@@ -372,6 +398,9 @@
     {{- if .Jetifier}}
     jetifier: true,
     {{- end}}
+    {{- if .IsHostAndDeviceModule}}
+    host_supported: true,
+    {{- end}}
     {{- if .IsAar}}
     min_sdk_version: "{{.MinSdkVersion}}",
     static_libs: [
@@ -399,6 +428,9 @@
     name: "{{.BpName}}",
     {{- if .IsDeviceModule}}
     sdk_version: "{{.SdkVersion}}",
+    {{- if .IsHostAndDeviceModule}}
+    host_supported: true,
+    {{- end}}
     {{- if .IsAar}}
     min_sdk_version: "{{.MinSdkVersion}}",
     manifest: "manifests/{{.BpName}}/AndroidManifest.xml",
@@ -564,6 +596,7 @@
 	flag.Var(&extraLibs, "extra-libs", "Extra runtime dependencies needed when depending on a module")
 	flag.Var(&rewriteNames, "rewrite", "Regex(es) to rewrite artifact names")
 	flag.Var(&hostModuleNames, "host", "Specifies that the corresponding module (specified in the form 'module.group:module.artifact') is a host module")
+	flag.Var(&hostAndDeviceModuleNames, "host-and-device", "Specifies that the corresponding module (specified in the form 'module.group:module.artifact') is both a host and device module.")
 	flag.StringVar(&sdkVersion, "sdk-version", "", "What to write to sdk_version")
 	flag.StringVar(&useVersion, "use-version", "", "Only read artifacts of a specific version")
 	flag.BoolVar(&staticDeps, "static-deps", false, "Statically include direct dependencies")
diff --git a/cmd/sbox/Android.bp b/cmd/sbox/Android.bp
index fe4c7bb..a706810d 100644
--- a/cmd/sbox/Android.bp
+++ b/cmd/sbox/Android.bp
@@ -14,6 +14,7 @@
 
 blueprint_go_binary {
     name: "sbox",
+    deps: ["soong-makedeps"],
     srcs: [
         "sbox.go",
     ],
diff --git a/cmd/sbox/sbox.go b/cmd/sbox/sbox.go
index 4ac9295..7057b33 100644
--- a/cmd/sbox/sbox.go
+++ b/cmd/sbox/sbox.go
@@ -15,6 +15,7 @@
 package main
 
 import (
+	"bytes"
 	"errors"
 	"flag"
 	"fmt"
@@ -25,6 +26,8 @@
 	"path/filepath"
 	"strings"
 	"time"
+
+	"android/soong/makedeps"
 )
 
 var (
@@ -152,9 +155,6 @@
 			return err
 		}
 		allOutputs = append(allOutputs, sandboxedDepfile)
-		if !strings.Contains(rawCommand, "__SBOX_DEPFILE__") {
-			return fmt.Errorf("the --depfile-out argument only makes sense if the command contains the text __SBOX_DEPFILE__")
-		}
 		rawCommand = strings.Replace(rawCommand, "__SBOX_DEPFILE__", filepath.Join(tempDir, sandboxedDepfile), -1)
 
 	}
@@ -281,6 +281,26 @@
 		}
 	}
 
+	// Rewrite the depfile so that it doesn't include the (randomized) sandbox directory
+	if depfileOut != "" {
+		in, err := ioutil.ReadFile(depfileOut)
+		if err != nil {
+			return err
+		}
+
+		deps, err := makedeps.Parse(depfileOut, bytes.NewBuffer(in))
+		if err != nil {
+			return err
+		}
+
+		deps.Output = "outputfile"
+
+		err = ioutil.WriteFile(depfileOut, deps.Print(), 0666)
+		if err != nil {
+			return err
+		}
+	}
+
 	// TODO(jeffrygaston) if a process creates more output files than it declares, should there be a warning?
 	return nil
 }
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 41c7d46..30381e0 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -18,7 +18,12 @@
 	"flag"
 	"fmt"
 	"os"
+	"os/exec"
 	"path/filepath"
+	"strconv"
+	"strings"
+	"syscall"
+	"time"
 
 	"github.com/google/blueprint/bootstrap"
 
@@ -50,6 +55,42 @@
 }
 
 func main() {
+	if android.SoongDelveListen != "" {
+		if android.SoongDelvePath == "" {
+			fmt.Fprintln(os.Stderr, "SOONG_DELVE is set but failed to find dlv")
+			os.Exit(1)
+		}
+		pid := strconv.Itoa(os.Getpid())
+		cmd := []string{android.SoongDelvePath,
+			"attach", pid,
+			"--headless",
+			"-l", android.SoongDelveListen,
+			"--api-version=2",
+			"--accept-multiclient",
+			"--log",
+		}
+
+		fmt.Println("Starting", strings.Join(cmd, " "))
+		dlv := exec.Command(cmd[0], cmd[1:]...)
+		dlv.Stdout = os.Stdout
+		dlv.Stderr = os.Stderr
+		dlv.Stdin = nil
+
+		// Put dlv into its own process group so we can kill it and the child process it starts.
+		dlv.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
+
+		err := dlv.Start()
+		if err != nil {
+			// Print the error starting dlv and continue.
+			fmt.Println(err)
+		} else {
+			// Kill the process group for dlv when soong_build exits.
+			defer syscall.Kill(-dlv.Process.Pid, syscall.SIGKILL)
+			// Wait to give dlv a chance to connect and pause the process.
+			time.Sleep(time.Second)
+		}
+	}
+
 	flag.Parse()
 
 	// The top-level Blueprints file is passed as the first argument.
@@ -72,7 +113,17 @@
 
 	ctx.SetAllowMissingDependencies(configuration.AllowMissingDependencies())
 
-	bootstrap.Main(ctx.Context, configuration, configuration.ConfigFileName, configuration.ProductVariablesFileName)
+	extraNinjaDeps := []string{configuration.ConfigFileName, configuration.ProductVariablesFileName}
+
+	// Read the SOONG_DELVE again through configuration so that there is a dependency on the environment variable
+	// and soong_build will rerun when it is set for the first time.
+	if listen := configuration.Getenv("SOONG_DELVE"); listen != "" {
+		// Add a non-existent file to the dependencies so that soong_build will rerun when the debugger is
+		// enabled even if it completed successfully.
+		extraNinjaDeps = append(extraNinjaDeps, filepath.Join(configuration.BuildDir(), "always_rerun_for_delve"))
+	}
+
+	bootstrap.Main(ctx.Context, configuration, extraNinjaDeps...)
 
 	if docFile != "" {
 		if err := writeDocs(ctx, docFile); err != nil {
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index b909779..974c644 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -41,6 +41,12 @@
 	// description for the flag (to display when running help)
 	description string
 
+	// Forces the status output into dumb terminal mode.
+	forceDumbOutput bool
+
+	// Sets a prefix string to use for filenames of log files.
+	logsPrefix string
+
 	// Creates the build configuration based on the args and build context.
 	config func(ctx build.Context, args ...string) build.Config
 
@@ -64,17 +70,21 @@
 		stdio: stdio,
 		run:   make,
 	}, {
-		flag:        "--dumpvar-mode",
-		description: "print the value of the legacy make variable VAR to stdout",
-		config:      dumpVarConfig,
-		stdio:       customStdio,
-		run:         dumpVar,
+		flag:            "--dumpvar-mode",
+		description:     "print the value of the legacy make variable VAR to stdout",
+		forceDumbOutput: true,
+		logsPrefix:      "dumpvars-",
+		config:          dumpVarConfig,
+		stdio:           customStdio,
+		run:             dumpVar,
 	}, {
-		flag:        "--dumpvars-mode",
-		description: "dump the values of one or more legacy make variables, in shell syntax",
-		config:      dumpVarConfig,
-		stdio:       customStdio,
-		run:         dumpVars,
+		flag:            "--dumpvars-mode",
+		description:     "dump the values of one or more legacy make variables, in shell syntax",
+		forceDumbOutput: true,
+		logsPrefix:      "dumpvars-",
+		config:          dumpVarConfig,
+		stdio:           customStdio,
+		run:             dumpVars,
 	}, {
 		flag:        "--build-mode",
 		description: "build modules based on the specified build action",
@@ -113,7 +123,7 @@
 		os.Exit(1)
 	}
 
-	output := terminal.NewStatusOutput(c.stdio().Stdout(), os.Getenv("NINJA_STATUS"),
+	output := terminal.NewStatusOutput(c.stdio().Stdout(), os.Getenv("NINJA_STATUS"), c.forceDumbOutput,
 		build.OsEnvironment().IsEnvTrue("ANDROID_QUIET_BUILD"))
 
 	log := logger.New(output)
@@ -157,12 +167,14 @@
 	}
 
 	os.MkdirAll(logsDir, 0777)
-	log.SetOutput(filepath.Join(logsDir, "soong.log"))
-	trace.SetOutput(filepath.Join(logsDir, "build.trace"))
-	stat.AddOutput(status.NewVerboseLog(log, filepath.Join(logsDir, "verbose.log")))
-	stat.AddOutput(status.NewErrorLog(log, filepath.Join(logsDir, "error.log")))
+	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.NewCriticalPath(log))
 
-	defer met.Dump(filepath.Join(logsDir, "soong_metrics"))
+	defer met.Dump(filepath.Join(logsDir, c.logsPrefix+"soong_metrics"))
 
 	if start, ok := os.LookupEnv("TRACE_BEGIN_SOONG"); ok {
 		if !strings.HasSuffix(start, "N") {
@@ -178,6 +190,11 @@
 		}
 	}
 
+	// Fix up the source tree due to a repo bug where it doesn't remove
+	// linkfiles that have been removed
+	fixBadDanglingLink(buildCtx, "hardware/qcom/sdm710/Android.bp")
+	fixBadDanglingLink(buildCtx, "hardware/qcom/sdm710/Android.mk")
+
 	f := build.NewSourceFinder(buildCtx, config)
 	defer f.Shutdown()
 	build.FindSources(buildCtx, config, f)
@@ -185,6 +202,20 @@
 	c.run(buildCtx, config, args, logsDir)
 }
 
+func fixBadDanglingLink(ctx build.Context, name string) {
+	_, err := os.Lstat(name)
+	if err != nil {
+		return
+	}
+	_, err = os.Stat(name)
+	if os.IsNotExist(err) {
+		err = os.Remove(name)
+		if err != nil {
+			ctx.Fatalf("Failed to remove dangling link %q: %v", name, err)
+		}
+	}
+}
+
 func dumpVar(ctx build.Context, config build.Config, args []string, _ string) {
 	flags := flag.NewFlagSet("dumpvar", flag.ExitOnError)
 	flags.Usage = func() {
@@ -329,36 +360,36 @@
 	}
 
 	buildActionFlags := []struct {
-		name              string
-		description       string
-		action            build.BuildAction
-		buildDependencies bool
-		set               bool
+		name        string
+		description string
+		action      build.BuildAction
+		set         bool
 	}{{
-		name:              "all-modules",
-		description:       "Build action: build from the top of the source tree.",
-		action:            build.BUILD_MODULES_IN_A_DIRECTORY,
-		buildDependencies: true,
+		name:        "all-modules",
+		description: "Build action: build from the top of the source tree.",
+		action:      build.BUILD_MODULES,
 	}, {
-		name:              "modules-in-a-dir-no-deps",
-		description:       "Build action: builds all of the modules in the current directory without their dependencies.",
-		action:            build.BUILD_MODULES_IN_A_DIRECTORY,
-		buildDependencies: false,
+		// This is redirecting to mma build command behaviour. Once it has soaked for a
+		// while, the build command is deleted from here once it has been removed from the
+		// envsetup.sh.
+		name:        "modules-in-a-dir-no-deps",
+		description: "Build action: builds all of the modules in the current directory without their dependencies.",
+		action:      build.BUILD_MODULES_IN_A_DIRECTORY,
 	}, {
-		name:              "modules-in-dirs-no-deps",
-		description:       "Build action: builds all of the modules in the supplied directories without their dependencies.",
-		action:            build.BUILD_MODULES_IN_DIRECTORIES,
-		buildDependencies: false,
+		// This is redirecting to mmma build command behaviour. Once it has soaked for a
+		// while, the build command is deleted from here once it has been removed from the
+		// envsetup.sh.
+		name:        "modules-in-dirs-no-deps",
+		description: "Build action: builds all of the modules in the supplied directories without their dependencies.",
+		action:      build.BUILD_MODULES_IN_DIRECTORIES,
 	}, {
-		name:              "modules-in-a-dir",
-		description:       "Build action: builds all of the modules in the current directory and their dependencies.",
-		action:            build.BUILD_MODULES_IN_A_DIRECTORY,
-		buildDependencies: true,
+		name:        "modules-in-a-dir",
+		description: "Build action: builds all of the modules in the current directory and their dependencies.",
+		action:      build.BUILD_MODULES_IN_A_DIRECTORY,
 	}, {
-		name:              "modules-in-dirs",
-		description:       "Build action: builds all of the modules in the supplied directories and their dependencies.",
-		action:            build.BUILD_MODULES_IN_DIRECTORIES,
-		buildDependencies: true,
+		name:        "modules-in-dirs",
+		description: "Build action: builds all of the modules in the supplied directories and their dependencies.",
+		action:      build.BUILD_MODULES_IN_DIRECTORIES,
 	}}
 	for i, flag := range buildActionFlags {
 		flags.BoolVar(&buildActionFlags[i].set, flag.name, false, flag.description)
@@ -378,12 +409,10 @@
 	// is specified.
 	buildActionCount := 0
 	var buildAction build.BuildAction
-	buildDependency := false
 	for _, flag := range buildActionFlags {
 		if flag.set {
 			buildActionCount++
 			buildAction = flag.action
-			buildDependency = flag.buildDependencies
 		}
 	}
 	if buildActionCount != 1 {
@@ -395,7 +424,7 @@
 
 	// Remove the build action flags from the args as they are not recognized by the config.
 	args = args[numBuildActionFlags:]
-	return build.NewBuildActionConfig(buildAction, *dir, buildDependency, ctx, args...)
+	return build.NewBuildActionConfig(buildAction, *dir, ctx, args...)
 }
 
 func make(ctx build.Context, config build.Config, _ []string, logsDir string) {
@@ -408,7 +437,22 @@
 		fmt.Fprintln(writer, "!")
 		fmt.Fprintln(writer, "! Older versions are saved in verbose.log.#.gz files")
 		fmt.Fprintln(writer, "")
-		time.Sleep(5 * time.Second)
+		select {
+		case <-time.After(5 * time.Second):
+		case <-ctx.Done():
+			return
+		}
+	}
+
+	if _, ok := config.Environment().Get("ONE_SHOT_MAKEFILE"); ok {
+		writer := ctx.Writer
+		fmt.Fprintln(writer, "! The variable `ONE_SHOT_MAKEFILE` is obsolete.")
+		fmt.Fprintln(writer, "!")
+		fmt.Fprintln(writer, "! If you're using `mm`, you'll need to run `source build/envsetup.sh` to update.")
+		fmt.Fprintln(writer, "!")
+		fmt.Fprintln(writer, "! Otherwise, either specify a module name with m, or use mma / MODULES-IN-...")
+		fmt.Fprintln(writer, "")
+		ctx.Fatal("done")
 	}
 
 	toBuild := build.BuildAll
diff --git a/cuj/Android.bp b/cuj/Android.bp
new file mode 100644
index 0000000..21d667f
--- /dev/null
+++ b/cuj/Android.bp
@@ -0,0 +1,12 @@
+blueprint_go_binary {
+    name: "cuj_tests",
+    deps: [
+        "soong-ui-build",
+        "soong-ui-logger",
+        "soong-ui-terminal",
+        "soong-ui-tracer",
+    ],
+    srcs: [
+        "cuj.go",
+    ],
+}
diff --git a/cuj/cuj.go b/cuj/cuj.go
new file mode 100644
index 0000000..c7ff8ff
--- /dev/null
+++ b/cuj/cuj.go
@@ -0,0 +1,190 @@
+// 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.
+
+// This executable runs a series of build commands to test and benchmark some critical user journeys.
+package main
+
+import (
+	"context"
+	"fmt"
+	"os"
+	"path/filepath"
+	"strconv"
+	"strings"
+	"time"
+
+	"android/soong/ui/build"
+	"android/soong/ui/logger"
+	"android/soong/ui/metrics"
+	"android/soong/ui/status"
+	"android/soong/ui/terminal"
+	"android/soong/ui/tracer"
+)
+
+type Test struct {
+	name string
+	args []string
+
+	results TestResults
+}
+
+type TestResults struct {
+	metrics *metrics.Metrics
+	err     error
+}
+
+// Run runs a single build command.  It emulates the "m" command line by calling into Soong UI directly.
+func (t *Test) Run(logsDir string) {
+	output := terminal.NewStatusOutput(os.Stdout, "", false, false)
+
+	log := logger.New(output)
+	defer log.Cleanup()
+
+	ctx, cancel := context.WithCancel(context.Background())
+	defer cancel()
+
+	trace := tracer.New(log)
+	defer trace.Close()
+
+	met := metrics.New()
+
+	stat := &status.Status{}
+	defer stat.Finish()
+	stat.AddOutput(output)
+	stat.AddOutput(trace.StatusTracer())
+
+	build.SetupSignals(log, cancel, func() {
+		trace.Close()
+		log.Cleanup()
+		stat.Finish()
+	})
+
+	buildCtx := build.Context{ContextImpl: &build.ContextImpl{
+		Context: ctx,
+		Logger:  log,
+		Metrics: met,
+		Tracer:  trace,
+		Writer:  output,
+		Status:  stat,
+	}}
+
+	defer logger.Recover(func(err error) {
+		t.results.err = err
+	})
+
+	config := build.NewConfig(buildCtx, t.args...)
+	build.SetupOutDir(buildCtx, config)
+
+	os.MkdirAll(logsDir, 0777)
+	log.SetOutput(filepath.Join(logsDir, "soong.log"))
+	trace.SetOutput(filepath.Join(logsDir, "build.trace"))
+	stat.AddOutput(status.NewVerboseLog(log, filepath.Join(logsDir, "verbose.log")))
+	stat.AddOutput(status.NewErrorLog(log, filepath.Join(logsDir, "error.log")))
+	stat.AddOutput(status.NewProtoErrorLog(log, filepath.Join(logsDir, "build_error")))
+	stat.AddOutput(status.NewCriticalPath(log))
+
+	defer met.Dump(filepath.Join(logsDir, "soong_metrics"))
+
+	if start, ok := os.LookupEnv("TRACE_BEGIN_SOONG"); ok {
+		if !strings.HasSuffix(start, "N") {
+			if start_time, err := strconv.ParseUint(start, 10, 64); err == nil {
+				log.Verbosef("Took %dms to start up.",
+					time.Since(time.Unix(0, int64(start_time))).Nanoseconds()/time.Millisecond.Nanoseconds())
+				buildCtx.CompleteTrace(metrics.RunSetupTool, "startup", start_time, uint64(time.Now().UnixNano()))
+			}
+		}
+
+		if executable, err := os.Executable(); err == nil {
+			trace.ImportMicrofactoryLog(filepath.Join(filepath.Dir(executable), "."+filepath.Base(executable)+".trace"))
+		}
+	}
+
+	f := build.NewSourceFinder(buildCtx, config)
+	defer f.Shutdown()
+	build.FindSources(buildCtx, config, f)
+
+	build.Build(buildCtx, config, build.BuildAll)
+
+	t.results.metrics = met
+}
+
+func main() {
+	outDir := os.Getenv("OUT_DIR")
+	if outDir == "" {
+		outDir = "out"
+	}
+
+	cujDir := filepath.Join(outDir, "cuj_tests")
+
+	// Use a subdirectory for the out directory for the tests to keep them isolated.
+	os.Setenv("OUT_DIR", filepath.Join(cujDir, "out"))
+
+	// Each of these tests is run in sequence without resetting the output tree.  The state of the output tree will
+	// affect each successive test.  To maintain the validity of the benchmarks across changes, care must be taken
+	// to avoid changing the state of the tree when a test is run.  This is most easily accomplished by adding tests
+	// at the end.
+	tests := []Test{
+		{
+			// Reset the out directory to get reproducible results.
+			name: "clean",
+			args: []string{"clean"},
+		},
+		{
+			// Parse the build files.
+			name: "nothing",
+			args: []string{"nothing"},
+		},
+		{
+			// Parse the build files again to monitor issues like globs rerunning.
+			name: "nothing_rebuild",
+			args: []string{"nothing"},
+		},
+		{
+			// Parse the build files again, this should always be very short.
+			name: "nothing_rebuild_twice",
+			args: []string{"nothing"},
+		},
+		{
+			// Build the framework as a common developer task and one that keeps getting longer.
+			name: "framework",
+			args: []string{"framework"},
+		},
+		{
+			// Build the framework again to make sure it doesn't rebuild anything.
+			name: "framework_rebuild",
+			args: []string{"framework"},
+		},
+		{
+			// Build the framework again to make sure it doesn't rebuild anything even if it did the second time.
+			name: "framework_rebuild_twice",
+			args: []string{"framework"},
+		},
+	}
+
+	cujMetrics := metrics.NewCriticalUserJourneysMetrics()
+	defer cujMetrics.Dump(filepath.Join(cujDir, "logs", "cuj_metrics.pb"))
+
+	for i, t := range tests {
+		logsSubDir := fmt.Sprintf("%02d_%s", i, t.name)
+		logsDir := filepath.Join(cujDir, "logs", logsSubDir)
+		t.Run(logsDir)
+		if t.results.err != nil {
+			fmt.Printf("error running test %q: %s\n", t.name, t.results.err)
+			break
+		}
+		if t.results.metrics != nil {
+			cujMetrics.Add(t.name, t.results.metrics)
+		}
+	}
+}
diff --git a/cuj/run_cuj_tests.sh b/cuj/run_cuj_tests.sh
new file mode 100755
index 0000000..b4f9f88
--- /dev/null
+++ b/cuj/run_cuj_tests.sh
@@ -0,0 +1,29 @@
+#!/bin/bash -e
+
+readonly UNAME="$(uname)"
+case "$UNAME" in
+Linux)
+    readonly OS='linux'
+    ;;
+Darwin)
+    readonly OS='darwin'
+    ;;
+*)
+    echo "Unsupported OS '$UNAME'"
+    exit 1
+    ;;
+esac
+
+readonly ANDROID_TOP="$(cd $(dirname $0)/../../..; pwd)"
+cd "$ANDROID_TOP"
+
+export OUT_DIR="${OUT_DIR:-out}"
+readonly SOONG_OUT="${OUT_DIR}/soong"
+
+build/soong/soong_ui.bash --make-mode "${SOONG_OUT}/host/${OS}-x86/bin/cuj_tests"
+
+"${SOONG_OUT}/host/${OS}-x86/bin/cuj_tests" || true
+
+if [ -n "${DIST_DIR}" ]; then
+  cp -r "${OUT_DIR}/cuj_tests/logs" "${DIST_DIR}"
+fi
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
index a2f1af4..2a929c5 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -16,16 +16,15 @@
 
 import (
 	"encoding/json"
-	"io/ioutil"
 	"strings"
 
 	"android/soong/android"
 )
 
-// GlobalConfig stores the configuration for dex preopting set by the product
+// GlobalConfig stores the configuration for dex preopting. The fields are set
+// from product variables via dex_preopt_config.mk, except for SoongConfig
+// which come from CreateGlobalSoongConfig.
 type GlobalConfig struct {
-	DefaultNoStripping bool // don't strip dex files by default
-
 	DisablePreopt        bool     // disable preopt for all modules
 	DisablePreoptModules []string // modules with preopt disabled by product-specific config
 
@@ -40,23 +39,22 @@
 	DisableGenerateProfile bool   // don't generate profiles
 	ProfileDir             string // directory to find profiles in
 
-	BootJars []string // modules for jars that form the boot class path
+	BootJars          []string // modules for jars that form the boot class path
+	UpdatableBootJars []string // jars within apex that form the boot class path
 
-	RuntimeApexJars               []string // modules for jars that are in the runtime apex
-	ProductUpdatableBootModules   []string
-	ProductUpdatableBootLocations []string
+	ArtApexJars []string // modules for jars that are in the ART APEX
 
-	SystemServerJars []string // jars that form the system server
-	SystemServerApps []string // apps that are loaded into system server
-	SpeedApps        []string // apps that should be speed optimized
+	SystemServerJars          []string // jars that form the system server
+	SystemServerApps          []string // apps that are loaded into system server
+	UpdatableSystemServerJars []string // jars within apex that are loaded into system server
+	SpeedApps                 []string // apps that should be speed optimized
 
 	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
 	SystemServerCompilerFilter string // default compiler filter to pass to dex2oat for system server jars
 
-	GenerateDMFiles     bool // generate Dex Metadata files
-	NeverAllowStripping bool // whether stripping should not be done - used as build time check to make sure dex files are always available
+	GenerateDMFiles bool // generate Dex Metadata files
 
 	NoDebugInfo                 bool // don't generate debug info by default
 	DontResolveStartupStrings   bool // don't resolve string literals loaded during application startup.
@@ -79,27 +77,25 @@
 	InstructionSetFeatures map[android.ArchType]string // instruction set for each architecture
 
 	// Only used for boot image
-	DirtyImageObjects      android.OptionalPath // path to a dirty-image-objects file
-	PreloadedClasses       android.OptionalPath // path to a preloaded-classes file
-	BootImageProfiles      android.Paths        // path to a boot-image-profile.txt file
-	UseProfileForBootImage bool                 // whether a profile should be used to compile the boot image
-	BootFlags              string               // extra flags to pass to dex2oat for the boot image
-	Dex2oatImageXmx        string               // max heap size for dex2oat for the boot image
-	Dex2oatImageXms        string               // initial heap size for dex2oat for the boot image
+	DirtyImageObjects android.OptionalPath // path to a dirty-image-objects file
+	BootImageProfiles android.Paths        // path to a boot-image-profile.txt file
+	BootFlags         string               // extra flags to pass to dex2oat for the boot image
+	Dex2oatImageXmx   string               // max heap size for dex2oat for the boot image
+	Dex2oatImageXms   string               // initial heap size for dex2oat for the boot image
 
-	Tools Tools // paths to tools possibly used by the generated commands
+	SoongConfig GlobalSoongConfig // settings read from dexpreopt_soong.config
 }
 
-// Tools contains paths to tools possibly used by the generated commands.  If you add a new tool here you MUST add it
-// to the order-only dependency list in DEXPREOPT_GEN_DEPS.
-type Tools struct {
-	Profman       android.Path
-	Dex2oat       android.Path
-	Aapt          android.Path
-	SoongZip      android.Path
-	Zip2zip       android.Path
-	ManifestCheck android.Path
-
+// GlobalSoongConfig contains the global config that is generated from Soong,
+// stored in dexpreopt_soong.config.
+type GlobalSoongConfig struct {
+	// Paths to tools possibly used by the generated commands.
+	Profman          android.Path
+	Dex2oat          android.Path
+	Aapt             android.Path
+	SoongZip         android.Path
+	Zip2zip          android.Path
+	ManifestCheck    android.Path
 	ConstructContext android.Path
 }
 
@@ -115,15 +111,17 @@
 
 	ProfileClassListing  android.OptionalPath
 	ProfileIsTextListing bool
+	ProfileBootListing   android.OptionalPath
 
 	EnforceUsesLibraries         bool
 	PresentOptionalUsesLibraries []string
 	UsesLibraries                []string
 	LibraryPaths                 map[string]android.Path
 
-	Archs               []android.ArchType
-	DexPreoptImages     []android.Path
-	DexPreoptImagesDeps []android.Paths
+	Archs                   []android.ArchType
+	DexPreoptImages         []android.Path
+	DexPreoptImagesDeps     []android.OutputPaths
+	DexPreoptImageLocations []string
 
 	PreoptBootClassPathDexFiles     android.Paths // file paths of boot class path files
 	PreoptBootClassPathDexLocations []string      // virtual locations of boot class path files
@@ -134,10 +132,17 @@
 	ForceCreateAppImage bool
 
 	PresignedPrebuilt bool
+}
 
-	NoStripping     bool
-	StripInputPath  android.Path
-	StripOutputPath android.WritablePath
+type globalSoongConfigSingleton struct{}
+
+var pctx = android.NewPackageContext("android/soong/dexpreopt")
+
+func init() {
+	pctx.Import("android/soong/android")
+	android.RegisterSingletonType("dexpreopt-soong-config", func() android.Singleton {
+		return &globalSoongConfigSingleton{}
+	})
 }
 
 func constructPath(ctx android.PathContext, path string) android.Path {
@@ -174,56 +179,42 @@
 	return constructPath(ctx, path).(android.WritablePath)
 }
 
-// LoadGlobalConfig reads the global dexpreopt.config file into a GlobalConfig struct.  It is used directly in Soong
-// and in dexpreopt_gen called from Make to read the $OUT/dexpreopt.config written by Make.
-func LoadGlobalConfig(ctx android.PathContext, path string) (GlobalConfig, []byte, error) {
+// LoadGlobalConfig reads the global dexpreopt.config file into a GlobalConfig
+// struct, except the SoongConfig field which is set from the provided
+// soongConfig argument. LoadGlobalConfig is used directly in Soong and in
+// dexpreopt_gen called from Make to read the $OUT/dexpreopt.config written by
+// Make.
+func LoadGlobalConfig(ctx android.PathContext, data []byte, soongConfig GlobalSoongConfig) (GlobalConfig, error) {
 	type GlobalJSONConfig struct {
 		GlobalConfig
 
 		// Copies of entries in GlobalConfig that are not constructable without extra parameters.  They will be
 		// used to construct the real value manually below.
 		DirtyImageObjects string
-		PreloadedClasses  string
 		BootImageProfiles []string
-
-		Tools struct {
-			Profman       string
-			Dex2oat       string
-			Aapt          string
-			SoongZip      string
-			Zip2zip       string
-			ManifestCheck string
-
-			ConstructContext string
-		}
 	}
 
 	config := GlobalJSONConfig{}
-	data, err := loadConfig(ctx, path, &config)
+	err := json.Unmarshal(data, &config)
 	if err != nil {
-		return config.GlobalConfig, nil, err
+		return config.GlobalConfig, err
 	}
 
 	// Construct paths that require a PathContext.
 	config.GlobalConfig.DirtyImageObjects = android.OptionalPathForPath(constructPath(ctx, config.DirtyImageObjects))
-	config.GlobalConfig.PreloadedClasses = android.OptionalPathForPath(constructPath(ctx, config.PreloadedClasses))
 	config.GlobalConfig.BootImageProfiles = constructPaths(ctx, config.BootImageProfiles)
 
-	config.GlobalConfig.Tools.Profman = constructPath(ctx, config.Tools.Profman)
-	config.GlobalConfig.Tools.Dex2oat = constructPath(ctx, config.Tools.Dex2oat)
-	config.GlobalConfig.Tools.Aapt = constructPath(ctx, config.Tools.Aapt)
-	config.GlobalConfig.Tools.SoongZip = constructPath(ctx, config.Tools.SoongZip)
-	config.GlobalConfig.Tools.Zip2zip = constructPath(ctx, config.Tools.Zip2zip)
-	config.GlobalConfig.Tools.ManifestCheck = constructPath(ctx, config.Tools.ManifestCheck)
-	config.GlobalConfig.Tools.ConstructContext = constructPath(ctx, config.Tools.ConstructContext)
+	// Set this here to force the caller to provide a value for this struct (from
+	// either CreateGlobalSoongConfig or LoadGlobalSoongConfig).
+	config.GlobalConfig.SoongConfig = soongConfig
 
-	return config.GlobalConfig, data, nil
+	return config.GlobalConfig, nil
 }
 
 // LoadModuleConfig reads a per-module dexpreopt.config file into a ModuleConfig struct.  It is not used in Soong, which
 // receives a ModuleConfig struct directly from java/dexpreopt.go.  It is used in dexpreopt_gen called from oMake to
 // read the module dexpreopt.config written by Make.
-func LoadModuleConfig(ctx android.PathContext, path string) (ModuleConfig, error) {
+func LoadModuleConfig(ctx android.PathContext, data []byte) (ModuleConfig, error) {
 	type ModuleJSONConfig struct {
 		ModuleConfig
 
@@ -235,14 +226,13 @@
 		ProfileClassListing         string
 		LibraryPaths                map[string]string
 		DexPreoptImages             []string
+		DexPreoptImageLocations     []string
 		PreoptBootClassPathDexFiles []string
-		StripInputPath              string
-		StripOutputPath             string
 	}
 
 	config := ModuleJSONConfig{}
 
-	_, err := loadConfig(ctx, path, &config)
+	err := json.Unmarshal(data, &config)
 	if err != nil {
 		return config.ModuleConfig, err
 	}
@@ -254,39 +244,115 @@
 	config.ModuleConfig.ProfileClassListing = android.OptionalPathForPath(constructPath(ctx, config.ProfileClassListing))
 	config.ModuleConfig.LibraryPaths = constructPathMap(ctx, config.LibraryPaths)
 	config.ModuleConfig.DexPreoptImages = constructPaths(ctx, config.DexPreoptImages)
+	config.ModuleConfig.DexPreoptImageLocations = config.DexPreoptImageLocations
 	config.ModuleConfig.PreoptBootClassPathDexFiles = constructPaths(ctx, config.PreoptBootClassPathDexFiles)
-	config.ModuleConfig.StripInputPath = constructPath(ctx, config.StripInputPath)
-	config.ModuleConfig.StripOutputPath = constructWritablePath(ctx, config.StripOutputPath)
 
 	// This needs to exist, but dependencies are already handled in Make, so we don't need to pass them through JSON.
-	config.ModuleConfig.DexPreoptImagesDeps = make([]android.Paths, len(config.ModuleConfig.DexPreoptImages))
+	config.ModuleConfig.DexPreoptImagesDeps = make([]android.OutputPaths, len(config.ModuleConfig.DexPreoptImages))
 
 	return config.ModuleConfig, nil
 }
 
-func loadConfig(ctx android.PathContext, path string, config interface{}) ([]byte, error) {
-	r, err := ctx.Fs().Open(path)
-	if err != nil {
-		return nil, err
-	}
-	defer r.Close()
-
-	data, err := ioutil.ReadAll(r)
-	if err != nil {
-		return nil, err
+// CreateGlobalSoongConfig creates a GlobalSoongConfig from the current context.
+// Should not be used in dexpreopt_gen.
+func CreateGlobalSoongConfig(ctx android.PathContext) GlobalSoongConfig {
+	// Default to debug version to help find bugs.
+	// Set USE_DEX2OAT_DEBUG to false for only building non-debug versions.
+	var dex2oatBinary string
+	if ctx.Config().Getenv("USE_DEX2OAT_DEBUG") == "false" {
+		dex2oatBinary = "dex2oat"
+	} else {
+		dex2oatBinary = "dex2oatd"
 	}
 
-	err = json.Unmarshal(data, config)
+	return GlobalSoongConfig{
+		Profman:          ctx.Config().HostToolPath(ctx, "profman"),
+		Dex2oat:          ctx.Config().HostToolPath(ctx, dex2oatBinary),
+		Aapt:             ctx.Config().HostToolPath(ctx, "aapt"),
+		SoongZip:         ctx.Config().HostToolPath(ctx, "soong_zip"),
+		Zip2zip:          ctx.Config().HostToolPath(ctx, "zip2zip"),
+		ManifestCheck:    ctx.Config().HostToolPath(ctx, "manifest_check"),
+		ConstructContext: android.PathForSource(ctx, "build/make/core/construct_context.sh"),
+	}
+}
+
+type globalJsonSoongConfig struct {
+	Profman          string
+	Dex2oat          string
+	Aapt             string
+	SoongZip         string
+	Zip2zip          string
+	ManifestCheck    string
+	ConstructContext string
+}
+
+// LoadGlobalSoongConfig reads the dexpreopt_soong.config file into a
+// GlobalSoongConfig struct. It is only used in dexpreopt_gen.
+func LoadGlobalSoongConfig(ctx android.PathContext, data []byte) (GlobalSoongConfig, error) {
+	var jc globalJsonSoongConfig
+
+	err := json.Unmarshal(data, &jc)
 	if err != nil {
-		return nil, err
+		return GlobalSoongConfig{}, err
 	}
 
-	return data, nil
+	config := GlobalSoongConfig{
+		Profman:          constructPath(ctx, jc.Profman),
+		Dex2oat:          constructPath(ctx, jc.Dex2oat),
+		Aapt:             constructPath(ctx, jc.Aapt),
+		SoongZip:         constructPath(ctx, jc.SoongZip),
+		Zip2zip:          constructPath(ctx, jc.Zip2zip),
+		ManifestCheck:    constructPath(ctx, jc.ManifestCheck),
+		ConstructContext: constructPath(ctx, jc.ConstructContext),
+	}
+
+	return config, nil
+}
+
+func (s *globalSoongConfigSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+	config := CreateGlobalSoongConfig(ctx)
+	jc := globalJsonSoongConfig{
+		Profman:          config.Profman.String(),
+		Dex2oat:          config.Dex2oat.String(),
+		Aapt:             config.Aapt.String(),
+		SoongZip:         config.SoongZip.String(),
+		Zip2zip:          config.Zip2zip.String(),
+		ManifestCheck:    config.ManifestCheck.String(),
+		ConstructContext: config.ConstructContext.String(),
+	}
+
+	data, err := json.Marshal(jc)
+	if err != nil {
+		ctx.Errorf("failed to JSON marshal GlobalSoongConfig: %v", err)
+		return
+	}
+
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   android.WriteFile,
+		Output: android.PathForOutput(ctx, "dexpreopt_soong.config"),
+		Args: map[string]string{
+			"content": string(data),
+		},
+	})
+}
+
+func (s *globalSoongConfigSingleton) MakeVars(ctx android.MakeVarsContext) {
+	config := CreateGlobalSoongConfig(ctx)
+
+	ctx.Strict("DEX2OAT", config.Dex2oat.String())
+	ctx.Strict("DEXPREOPT_GEN_DEPS", strings.Join([]string{
+		config.Profman.String(),
+		config.Dex2oat.String(),
+		config.Aapt.String(),
+		config.SoongZip.String(),
+		config.Zip2zip.String(),
+		config.ManifestCheck.String(),
+		config.ConstructContext.String(),
+	}, " "))
 }
 
 func GlobalConfigForTests(ctx android.PathContext) GlobalConfig {
 	return GlobalConfig{
-		DefaultNoStripping:                 false,
 		DisablePreopt:                      false,
 		DisablePreoptModules:               nil,
 		OnlyPreoptBootImageAndSystemServer: false,
@@ -295,17 +361,16 @@
 		DisableGenerateProfile:             false,
 		ProfileDir:                         "",
 		BootJars:                           nil,
-		RuntimeApexJars:                    nil,
-		ProductUpdatableBootModules:        nil,
-		ProductUpdatableBootLocations:      nil,
+		UpdatableBootJars:                  nil,
+		ArtApexJars:                        nil,
 		SystemServerJars:                   nil,
 		SystemServerApps:                   nil,
+		UpdatableSystemServerJars:          nil,
 		SpeedApps:                          nil,
 		PreoptFlags:                        nil,
 		DefaultCompilerFilter:              "",
 		SystemServerCompilerFilter:         "",
 		GenerateDMFiles:                    false,
-		NeverAllowStripping:                false,
 		NoDebugInfo:                        false,
 		DontResolveStartupStrings:          false,
 		AlwaysSystemServerDebugInfo:        false,
@@ -321,13 +386,11 @@
 		CpuVariant:                         nil,
 		InstructionSetFeatures:             nil,
 		DirtyImageObjects:                  android.OptionalPath{},
-		PreloadedClasses:                   android.OptionalPath{},
 		BootImageProfiles:                  nil,
-		UseProfileForBootImage:             false,
 		BootFlags:                          "",
 		Dex2oatImageXmx:                    "",
 		Dex2oatImageXms:                    "",
-		Tools: Tools{
+		SoongConfig: GlobalSoongConfig{
 			Profman:          android.PathForTesting("profman"),
 			Dex2oat:          android.PathForTesting("dex2oat"),
 			Aapt:             android.PathForTesting("aapt"),
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index e02e60f..ac5b691 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -13,7 +13,7 @@
 // limitations under the License.
 
 // The dexpreopt package converts a global dexpreopt config and a module dexpreopt config into rules to perform
-// dexpreopting and to strip the dex files from the APK or JAR.
+// dexpreopting.
 //
 // It is used in two places; in the dexpeopt_gen binary for modules defined in Make, and directly linked into Soong.
 //
@@ -22,8 +22,7 @@
 // changed.  One script takes an APK or JAR as an input and produces a zip file containing any outputs of preopting,
 // in the location they should be on the device.  The Make build rules will unzip the zip file into $(PRODUCT_OUT) when
 // installing the APK, which will install the preopt outputs into $(PRODUCT_OUT)/system or $(PRODUCT_OUT)/system_other
-// as necessary.  The zip file may be empty if preopting was disabled for any reason.  The second script takes an APK or
-// JAR as an input and strips the dex files in it as necessary.
+// as necessary.  The zip file may be empty if preopting was disabled for any reason.
 //
 // The intermediate shell scripts allow changes to this package or to the global config to regenerate the shell scripts
 // but only require re-executing preopting if the script has changed.
@@ -48,45 +47,6 @@
 const SystemPartition = "/system/"
 const SystemOtherPartition = "/system_other/"
 
-// GenerateStripRule generates a set of commands that will take an APK or JAR as an input and strip the dex files if
-// they are no longer necessary after preopting.
-func GenerateStripRule(global GlobalConfig, module ModuleConfig) (rule *android.RuleBuilder, err error) {
-	defer func() {
-		if r := recover(); r != nil {
-			if _, ok := r.(runtime.Error); ok {
-				panic(r)
-			} else if e, ok := r.(error); ok {
-				err = e
-				rule = nil
-			} else {
-				panic(r)
-			}
-		}
-	}()
-
-	tools := global.Tools
-
-	rule = android.NewRuleBuilder()
-
-	strip := shouldStripDex(module, global)
-
-	if strip {
-		if global.NeverAllowStripping {
-			panic(fmt.Errorf("Stripping requested on %q, though the product does not allow it", module.DexLocation))
-		}
-		// Only strips if the dex files are not already uncompressed
-		rule.Command().
-			Textf(`if (zipinfo %s '*.dex' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then`, module.StripInputPath).
-			Tool(tools.Zip2zip).FlagWithInput("-i ", module.StripInputPath).FlagWithOutput("-o ", module.StripOutputPath).
-			FlagWithArg("-x ", `"classes*.dex"`).
-			Textf(`; else cp -f %s %s; fi`, module.StripInputPath, module.StripOutputPath)
-	} else {
-		rule.Command().Text("cp -f").Input(module.StripInputPath).Output(module.StripOutputPath)
-	}
-
-	return rule, nil
-}
-
 // GenerateDexpreoptRule generates a set of commands that will preopt a module based on a GlobalConfig and a
 // ModuleConfig.  The produced files and their install locations will be available through rule.Installs().
 func GenerateDexpreoptRule(ctx android.PathContext,
@@ -108,25 +68,26 @@
 	rule = android.NewRuleBuilder()
 
 	generateProfile := module.ProfileClassListing.Valid() && !global.DisableGenerateProfile
+	generateBootProfile := module.ProfileBootListing.Valid() && !global.DisableGenerateProfile
 
 	var profile android.WritablePath
 	if generateProfile {
 		profile = profileCommand(ctx, global, module, rule)
 	}
+	if generateBootProfile {
+		bootProfileCommand(ctx, global, module, rule)
+	}
 
 	if !dexpreoptDisabled(global, module) {
 		// Don't preopt individual boot jars, they will be preopted together.
-		// This check is outside dexpreoptDisabled because they still need to be stripped.
 		if !contains(global.BootJars, module.Name) {
 			appImage := (generateProfile || module.ForceCreateAppImage || global.DefaultAppImages) &&
 				!module.NoCreateAppImage
 
 			generateDM := shouldGenerateDM(module, global)
 
-			for i, arch := range module.Archs {
-				image := module.DexPreoptImages[i]
-				imageDeps := module.DexPreoptImagesDeps[i]
-				dexpreoptCommand(ctx, global, module, rule, arch, profile, image, imageDeps, appImage, generateDM)
+			for archIdx, _ := range module.Archs {
+				dexpreoptCommand(ctx, global, module, rule, archIdx, profile, appImage, generateDM)
 			}
 		}
 	}
@@ -139,6 +100,13 @@
 		return true
 	}
 
+	// Don't preopt system server jars that are updatable.
+	for _, p := range global.UpdatableSystemServerJars {
+		if _, jar := SplitApexJarPair(p); jar == module.Name {
+			return true
+		}
+	}
+
 	// If OnlyPreoptBootImageAndSystemServer=true and module is not in boot class path skip
 	// Also preopt system server jars since selinux prevents system server from loading anything from
 	// /data. If we don't do this they will need to be extracted which is not favorable for RAM usage
@@ -163,7 +131,7 @@
 
 	cmd := rule.Command().
 		Text(`ANDROID_LOG_TAGS="*:e"`).
-		Tool(global.Tools.Profman)
+		Tool(global.SoongConfig.Profman)
 
 	if module.ProfileIsTextListing {
 		// The profile is a test listing of classes (used for framework jars).
@@ -190,8 +158,42 @@
 	return profilePath
 }
 
+func bootProfileCommand(ctx android.PathContext, global GlobalConfig, module ModuleConfig,
+	rule *android.RuleBuilder) android.WritablePath {
+
+	profilePath := module.BuildPath.InSameDir(ctx, "profile.bprof")
+	profileInstalledPath := module.DexLocation + ".bprof"
+
+	if !module.ProfileIsTextListing {
+		rule.Command().FlagWithOutput("touch ", profilePath)
+	}
+
+	cmd := rule.Command().
+		Text(`ANDROID_LOG_TAGS="*:e"`).
+		Tool(global.SoongConfig.Profman)
+
+	// The profile is a test listing of methods.
+	// We need to generate the actual binary profile.
+	cmd.FlagWithInput("--create-profile-from=", module.ProfileBootListing.Path())
+
+	cmd.
+		Flag("--generate-boot-profile").
+		FlagWithInput("--apk=", module.DexPath).
+		Flag("--dex-location="+module.DexLocation).
+		FlagWithOutput("--reference-profile-file=", profilePath)
+
+	if !module.ProfileIsTextListing {
+		cmd.Text(fmt.Sprintf(`|| echo "Profile out of date for %s"`, module.DexPath))
+	}
+	rule.Install(profilePath, profileInstalledPath)
+
+	return profilePath
+}
+
 func dexpreoptCommand(ctx android.PathContext, global GlobalConfig, module ModuleConfig, rule *android.RuleBuilder,
-	arch android.ArchType, profile, bootImage android.Path, bootImageDeps android.Paths, appImage, generateDM bool) {
+	archIdx int, profile android.WritablePath, appImage bool, generateDM bool) {
+
+	arch := module.Archs[archIdx]
 
 	// HACK: make soname in Soong-generated .odex files match Make.
 	base := filepath.Base(module.DexLocation)
@@ -212,7 +214,7 @@
 	odexPath := module.BuildPath.InSameDir(ctx, "oat", arch.String(), pathtools.ReplaceExtension(base, "odex"))
 	odexInstallPath := toOdexPath(module.DexLocation)
 	if odexOnSystemOther(module, global) {
-		odexInstallPath = strings.Replace(odexInstallPath, SystemPartition, SystemOtherPartition, 1)
+		odexInstallPath = filepath.Join(SystemOtherPartition, odexInstallPath)
 	}
 
 	vdexPath := odexPath.ReplaceExtension(ctx, "vdex")
@@ -220,13 +222,6 @@
 
 	invocationPath := odexPath.ReplaceExtension(ctx, "invocation")
 
-	// bootImage is .../dex_bootjars/system/framework/arm64/boot.art, but dex2oat wants
-	// .../dex_bootjars/system/framework/boot.art on the command line
-	var bootImageLocation string
-	if bootImage != nil {
-		bootImageLocation = PathToLocation(bootImage, arch)
-	}
-
 	// The class loader context using paths in the build
 	var classLoaderContextHost android.Paths
 
@@ -304,14 +299,14 @@
 	if module.EnforceUsesLibraries {
 		if module.ManifestPath != nil {
 			rule.Command().Text(`target_sdk_version="$(`).
-				Tool(global.Tools.ManifestCheck).
+				Tool(global.SoongConfig.ManifestCheck).
 				Flag("--extract-target-sdk-version").
 				Input(module.ManifestPath).
 				Text(`)"`)
 		} else {
 			// No manifest to extract targetSdkVersion from, hope that DexJar is an APK
 			rule.Command().Text(`target_sdk_version="$(`).
-				Tool(global.Tools.Aapt).
+				Tool(global.SoongConfig.Aapt).
 				Flag("dump badging").
 				Input(module.DexPath).
 				Text(`| grep "targetSdkVersion" | sed -n "s/targetSdkVersion:'\(.*\)'/\1/p"`).
@@ -332,7 +327,7 @@
 			Implicits(conditionalClassLoaderContextHost29)
 		rule.Command().Textf(`conditional_target_libs_29="%s"`,
 			strings.Join(conditionalClassLoaderContextTarget29, " "))
-		rule.Command().Text("source").Tool(global.Tools.ConstructContext).Input(module.DexPath)
+		rule.Command().Text("source").Tool(global.SoongConfig.ConstructContext).Input(module.DexPath)
 	}
 
 	// Devices that do not have a product partition use a symlink from /product to /system/product.
@@ -345,7 +340,7 @@
 
 	cmd := rule.Command().
 		Text(`ANDROID_LOG_TAGS="*:e"`).
-		Tool(global.Tools.Dex2oat).
+		Tool(global.SoongConfig.Dex2oat).
 		Flag("--avoid-storing-invocation").
 		FlagWithOutput("--write-invocation-to=", invocationPath).ImplicitOutput(invocationPath).
 		Flag("--runtime-arg").FlagWithArg("-Xms", global.Dex2oatXms).
@@ -354,7 +349,7 @@
 		Flag("--runtime-arg").FlagWithList("-Xbootclasspath-locations:", module.PreoptBootClassPathDexLocations, ":").
 		Flag("${class_loader_context_arg}").
 		Flag("${stored_class_loader_context_arg}").
-		FlagWithArg("--boot-image=", bootImageLocation).Implicits(bootImageDeps).
+		FlagWithArg("--boot-image=", strings.Join(module.DexPreoptImageLocations, ":")).Implicits(module.DexPreoptImagesDeps[archIdx].Paths()).
 		FlagWithInput("--dex-file=", module.DexPath).
 		FlagWithArg("--dex-location=", dexLocationArg).
 		FlagWithOutput("--oat-file=", odexPath).ImplicitOutput(vdexPath).
@@ -414,7 +409,7 @@
 		dmInstalledPath := pathtools.ReplaceExtension(module.DexLocation, "dm")
 		tmpPath := module.BuildPath.InSameDir(ctx, "primary.vdex")
 		rule.Command().Text("cp -f").Input(vdexPath).Output(tmpPath)
-		rule.Command().Tool(global.Tools.SoongZip).
+		rule.Command().Tool(global.SoongConfig.SoongZip).
 			FlagWithArg("-L", "9").
 			FlagWithOutput("-o", dmPath).
 			Flag("-j").
@@ -479,51 +474,6 @@
 	rule.Install(vdexPath, vdexInstallPath)
 }
 
-// Return if the dex file in the APK should be stripped.  If an APK is found to contain uncompressed dex files at
-// dex2oat time it will not be stripped even if strip=true.
-func shouldStripDex(module ModuleConfig, global GlobalConfig) bool {
-	strip := !global.DefaultNoStripping
-
-	if dexpreoptDisabled(global, module) {
-		strip = false
-	}
-
-	if module.NoStripping {
-		strip = false
-	}
-
-	// Don't strip modules that are not on the system partition in case the oat/vdex version in system ROM
-	// doesn't match the one in other partitions. It needs to be able to fall back to the APK for that case.
-	if !strings.HasPrefix(module.DexLocation, SystemPartition) {
-		strip = false
-	}
-
-	// system_other isn't there for an OTA, so don't strip if module is on system, and odex is on system_other.
-	if odexOnSystemOther(module, global) {
-		strip = false
-	}
-
-	if module.HasApkLibraries {
-		strip = false
-	}
-
-	// Don't strip with dex files we explicitly uncompress (dexopt will not store the dex code).
-	if module.UncompressedDex {
-		strip = false
-	}
-
-	if shouldGenerateDM(module, global) {
-		strip = false
-	}
-
-	if module.PresignedPrebuilt {
-		// Only strip out files if we can re-sign the package.
-		strip = false
-	}
-
-	return strip
-}
-
 func shouldGenerateDM(module ModuleConfig, global GlobalConfig) bool {
 	// Generating DM files only makes sense for verify, avoid doing for non verify compiler filter APKs.
 	// No reason to use a dm file if the dex is already uncompressed.
@@ -586,6 +536,22 @@
 	}
 }
 
+// Expected format for apexJarValue = <apex name>:<jar name>
+func SplitApexJarPair(apexJarValue string) (string, string) {
+	var apexJarPair []string = strings.SplitN(apexJarValue, ":", 2)
+	if apexJarPair == nil || len(apexJarPair) != 2 {
+		panic(fmt.Errorf("malformed apexJarValue: %q, expected format: <apex>:<jar>",
+			apexJarValue))
+	}
+	return apexJarPair[0], apexJarPair[1]
+}
+
+// Expected format for apexJarValue = <apex name>:<jar name>
+func GetJarLocationFromApexJarPair(apexJarValue string) string {
+	apex, jar := SplitApexJarPair(apexJarValue)
+	return filepath.Join("/apex", apex, "javalib", jar+".jar")
+}
+
 func contains(l []string, s string) bool {
 	for _, e := range l {
 		if e == s {
diff --git a/dexpreopt/dexpreopt_gen/dexpreopt_gen.go b/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
index d54ddb1..e2818bb 100644
--- a/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
+++ b/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
@@ -18,6 +18,7 @@
 	"bytes"
 	"flag"
 	"fmt"
+	"io/ioutil"
 	"os"
 	"path/filepath"
 	"runtime"
@@ -30,18 +31,17 @@
 )
 
 var (
-	dexpreoptScriptPath = flag.String("dexpreopt_script", "", "path to output dexpreopt script")
-	stripScriptPath     = flag.String("strip_script", "", "path to output strip script")
-	globalConfigPath    = flag.String("global", "", "path to global configuration file")
-	moduleConfigPath    = flag.String("module", "", "path to module configuration file")
-	outDir              = flag.String("out_dir", "", "path to output directory")
+	dexpreoptScriptPath   = flag.String("dexpreopt_script", "", "path to output dexpreopt script")
+	globalSoongConfigPath = flag.String("global_soong", "", "path to global configuration file for settings originating from Soong")
+	globalConfigPath      = flag.String("global", "", "path to global configuration file")
+	moduleConfigPath      = flag.String("module", "", "path to module configuration file")
+	outDir                = flag.String("out_dir", "", "path to output directory")
 )
 
 type pathContext struct {
 	config android.Config
 }
 
-func (x *pathContext) Fs() pathtools.FileSystem   { return pathtools.OsFs }
 func (x *pathContext) Config() android.Config     { return x.config }
 func (x *pathContext) AddNinjaFileDeps(...string) {}
 
@@ -64,36 +64,56 @@
 		usage("path to output dexpreopt script is required")
 	}
 
-	if *stripScriptPath == "" {
-		usage("path to output strip script is required")
+	if *globalSoongConfigPath == "" {
+		usage("--global_soong configuration file is required")
 	}
 
 	if *globalConfigPath == "" {
-		usage("path to global configuration file is required")
+		usage("--global configuration file is required")
 	}
 
 	if *moduleConfigPath == "" {
-		usage("path to module configuration file is required")
+		usage("--module configuration file is required")
 	}
 
-	ctx := &pathContext{android.TestConfig(*outDir, nil)}
+	ctx := &pathContext{android.NullConfig(*outDir)}
 
-	globalConfig, _, err := dexpreopt.LoadGlobalConfig(ctx, *globalConfigPath)
+	globalSoongConfigData, err := ioutil.ReadFile(*globalSoongConfigPath)
 	if err != nil {
-		fmt.Fprintf(os.Stderr, "error loading global config %q: %s\n", *globalConfigPath, err)
+		fmt.Fprintf(os.Stderr, "error reading global config %q: %s\n", *globalSoongConfigPath, err)
 		os.Exit(2)
 	}
 
-	moduleConfig, err := dexpreopt.LoadModuleConfig(ctx, *moduleConfigPath)
+	globalSoongConfig, err := dexpreopt.LoadGlobalSoongConfig(ctx, globalSoongConfigData)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "error loading global config %q: %s\n", *globalSoongConfigPath, err)
+		os.Exit(2)
+	}
+
+	globalConfigData, err := ioutil.ReadFile(*globalConfigPath)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "error reading global config %q: %s\n", *globalConfigPath, err)
+		os.Exit(2)
+	}
+
+	globalConfig, err := dexpreopt.LoadGlobalConfig(ctx, globalConfigData, globalSoongConfig)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "error parse global config %q: %s\n", *globalConfigPath, err)
+		os.Exit(2)
+	}
+
+	moduleConfigData, err := ioutil.ReadFile(*moduleConfigPath)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "error reading module config %q: %s\n", *moduleConfigPath, err)
+		os.Exit(2)
+	}
+
+	moduleConfig, err := dexpreopt.LoadModuleConfig(ctx, moduleConfigData)
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "error loading module config %q: %s\n", *moduleConfigPath, err)
 		os.Exit(2)
 	}
 
-	// This shouldn't be using *PathForTesting, but it's outside of soong_build so its OK for now.
-	moduleConfig.StripInputPath = android.PathForTesting("$1")
-	moduleConfig.StripOutputPath = android.WritablePathForTesting("$2")
-
 	moduleConfig.DexPath = android.PathForTesting("$1")
 
 	defer func() {
@@ -110,11 +130,11 @@
 		}
 	}()
 
-	writeScripts(ctx, globalConfig, moduleConfig, *dexpreoptScriptPath, *stripScriptPath)
+	writeScripts(ctx, globalConfig, moduleConfig, *dexpreoptScriptPath)
 }
 
 func writeScripts(ctx android.PathContext, global dexpreopt.GlobalConfig, module dexpreopt.ModuleConfig,
-	dexpreoptScriptPath, stripScriptPath string) {
+	dexpreoptScriptPath string) {
 	dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(ctx, global, module)
 	if err != nil {
 		panic(err)
@@ -130,16 +150,11 @@
 		dexpreoptRule.Command().Text("mkdir -p").Flag(filepath.Dir(installPath.String()))
 		dexpreoptRule.Command().Text("cp -f").Input(install.From).Output(installPath)
 	}
-	dexpreoptRule.Command().Tool(global.Tools.SoongZip).
+	dexpreoptRule.Command().Tool(global.SoongConfig.SoongZip).
 		FlagWithArg("-o ", "$2").
 		FlagWithArg("-C ", installDir.String()).
 		FlagWithArg("-D ", installDir.String())
 
-	stripRule, err := dexpreopt.GenerateStripRule(global, module)
-	if err != nil {
-		panic(err)
-	}
-
 	write := func(rule *android.RuleBuilder, file string) {
 		script := &bytes.Buffer{}
 		script.WriteString(scriptHeader)
@@ -180,15 +195,8 @@
 	if module.DexPath.String() != "$1" {
 		panic(fmt.Errorf("module.DexPath must be '$1', was %q", module.DexPath))
 	}
-	if module.StripInputPath.String() != "$1" {
-		panic(fmt.Errorf("module.StripInputPath must be '$1', was %q", module.StripInputPath))
-	}
-	if module.StripOutputPath.String() != "$2" {
-		panic(fmt.Errorf("module.StripOutputPath must be '$2', was %q", module.StripOutputPath))
-	}
 
 	write(dexpreoptRule, dexpreoptScriptPath)
-	write(stripRule, stripScriptPath)
 }
 
 const scriptHeader = `#!/bin/bash
diff --git a/dexpreopt/dexpreopt_test.go b/dexpreopt/dexpreopt_test.go
index 7f1fe42..a128dc0 100644
--- a/dexpreopt/dexpreopt_test.go
+++ b/dexpreopt/dexpreopt_test.go
@@ -16,17 +16,28 @@
 
 import (
 	"android/soong/android"
-	"reflect"
-	"strings"
+	"fmt"
 	"testing"
 )
 
-func testModuleConfig(ctx android.PathContext) ModuleConfig {
+func testSystemModuleConfig(ctx android.PathContext, name string) ModuleConfig {
+	return testModuleConfig(ctx, name, "system")
+}
+
+func testSystemProductModuleConfig(ctx android.PathContext, name string) ModuleConfig {
+	return testModuleConfig(ctx, name, "system/product")
+}
+
+func testProductModuleConfig(ctx android.PathContext, name string) ModuleConfig {
+	return testModuleConfig(ctx, name, "product")
+}
+
+func testModuleConfig(ctx android.PathContext, name, partition string) ModuleConfig {
 	return ModuleConfig{
-		Name:                            "test",
-		DexLocation:                     "/system/app/test/test.apk",
-		BuildPath:                       android.PathForOutput(ctx, "test/test.apk"),
-		DexPath:                         android.PathForOutput(ctx, "test/dex/test.jar"),
+		Name:                            name,
+		DexLocation:                     fmt.Sprintf("/%s/app/test/%s.apk", partition, name),
+		BuildPath:                       android.PathForOutput(ctx, fmt.Sprintf("%s/%s.apk", name, name)),
+		DexPath:                         android.PathForOutput(ctx, fmt.Sprintf("%s/dex/%s.jar", name, name)),
 		UncompressedDex:                 false,
 		HasApkLibraries:                 false,
 		PreoptFlags:                     nil,
@@ -38,22 +49,20 @@
 		LibraryPaths:                    nil,
 		Archs:                           []android.ArchType{android.Arm},
 		DexPreoptImages:                 android.Paths{android.PathForTesting("system/framework/arm/boot.art")},
-		DexPreoptImagesDeps:             []android.Paths{android.Paths{}},
+		DexPreoptImagesDeps:             []android.OutputPaths{android.OutputPaths{}},
+		DexPreoptImageLocations:         []string{},
 		PreoptBootClassPathDexFiles:     nil,
 		PreoptBootClassPathDexLocations: nil,
 		PreoptExtractedApk:              false,
 		NoCreateAppImage:                false,
 		ForceCreateAppImage:             false,
 		PresignedPrebuilt:               false,
-		NoStripping:                     false,
-		StripInputPath:                  android.PathForOutput(ctx, "unstripped/test.apk"),
-		StripOutputPath:                 android.PathForOutput(ctx, "stripped/test.apk"),
 	}
 }
 
 func TestDexPreopt(t *testing.T) {
-	ctx := android.PathContextForTesting(android.TestConfig("out", nil), nil)
-	global, module := GlobalConfigForTests(ctx), testModuleConfig(ctx)
+	ctx := android.PathContextForTesting(android.TestConfig("out", nil, "", nil))
+	global, module := GlobalConfigForTests(ctx), testSystemModuleConfig(ctx, "test")
 
 	rule, err := GenerateDexpreoptRule(ctx, global, module)
 	if err != nil {
@@ -70,45 +79,67 @@
 	}
 }
 
-func TestDexPreoptStrip(t *testing.T) {
-	// Test that we panic if we strip in a configuration where stripping is not allowed.
-	ctx := android.PathContextForTesting(android.TestConfig("out", nil), nil)
-	global, module := GlobalConfigForTests(ctx), testModuleConfig(ctx)
-
-	global.NeverAllowStripping = true
-	module.NoStripping = false
-
-	_, err := GenerateStripRule(global, module)
-	if err == nil {
-		t.Errorf("Expected an error when calling GenerateStripRule on a stripped module")
-	}
-}
-
 func TestDexPreoptSystemOther(t *testing.T) {
-	ctx := android.PathContextForTesting(android.TestConfig("out", nil), nil)
-	global, module := GlobalConfigForTests(ctx), testModuleConfig(ctx)
+	ctx := android.PathContextForTesting(android.TestConfig("out", nil, "", nil))
+	global := GlobalConfigForTests(ctx)
+	systemModule := testSystemModuleConfig(ctx, "Stest")
+	systemProductModule := testSystemProductModuleConfig(ctx, "SPtest")
+	productModule := testProductModuleConfig(ctx, "Ptest")
 
 	global.HasSystemOther = true
-	global.PatternsOnSystemOther = []string{"app/%"}
 
-	rule, err := GenerateDexpreoptRule(ctx, global, module)
-	if err != nil {
-		t.Fatal(err)
+	type moduleTest struct {
+		module            ModuleConfig
+		expectedPartition string
+	}
+	tests := []struct {
+		patterns    []string
+		moduleTests []moduleTest
+	}{
+		{
+			patterns: []string{"app/%"},
+			moduleTests: []moduleTest{
+				{module: systemModule, expectedPartition: "system_other/system"},
+				{module: systemProductModule, expectedPartition: "system/product"},
+				{module: productModule, expectedPartition: "product"},
+			},
+		},
+		// product/app/% only applies to product apps inside the system partition
+		{
+			patterns: []string{"app/%", "product/app/%"},
+			moduleTests: []moduleTest{
+				{module: systemModule, expectedPartition: "system_other/system"},
+				{module: systemProductModule, expectedPartition: "system_other/system/product"},
+				{module: productModule, expectedPartition: "product"},
+			},
+		},
 	}
 
-	wantInstalls := android.RuleBuilderInstalls{
-		{android.PathForOutput(ctx, "test/oat/arm/package.odex"), "/system_other/app/test/oat/arm/test.odex"},
-		{android.PathForOutput(ctx, "test/oat/arm/package.vdex"), "/system_other/app/test/oat/arm/test.vdex"},
+	for _, test := range tests {
+		global.PatternsOnSystemOther = test.patterns
+		for _, mt := range test.moduleTests {
+			rule, err := GenerateDexpreoptRule(ctx, global, mt.module)
+			if err != nil {
+				t.Fatal(err)
+			}
+
+			name := mt.module.Name
+			wantInstalls := android.RuleBuilderInstalls{
+				{android.PathForOutput(ctx, name+"/oat/arm/package.odex"), fmt.Sprintf("/%s/app/test/oat/arm/%s.odex", mt.expectedPartition, name)},
+				{android.PathForOutput(ctx, name+"/oat/arm/package.vdex"), fmt.Sprintf("/%s/app/test/oat/arm/%s.vdex", mt.expectedPartition, name)},
+			}
+
+			if rule.Installs().String() != wantInstalls.String() {
+				t.Errorf("\nwant installs:\n   %v\ngot:\n   %v", wantInstalls, rule.Installs())
+			}
+		}
 	}
 
-	if rule.Installs().String() != wantInstalls.String() {
-		t.Errorf("\nwant installs:\n   %v\ngot:\n   %v", wantInstalls, rule.Installs())
-	}
 }
 
 func TestDexPreoptProfile(t *testing.T) {
-	ctx := android.PathContextForTesting(android.TestConfig("out", nil), nil)
-	global, module := GlobalConfigForTests(ctx), testModuleConfig(ctx)
+	ctx := android.PathContextForTesting(android.TestConfig("out", nil, "", nil))
+	global, module := GlobalConfigForTests(ctx), testSystemModuleConfig(ctx, "test")
 
 	module.ProfileClassListing = android.OptionalPathForPath(android.PathForTesting("profile"))
 
@@ -128,56 +159,3 @@
 		t.Errorf("\nwant installs:\n   %v\ngot:\n   %v", wantInstalls, rule.Installs())
 	}
 }
-
-func TestStripDex(t *testing.T) {
-	tests := []struct {
-		name  string
-		setup func(global *GlobalConfig, module *ModuleConfig)
-		strip bool
-	}{
-		{
-			name:  "default strip",
-			setup: func(global *GlobalConfig, module *ModuleConfig) {},
-			strip: true,
-		},
-		{
-			name:  "global no stripping",
-			setup: func(global *GlobalConfig, module *ModuleConfig) { global.DefaultNoStripping = true },
-			strip: false,
-		},
-		{
-			name:  "module no stripping",
-			setup: func(global *GlobalConfig, module *ModuleConfig) { module.NoStripping = true },
-			strip: false,
-		},
-	}
-
-	for _, test := range tests {
-		t.Run(test.name, func(t *testing.T) {
-
-			ctx := android.PathContextForTesting(android.TestConfig("out", nil), nil)
-			global, module := GlobalConfigForTests(ctx), testModuleConfig(ctx)
-
-			test.setup(&global, &module)
-
-			rule, err := GenerateStripRule(global, module)
-			if err != nil {
-				t.Fatal(err)
-			}
-
-			if test.strip {
-				want := `zip2zip -i out/unstripped/test.apk -o out/stripped/test.apk -x "classes*.dex"`
-				if len(rule.Commands()) < 1 || !strings.Contains(rule.Commands()[0], want) {
-					t.Errorf("\nwant commands[0] to have:\n   %v\ngot:\n   %v", want, rule.Commands()[0])
-				}
-			} else {
-				wantCommands := []string{
-					"cp -f out/unstripped/test.apk out/stripped/test.apk",
-				}
-				if !reflect.DeepEqual(rule.Commands(), wantCommands) {
-					t.Errorf("\nwant commands:\n   %v\ngot:\n   %v", wantCommands, rule.Commands())
-				}
-			}
-		})
-	}
-}
diff --git a/docs/best_practices.md b/docs/best_practices.md
index 546e19e..bc760b8 100644
--- a/docs/best_practices.md
+++ b/docs/best_practices.md
@@ -146,3 +146,145 @@
 `LOCAL_SHARED_LIBRARIES` / `shared_libs`, then those dependencies will trigger
 them to be installed when necessary. Adding unnecessary libraries into
 `PRODUCT_PACKAGES` will force them to always be installed, wasting space.
+
+## Removing conditionals
+
+Over-use of conditionals in the build files results in an untestable number
+of build combinations, leading to more build breakages.  It also makes the
+code less testable, as it must be built with each combination of flags to
+be tested.
+
+### Conditionally compiled module
+
+Conditionally compiling a module can generally be replaced with conditional
+installation:
+
+```
+ifeq (some condition)
+# body of the Android.mk file
+LOCAL_MODULE:= bt_logger
+include $(BUILD_EXECUTABLE)
+endif
+```
+
+Becomes:
+
+```
+cc_binary {
+    name: "bt_logger",
+    // body of the module
+}
+```
+
+And in a product Makefile somewhere (something included with
+`$(call inherit-product, ...)`:
+
+```
+ifeq (some condition) # Or no condition
+PRODUCT_PACKAGES += bt_logger
+endif
+```
+
+If the condition was on a type of board or product, it can often be dropped
+completely by putting the `PRODUCT_PACKAGES` entry in a product makefile that
+is included only by the correct products or boards.
+
+### Conditionally compiled module with multiple implementations
+
+If there are multiple implementations of the same module with one selected
+for compilation via a conditional, the implementations can sometimes be renamed
+to unique values.
+
+For example, the name of the gralloc HAL module can be overridden by the
+`ro.hardware.gralloc` system property:
+
+```
+# In hardware/acme/soc_a/gralloc/Android.mk:
+ifeq ($(TARGET_BOARD_PLATFORM),soc_a)
+LOCAL_MODULE := gralloc.acme
+...
+include $(BUILD_SHARED_LIBRARY)
+endif
+
+# In hardware/acme/soc_b/gralloc/Android.mk:
+ifeq ($(TARGET_BOARD_PLATFORM),soc_b)
+LOCAL_MODULE := gralloc.acme
+...
+include $(BUILD_SHARED_LIBRARY)
+endif
+```
+
+Becomes:
+```
+# In hardware/acme/soc_a/gralloc/Android.bp:
+cc_library {
+    name: "gralloc.soc_a",
+    ...
+}
+
+# In hardware/acme/soc_b/gralloc/Android.bp:
+cc_library {
+    name: "gralloc.soc_b",
+    ...
+}
+```
+
+Then to select the correct gralloc implementation, a product makefile inherited
+by products that use soc_a should contain:
+
+```
+PRODUCT_PACKAGES += gralloc.soc_a
+PRODUCT_PROPERTY_OVERRIDES += ro.hardware.gralloc=soc_a
+```
+
+In cases where the names cannot be made unique a `soong_namespace` should be
+used to partition a set of modules so that they are built only when the
+namespace is listed in `PRODUCT_SOONG_NAMESPACES`.  See the
+[Referencing Modules](../README.md#referencing-modules) section of the Soong
+README.md for more on namespaces.
+
+### Module with name based on variable
+
+HAL modules sometimes use variables like `$(TARGET_BOARD_PLATFORM)` in their
+module name.  These can be renamed to a fixed name.
+
+For example, the name of the gralloc HAL module can be overridden by the
+`ro.hardware.gralloc` system property:
+
+```
+LOCAL_MODULE := gralloc.$(TARGET_BOARD_PLATFORM)
+...
+include $(BUILD_SHARED_LIBRARY)
+```
+
+Becomes:
+```
+cc_library {
+    name: "gralloc.acme",
+    ...
+}
+```
+
+Then to select the correct gralloc implementation, a product makefile should
+contain:
+
+```
+PRODUCT_PACKAGES += gralloc.acme
+PRODUCT_PROPERTY_OVERRIDES += ro.hardware.gralloc=acme
+```
+
+### Conditionally used source files, libraries or flags
+
+The preferred solution is to convert the conditional to runtime, either by
+autodetecting the correct value or loading the value from a system property
+or a configuration file.
+
+As a last resort, if the conditional cannot be removed, a Soong plugin can
+be written in Go that can implement additional features for specific module
+types.  Soong plugins are inherently tightly coupled to the build system
+and will require ongoing maintenance as the build system is changed; so
+plugins should be used only when absolutely required.
+
+See [art/build/art.go](https://android.googlesource.com/platform/art/+/master/build/art.go)
+or [external/llvm/soong/llvm.go](https://android.googlesource.com/platform/external/llvm/+/master/soong/llvm.go)
+for examples of more complex conditionals on product variables or environment variables.
diff --git a/env/env.go b/env/env.go
index bf58a99..a98e1f6 100644
--- a/env/env.go
+++ b/env/env.go
@@ -27,7 +27,7 @@
 type envFileEntry struct{ Key, Value string }
 type envFileData []envFileEntry
 
-func WriteEnvFile(filename string, envDeps map[string]string) error {
+func EnvFileContents(envDeps map[string]string) ([]byte, error) {
 	contents := make(envFileData, 0, len(envDeps))
 	for key, value := range envDeps {
 		contents = append(contents, envFileEntry{key, value})
@@ -37,17 +37,12 @@
 
 	data, err := json.MarshalIndent(contents, "", "    ")
 	if err != nil {
-		return err
+		return nil, err
 	}
 
 	data = append(data, '\n')
 
-	err = ioutil.WriteFile(filename, data, 0664)
-	if err != nil {
-		return err
-	}
-
-	return nil
+	return data, nil
 }
 
 func StaleEnvFile(filename string) (bool, error) {
diff --git a/finder/finder_test.go b/finder/finder_test.go
index 29711fc..f6d0aa9 100644
--- a/finder/finder_test.go
+++ b/finder/finder_test.go
@@ -891,8 +891,8 @@
 			IncludeFiles: []string{"findme.txt"},
 		},
 	)
-	foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
 	filesystem.Clock.Tick()
+	foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
 	finder.Shutdown()
 	// check the response of the first finder
 	assertSameResponse(t, foundPaths, []string{"/tmp/a/findme.txt"})
@@ -1522,8 +1522,8 @@
 			IncludeFiles: []string{"hi.txt"},
 		},
 	)
-	foundPaths := finder.FindAll()
 	filesystem.Clock.Tick()
+	foundPaths := finder.FindAll()
 	finder.Shutdown()
 	// check results
 	assertSameResponse(t, foundPaths, []string{"/tmp/a/hi.txt"})
@@ -1583,8 +1583,8 @@
 			IncludeFiles: []string{"hi.txt"},
 		},
 	)
-	foundPaths := finder.FindAll()
 	filesystem.Clock.Tick()
+	foundPaths := finder.FindAll()
 	finder.Shutdown()
 	allPaths := []string{"/tmp/hi.txt", "/tmp/a/hi.txt", "/tmp/a/a/hi.txt", "/tmp/b/hi.txt"}
 	// check results
@@ -1629,8 +1629,8 @@
 			IncludeFiles: []string{"hi.txt"},
 		},
 	)
-	foundPaths := finder.FindAll()
 	filesystem.Clock.Tick()
+	foundPaths := finder.FindAll()
 	finder.Shutdown()
 	// check results
 	assertSameResponse(t, foundPaths, []string{"/tmp/hi.txt"})
@@ -1650,8 +1650,8 @@
 			IncludeFiles: []string{"hi.txt"},
 		},
 	)
-	foundPaths := finder.FindAll()
 	filesystem.Clock.Tick()
+	foundPaths := finder.FindAll()
 	finder.Shutdown()
 	// check results
 	assertSameResponse(t, foundPaths, []string{"/tmp/a/hi.txt"})
diff --git a/genrule/genrule.go b/genrule/genrule.go
index b0657ff..57ca9bc 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -17,6 +17,7 @@
 import (
 	"fmt"
 	"io"
+	"strconv"
 	"strings"
 
 	"github.com/google/blueprint"
@@ -37,10 +38,21 @@
 
 var (
 	pctx = android.NewPackageContext("android/soong/genrule")
+
+	gensrcsMerge = pctx.AndroidStaticRule("gensrcsMerge", blueprint.RuleParams{
+		Command:        "${soongZip} -o ${tmpZip} @${tmpZip}.rsp && ${zipSync} -d ${genDir} ${tmpZip}",
+		CommandDeps:    []string{"${soongZip}", "${zipSync}"},
+		Rspfile:        "${tmpZip}.rsp",
+		RspfileContent: "${zipArgs}",
+	}, "tmpZip", "genDir", "zipArgs")
 )
 
 func init() {
+	pctx.Import("android/soong/android")
 	pctx.HostBinToolVariable("sboxCmd", "sbox")
+
+	pctx.HostBinToolVariable("soongZip", "soong_zip")
+	pctx.HostBinToolVariable("zipSync", "zipsync")
 }
 
 type SourceFileGenerator interface {
@@ -106,14 +118,15 @@
 	// For other packages to make their own genrules with extra
 	// properties
 	Extra interface{}
+	android.ImageInterface
 
 	properties generatorProperties
 
 	taskGenerator taskFunc
 
-	deps       android.Paths
-	rule       blueprint.Rule
-	rawCommand string
+	deps        android.Paths
+	rule        blueprint.Rule
+	rawCommands []string
 
 	exportedIncludeDirs android.Paths
 
@@ -121,15 +134,20 @@
 	outputDeps  android.Paths
 
 	subName string
+	subDir  string
 }
 
-type taskFunc func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) generateTask
+type taskFunc func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) []generateTask
 
 type generateTask struct {
 	in          android.Paths
 	out         android.WritablePaths
+	copyTo      android.WritablePaths
+	genDir      android.WritablePath
 	sandboxOuts []string
 	cmd         string
+	shard       int
+	shards      int
 }
 
 func (g *Module) GeneratedSourceFiles() android.Paths {
@@ -155,9 +173,7 @@
 			if m := android.SrcIsModule(tool); m != "" {
 				tool = m
 			}
-			ctx.AddFarVariationDependencies([]blueprint.Variation{
-				{Mutator: "arch", Variation: ctx.Config().BuildOsVariant},
-			}, tag, tool)
+			ctx.AddFarVariationDependencies(ctx.Config().BuildOSTarget.Variations(), tag, tool)
 		}
 	}
 }
@@ -168,10 +184,10 @@
 	if len(g.properties.Export_include_dirs) > 0 {
 		for _, dir := range g.properties.Export_include_dirs {
 			g.exportedIncludeDirs = append(g.exportedIncludeDirs,
-				android.PathForModuleGen(ctx, ctx.ModuleDir(), dir))
+				android.PathForModuleGen(ctx, g.subDir, ctx.ModuleDir(), dir))
 		}
 	} else {
-		g.exportedIncludeDirs = append(g.exportedIncludeDirs, android.PathForModuleGen(ctx, ""))
+		g.exportedIncludeDirs = append(g.exportedIncludeDirs, android.PathForModuleGen(ctx, g.subDir))
 	}
 
 	locationLabels := map[string][]string{}
@@ -276,120 +292,170 @@
 		}
 	}
 
-	task := g.taskGenerator(ctx, String(g.properties.Cmd), srcFiles)
+	var copyFrom android.Paths
+	var outputFiles android.WritablePaths
+	var zipArgs strings.Builder
 
-	for _, out := range task.out {
-		addLocationLabel(out.Rel(), []string{filepath.Join("__SBOX_OUT_DIR__", out.Rel())})
-	}
-
-	referencedDepfile := false
-
-	rawCommand, err := android.Expand(task.cmd, func(name string) (string, error) {
-		// report the error directly without returning an error to android.Expand to catch multiple errors in a
-		// single run
-		reportError := func(fmt string, args ...interface{}) (string, error) {
-			ctx.PropertyErrorf("cmd", fmt, args...)
-			return "SOONG_ERROR", nil
+	for _, task := range g.taskGenerator(ctx, String(g.properties.Cmd), srcFiles) {
+		for _, out := range task.out {
+			addLocationLabel(out.Rel(), []string{filepath.Join("__SBOX_OUT_DIR__", out.Rel())})
 		}
 
-		switch name {
-		case "location":
-			if len(g.properties.Tools) == 0 && len(g.properties.Tool_files) == 0 {
-				return reportError("at least one `tools` or `tool_files` is required if $(location) is used")
+		referencedDepfile := false
+
+		rawCommand, err := android.ExpandNinjaEscaped(task.cmd, func(name string) (string, bool, error) {
+			// report the error directly without returning an error to android.Expand to catch multiple errors in a
+			// single run
+			reportError := func(fmt string, args ...interface{}) (string, bool, error) {
+				ctx.PropertyErrorf("cmd", fmt, args...)
+				return "SOONG_ERROR", false, nil
 			}
-			paths := locationLabels[firstLabel]
-			if len(paths) == 0 {
-				return reportError("default label %q has no files", firstLabel)
-			} else if len(paths) > 1 {
-				return reportError("default label %q has multiple files, use $(locations %s) to reference it",
-					firstLabel, firstLabel)
-			}
-			return locationLabels[firstLabel][0], nil
-		case "in":
-			return "${in}", nil
-		case "out":
-			return "__SBOX_OUT_FILES__", nil
-		case "depfile":
-			referencedDepfile = true
-			if !Bool(g.properties.Depfile) {
-				return reportError("$(depfile) used without depfile property")
-			}
-			return "__SBOX_DEPFILE__", nil
-		case "genDir":
-			return "__SBOX_OUT_DIR__", nil
-		default:
-			if strings.HasPrefix(name, "location ") {
-				label := strings.TrimSpace(strings.TrimPrefix(name, "location "))
-				if paths, ok := locationLabels[label]; ok {
-					if len(paths) == 0 {
-						return reportError("label %q has no files", label)
-					} else if len(paths) > 1 {
-						return reportError("label %q has multiple files, use $(locations %s) to reference it",
-							label, label)
-					}
-					return paths[0], nil
-				} else {
-					return reportError("unknown location label %q", label)
+
+			switch name {
+			case "location":
+				if len(g.properties.Tools) == 0 && len(g.properties.Tool_files) == 0 {
+					return reportError("at least one `tools` or `tool_files` is required if $(location) is used")
 				}
-			} else if strings.HasPrefix(name, "locations ") {
-				label := strings.TrimSpace(strings.TrimPrefix(name, "locations "))
-				if paths, ok := locationLabels[label]; ok {
-					if len(paths) == 0 {
-						return reportError("label %q has no files", label)
-					}
-					return strings.Join(paths, " "), nil
-				} else {
-					return reportError("unknown locations label %q", label)
+				paths := locationLabels[firstLabel]
+				if len(paths) == 0 {
+					return reportError("default label %q has no files", firstLabel)
+				} else if len(paths) > 1 {
+					return reportError("default label %q has multiple files, use $(locations %s) to reference it",
+						firstLabel, firstLabel)
 				}
-			} else {
-				return reportError("unknown variable '$(%s)'", name)
+				return locationLabels[firstLabel][0], false, nil
+			case "in":
+				return "${in}", true, nil
+			case "out":
+				return "__SBOX_OUT_FILES__", false, nil
+			case "depfile":
+				referencedDepfile = true
+				if !Bool(g.properties.Depfile) {
+					return reportError("$(depfile) used without depfile property")
+				}
+				return "__SBOX_DEPFILE__", false, nil
+			case "genDir":
+				return "__SBOX_OUT_DIR__", false, nil
+			default:
+				if strings.HasPrefix(name, "location ") {
+					label := strings.TrimSpace(strings.TrimPrefix(name, "location "))
+					if paths, ok := locationLabels[label]; ok {
+						if len(paths) == 0 {
+							return reportError("label %q has no files", label)
+						} else if len(paths) > 1 {
+							return reportError("label %q has multiple files, use $(locations %s) to reference it",
+								label, label)
+						}
+						return paths[0], false, nil
+					} else {
+						return reportError("unknown location label %q", label)
+					}
+				} else if strings.HasPrefix(name, "locations ") {
+					label := strings.TrimSpace(strings.TrimPrefix(name, "locations "))
+					if paths, ok := locationLabels[label]; ok {
+						if len(paths) == 0 {
+							return reportError("label %q has no files", label)
+						}
+						return strings.Join(paths, " "), false, nil
+					} else {
+						return reportError("unknown locations label %q", label)
+					}
+				} else {
+					return reportError("unknown variable '$(%s)'", name)
+				}
 			}
+		})
+
+		if err != nil {
+			ctx.PropertyErrorf("cmd", "%s", err.Error())
+			return
 		}
-	})
 
-	if err != nil {
-		ctx.PropertyErrorf("cmd", "%s", err.Error())
-		return
+		if Bool(g.properties.Depfile) && !referencedDepfile {
+			ctx.PropertyErrorf("cmd", "specified depfile=true but did not include a reference to '${depfile}' in cmd")
+			return
+		}
+
+		// tell the sbox command which directory to use as its sandbox root
+		buildDir := android.PathForOutput(ctx).String()
+		sandboxPath := shared.TempDirForOutDir(buildDir)
+
+		// recall that Sprintf replaces percent sign expressions, whereas dollar signs expressions remain as written,
+		// to be replaced later by ninja_strings.go
+		depfilePlaceholder := ""
+		if Bool(g.properties.Depfile) {
+			depfilePlaceholder = "$depfileArgs"
+		}
+
+		// Escape the command for the shell
+		rawCommand = "'" + strings.Replace(rawCommand, "'", `'\''`, -1) + "'"
+		g.rawCommands = append(g.rawCommands, rawCommand)
+		sandboxCommand := fmt.Sprintf("rm -rf %s && $sboxCmd --sandbox-path %s --output-root %s -c %s %s $allouts",
+			task.genDir, sandboxPath, task.genDir, rawCommand, depfilePlaceholder)
+
+		ruleParams := blueprint.RuleParams{
+			Command:     sandboxCommand,
+			CommandDeps: []string{"$sboxCmd"},
+		}
+		args := []string{"allouts"}
+		if Bool(g.properties.Depfile) {
+			ruleParams.Deps = blueprint.DepsGCC
+			args = append(args, "depfileArgs")
+		}
+		name := "generator"
+		if task.shards > 1 {
+			name += strconv.Itoa(task.shard)
+		}
+		rule := ctx.Rule(pctx, name, ruleParams, args...)
+
+		g.generateSourceFile(ctx, task, rule)
+
+		if len(task.copyTo) > 0 {
+			outputFiles = append(outputFiles, task.copyTo...)
+			copyFrom = append(copyFrom, task.out.Paths()...)
+			zipArgs.WriteString(" -C " + task.genDir.String())
+			zipArgs.WriteString(android.JoinWithPrefix(task.out.Strings(), " -f "))
+		} else {
+			outputFiles = append(outputFiles, task.out...)
+		}
 	}
 
-	if Bool(g.properties.Depfile) && !referencedDepfile {
-		ctx.PropertyErrorf("cmd", "specified depfile=true but did not include a reference to '${depfile}' in cmd")
+	if len(copyFrom) > 0 {
+		ctx.Build(pctx, android.BuildParams{
+			Rule:      gensrcsMerge,
+			Implicits: copyFrom,
+			Outputs:   outputFiles,
+			Args: map[string]string{
+				"zipArgs": zipArgs.String(),
+				"tmpZip":  android.PathForModuleGen(ctx, g.subDir+".zip").String(),
+				"genDir":  android.PathForModuleGen(ctx, g.subDir).String(),
+			},
+		})
 	}
 
-	// tell the sbox command which directory to use as its sandbox root
-	buildDir := android.PathForOutput(ctx).String()
-	sandboxPath := shared.TempDirForOutDir(buildDir)
+	g.outputFiles = outputFiles.Paths()
 
-	// recall that Sprintf replaces percent sign expressions, whereas dollar signs expressions remain as written,
-	// to be replaced later by ninja_strings.go
-	depfilePlaceholder := ""
-	if Bool(g.properties.Depfile) {
-		depfilePlaceholder = "$depfileArgs"
+	// For <= 6 outputs, just embed those directly in the users. Right now, that covers >90% of
+	// the genrules on AOSP. That will make things simpler to look at the graph in the common
+	// case. For larger sets of outputs, inject a phony target in between to limit ninja file
+	// growth.
+	if len(g.outputFiles) <= 6 {
+		g.outputDeps = g.outputFiles
+	} else {
+		phonyFile := android.PathForModuleGen(ctx, "genrule-phony")
+
+		ctx.Build(pctx, android.BuildParams{
+			Rule:   blueprint.Phony,
+			Output: phonyFile,
+			Inputs: g.outputFiles,
+		})
+
+		g.outputDeps = android.Paths{phonyFile}
 	}
 
-	genDir := android.PathForModuleGen(ctx)
-	// Escape the command for the shell
-	rawCommand = "'" + strings.Replace(rawCommand, "'", `'\''`, -1) + "'"
-	g.rawCommand = rawCommand
-	sandboxCommand := fmt.Sprintf("$sboxCmd --sandbox-path %s --output-root %s -c %s %s $allouts",
-		sandboxPath, genDir, rawCommand, depfilePlaceholder)
-
-	ruleParams := blueprint.RuleParams{
-		Command:     sandboxCommand,
-		CommandDeps: []string{"$sboxCmd"},
-	}
-	args := []string{"allouts"}
-	if Bool(g.properties.Depfile) {
-		ruleParams.Deps = blueprint.DepsGCC
-		args = append(args, "depfileArgs")
-	}
-	g.rule = ctx.Rule(pctx, "generator", ruleParams, args...)
-
-	g.generateSourceFile(ctx, task)
-
 }
 
-func (g *Module) generateSourceFile(ctx android.ModuleContext, task generateTask) {
+func (g *Module) generateSourceFile(ctx android.ModuleContext, task generateTask, rule blueprint.Rule) {
 	desc := "generate"
 	if len(task.out) == 0 {
 		ctx.ModuleErrorf("must have at least one output file")
@@ -404,9 +470,13 @@
 		depFile = android.PathForModuleGen(ctx, task.out[0].Rel()+".d")
 	}
 
+	if task.shards > 1 {
+		desc += " " + strconv.Itoa(task.shard)
+	}
+
 	params := android.BuildParams{
-		Rule:            g.rule,
-		Description:     "generate",
+		Rule:            rule,
+		Description:     desc,
 		Output:          task.out[0],
 		ImplicitOutputs: task.out[1:],
 		Inputs:          task.in,
@@ -421,11 +491,6 @@
 	}
 
 	ctx.Build(pctx, params)
-
-	for _, outputFile := range task.out {
-		g.outputFiles = append(g.outputFiles, outputFile)
-	}
-	g.outputDeps = append(g.outputDeps, task.out[0])
 }
 
 // Collect information for opening IDE project files in java/jdeps.go.
@@ -447,7 +512,7 @@
 		SubName:    g.subName,
 		Extra: []android.AndroidMkExtraFunc{
 			func(w io.Writer, outputFile android.Path) {
-				fmt.Fprintln(w, "LOCAL_ADDITIONAL_DEPENDENCIES :=", strings.Join(g.outputFiles.Strings(), " "))
+				fmt.Fprintln(w, "LOCAL_ADDITIONAL_DEPENDENCIES :=", strings.Join(g.outputDeps.Strings(), " "))
 			},
 		},
 		Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
@@ -468,9 +533,20 @@
 	module.AddProperties(props...)
 	module.AddProperties(&module.properties)
 
+	module.ImageInterface = noopImageInterface{}
+
 	return module
 }
 
+type noopImageInterface struct{}
+
+func (x noopImageInterface) ImageMutatorBegin(android.BaseModuleContext)                 {}
+func (x noopImageInterface) CoreVariantNeeded(android.BaseModuleContext) bool            { return false }
+func (x noopImageInterface) RecoveryVariantNeeded(android.BaseModuleContext) bool        { return false }
+func (x noopImageInterface) ExtraImageVariations(ctx android.BaseModuleContext) []string { return nil }
+func (x noopImageInterface) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) {
+}
+
 // replace "out" with "__SBOX_OUT_DIR__/<the value of ${out}>"
 func pathToSandboxOut(path android.Path, genDir android.Path) string {
 	relOut, err := filepath.Rel(genDir.String(), path.String())
@@ -484,47 +560,80 @@
 func NewGenSrcs() *Module {
 	properties := &genSrcsProperties{}
 
-	taskGenerator := func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) generateTask {
-		commands := []string{}
-		outFiles := android.WritablePaths{}
-		genDir := android.PathForModuleGen(ctx)
-		sandboxOuts := []string{}
-		for _, in := range srcFiles {
-			outFile := android.GenPathWithExt(ctx, "", in, String(properties.Output_extension))
-			outFiles = append(outFiles, outFile)
+	taskGenerator := func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) []generateTask {
+		genDir := android.PathForModuleGen(ctx, "gensrcs")
+		shardSize := defaultShardSize
+		if s := properties.Shard_size; s != nil {
+			shardSize = int(*s)
+		}
 
-			sandboxOutfile := pathToSandboxOut(outFile, genDir)
-			sandboxOuts = append(sandboxOuts, sandboxOutfile)
+		shards := android.ShardPaths(srcFiles, shardSize)
+		var generateTasks []generateTask
 
-			command, err := android.Expand(rawCommand, func(name string) (string, error) {
-				switch name {
-				case "in":
-					return in.String(), nil
-				case "out":
-					return sandboxOutfile, nil
-				default:
-					return "$(" + name + ")", nil
-				}
-			})
-			if err != nil {
-				ctx.PropertyErrorf("cmd", err.Error())
+		for i, shard := range shards {
+			var commands []string
+			var outFiles android.WritablePaths
+			var copyTo android.WritablePaths
+			var shardDir android.WritablePath
+			var sandboxOuts []string
+
+			if len(shards) > 1 {
+				shardDir = android.PathForModuleGen(ctx, strconv.Itoa(i))
+			} else {
+				shardDir = genDir
 			}
 
-			// escape the command in case for example it contains '#', an odd number of '"', etc
-			command = fmt.Sprintf("bash -c %v", proptools.ShellEscape(command))
-			commands = append(commands, command)
-		}
-		fullCommand := strings.Join(commands, " && ")
+			for _, in := range shard {
+				outFile := android.GenPathWithExt(ctx, "gensrcs", in, String(properties.Output_extension))
+				sandboxOutfile := pathToSandboxOut(outFile, genDir)
 
-		return generateTask{
-			in:          srcFiles,
-			out:         outFiles,
-			sandboxOuts: sandboxOuts,
-			cmd:         fullCommand,
+				if len(shards) > 1 {
+					shardFile := android.GenPathWithExt(ctx, strconv.Itoa(i), in, String(properties.Output_extension))
+					copyTo = append(copyTo, outFile)
+					outFile = shardFile
+				}
+
+				outFiles = append(outFiles, outFile)
+				sandboxOuts = append(sandboxOuts, sandboxOutfile)
+
+				command, err := android.Expand(rawCommand, func(name string) (string, error) {
+					switch name {
+					case "in":
+						return in.String(), nil
+					case "out":
+						return sandboxOutfile, nil
+					default:
+						return "$(" + name + ")", nil
+					}
+				})
+				if err != nil {
+					ctx.PropertyErrorf("cmd", err.Error())
+				}
+
+				// escape the command in case for example it contains '#', an odd number of '"', etc
+				command = fmt.Sprintf("bash -c %v", proptools.ShellEscape(command))
+				commands = append(commands, command)
+			}
+			fullCommand := strings.Join(commands, " && ")
+
+			generateTasks = append(generateTasks, generateTask{
+				in:          shard,
+				out:         outFiles,
+				copyTo:      copyTo,
+				genDir:      shardDir,
+				sandboxOuts: sandboxOuts,
+				cmd:         fullCommand,
+				shard:       i,
+				shards:      len(shards),
+			})
 		}
+
+		return generateTasks
 	}
 
-	return generatorFactory(taskGenerator, properties)
+	g := generatorFactory(taskGenerator, properties)
+	g.subDir = "gensrcs"
+	return g
 }
 
 func GenSrcsFactory() android.Module {
@@ -536,12 +645,17 @@
 type genSrcsProperties struct {
 	// extension that will be substituted for each output file
 	Output_extension *string
+
+	// maximum number of files that will be passed on a single command line.
+	Shard_size *int64
 }
 
+const defaultShardSize = 100
+
 func NewGenRule() *Module {
 	properties := &genRuleProperties{}
 
-	taskGenerator := func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) generateTask {
+	taskGenerator := func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) []generateTask {
 		outs := make(android.WritablePaths, len(properties.Out))
 		sandboxOuts := make([]string, len(properties.Out))
 		genDir := android.PathForModuleGen(ctx)
@@ -549,12 +663,13 @@
 			outs[i] = android.PathForModuleGen(ctx, out)
 			sandboxOuts[i] = pathToSandboxOut(outs[i], genDir)
 		}
-		return generateTask{
+		return []generateTask{{
 			in:          srcFiles,
 			out:         outs,
+			genDir:      android.PathForModuleGen(ctx),
 			sandboxOuts: sandboxOuts,
 			cmd:         rawCommand,
-		}
+		}}
 	}
 
 	return generatorFactory(taskGenerator, properties)
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index 0b6952f..ea49e08 100644
--- a/genrule/genrule_test.go
+++ b/genrule/genrule_test.go
@@ -51,17 +51,21 @@
 	os.Exit(run())
 }
 
-func testContext(config android.Config, bp string,
-	fs map[string][]byte) *android.TestContext {
+func testContext(config android.Config) *android.TestContext {
 
 	ctx := android.NewTestArchContext()
-	ctx.RegisterModuleType("filegroup", android.ModuleFactoryAdaptor(android.FileGroupFactory))
-	ctx.RegisterModuleType("genrule", android.ModuleFactoryAdaptor(GenRuleFactory))
-	ctx.RegisterModuleType("genrule_defaults", android.ModuleFactoryAdaptor(defaultsFactory))
-	ctx.RegisterModuleType("tool", android.ModuleFactoryAdaptor(toolFactory))
+	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
+	ctx.RegisterModuleType("genrule", GenRuleFactory)
+	ctx.RegisterModuleType("gensrcs", GenSrcsFactory)
+	ctx.RegisterModuleType("genrule_defaults", defaultsFactory)
+	ctx.RegisterModuleType("tool", toolFactory)
 	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
-	ctx.Register()
+	ctx.Register(config)
 
+	return ctx
+}
+
+func testConfig(bp string, fs map[string][]byte) android.Config {
 	bp += `
 		tool {
 			name: "tool",
@@ -103,21 +107,21 @@
 	`
 
 	mockFS := map[string][]byte{
-		"Android.bp": []byte(bp),
 		"tool":       nil,
 		"tool_file1": nil,
 		"tool_file2": nil,
 		"in1":        nil,
 		"in2":        nil,
+		"in1.txt":    nil,
+		"in2.txt":    nil,
+		"in3.txt":    nil,
 	}
 
 	for k, v := range fs {
 		mockFS[k] = v
 	}
 
-	ctx.MockFileSystem(mockFS)
-
-	return ctx
+	return android.TestArchConfig(buildDir, nil, bp, mockFS)
 }
 
 func TestGenruleCmd(t *testing.T) {
@@ -457,15 +461,15 @@
 
 	for _, test := range testcases {
 		t.Run(test.name, func(t *testing.T) {
-			config := android.TestArchConfig(buildDir, nil)
 			bp := "genrule {\n"
 			bp += "name: \"gen\",\n"
 			bp += test.prop
 			bp += "}\n"
 
+			config := testConfig(bp, nil)
 			config.TestProductVariables.Allow_missing_dependencies = proptools.BoolPtr(test.allowMissingDependencies)
 
-			ctx := testContext(config, bp, nil)
+			ctx := testContext(config)
 			ctx.SetAllowMissingDependencies(test.allowMissingDependencies)
 
 			_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
@@ -491,16 +495,106 @@
 			}
 
 			gen := ctx.ModuleForTests("gen", "").Module().(*Module)
-			if g, w := gen.rawCommand, "'"+test.expect+"'"; w != g {
+			if g, w := gen.rawCommands[0], "'"+test.expect+"'"; w != g {
 				t.Errorf("want %q, got %q", w, g)
 			}
 		})
 	}
+}
+
+func TestGenSrcs(t *testing.T) {
+	testcases := []struct {
+		name string
+		prop string
+
+		allowMissingDependencies bool
+
+		err   string
+		cmds  []string
+		deps  []string
+		files []string
+	}{
+		{
+			name: "gensrcs",
+			prop: `
+				tools: ["tool"],
+				srcs: ["in1.txt", "in2.txt"],
+				cmd: "$(location) $(in) > $(out)",
+			`,
+			cmds: []string{
+				"'bash -c '\\''out/tool in1.txt > __SBOX_OUT_DIR__/in1.h'\\'' && bash -c '\\''out/tool in2.txt > __SBOX_OUT_DIR__/in2.h'\\'''",
+			},
+			deps:  []string{buildDir + "/.intermediates/gen/gen/gensrcs/in1.h", buildDir + "/.intermediates/gen/gen/gensrcs/in2.h"},
+			files: []string{buildDir + "/.intermediates/gen/gen/gensrcs/in1.h", buildDir + "/.intermediates/gen/gen/gensrcs/in2.h"},
+		},
+		{
+			name: "shards",
+			prop: `
+				tools: ["tool"],
+				srcs: ["in1.txt", "in2.txt", "in3.txt"],
+				cmd: "$(location) $(in) > $(out)",
+				shard_size: 2,
+			`,
+			cmds: []string{
+				"'bash -c '\\''out/tool in1.txt > __SBOX_OUT_DIR__/in1.h'\\'' && bash -c '\\''out/tool in2.txt > __SBOX_OUT_DIR__/in2.h'\\'''",
+				"'bash -c '\\''out/tool in3.txt > __SBOX_OUT_DIR__/in3.h'\\'''",
+			},
+			deps:  []string{buildDir + "/.intermediates/gen/gen/gensrcs/in1.h", buildDir + "/.intermediates/gen/gen/gensrcs/in2.h", buildDir + "/.intermediates/gen/gen/gensrcs/in3.h"},
+			files: []string{buildDir + "/.intermediates/gen/gen/gensrcs/in1.h", buildDir + "/.intermediates/gen/gen/gensrcs/in2.h", buildDir + "/.intermediates/gen/gen/gensrcs/in3.h"},
+		},
+	}
+
+	for _, test := range testcases {
+		t.Run(test.name, func(t *testing.T) {
+			bp := "gensrcs {\n"
+			bp += `name: "gen",` + "\n"
+			bp += `output_extension: "h",` + "\n"
+			bp += test.prop
+			bp += "}\n"
+
+			config := testConfig(bp, nil)
+			ctx := testContext(config)
+
+			_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+			if errs == nil {
+				_, errs = ctx.PrepareBuildActions(config)
+			}
+			if errs == nil && test.err != "" {
+				t.Fatalf("want error %q, got no error", test.err)
+			} else if errs != nil && test.err == "" {
+				android.FailIfErrored(t, errs)
+			} else if test.err != "" {
+				if len(errs) != 1 {
+					t.Errorf("want 1 error, got %d errors:", len(errs))
+					for _, err := range errs {
+						t.Errorf("   %s", err.Error())
+					}
+					t.FailNow()
+				}
+				if !strings.Contains(errs[0].Error(), test.err) {
+					t.Fatalf("want %q, got %q", test.err, errs[0].Error())
+				}
+				return
+			}
+
+			gen := ctx.ModuleForTests("gen", "").Module().(*Module)
+			if g, w := gen.rawCommands, test.cmds; !reflect.DeepEqual(w, g) {
+				t.Errorf("want %q, got %q", w, g)
+			}
+
+			if g, w := gen.outputDeps.Strings(), test.deps; !reflect.DeepEqual(w, g) {
+				t.Errorf("want deps %q, got %q", w, g)
+			}
+
+			if g, w := gen.outputFiles.Strings(), test.files; !reflect.DeepEqual(w, g) {
+				t.Errorf("want files %q, got %q", w, g)
+			}
+		})
+	}
 
 }
 
 func TestGenruleDefaults(t *testing.T) {
-	config := android.TestArchConfig(buildDir, nil)
 	bp := `
 				genrule_defaults {
 					name: "gen_defaults1",
@@ -518,7 +612,8 @@
 					defaults: ["gen_defaults1", "gen_defaults2"],
 				}
 			`
-	ctx := testContext(config, bp, nil)
+	config := testConfig(bp, nil)
+	ctx := testContext(config)
 	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
 	if errs == nil {
 		_, errs = ctx.PrepareBuildActions(config)
@@ -529,8 +624,8 @@
 	gen := ctx.ModuleForTests("gen", "").Module().(*Module)
 
 	expectedCmd := "'cp ${in} __SBOX_OUT_FILES__'"
-	if gen.rawCommand != expectedCmd {
-		t.Errorf("Expected cmd: %q, actual: %q", expectedCmd, gen.rawCommand)
+	if gen.rawCommands[0] != expectedCmd {
+		t.Errorf("Expected cmd: %q, actual: %q", expectedCmd, gen.rawCommands[0])
 	}
 
 	expectedSrcs := []string{"in1"}
diff --git a/go.mod b/go.mod
index cc328e0..1483a31 100644
--- a/go.mod
+++ b/go.mod
@@ -7,3 +7,5 @@
 replace github.com/golang/protobuf v0.0.0 => ../../external/golang-protobuf
 
 replace github.com/google/blueprint v0.0.0 => ../blueprint
+
+go 1.13
diff --git a/java/aapt2.go b/java/aapt2.go
index a815160..cfe0dea 100644
--- a/java/aapt2.go
+++ b/java/aapt2.go
@@ -55,13 +55,15 @@
 
 var aapt2CompileRule = pctx.AndroidStaticRule("aapt2Compile",
 	blueprint.RuleParams{
-		Command:     `${config.Aapt2Cmd} compile -o $outDir $cFlags --legacy $in`,
+		Command:     `${config.Aapt2Cmd} compile -o $outDir $cFlags $in`,
 		CommandDeps: []string{"${config.Aapt2Cmd}"},
 	},
 	"outDir", "cFlags")
 
-func aapt2Compile(ctx android.ModuleContext, dir android.Path, paths android.Paths) android.WritablePaths {
-	shards := shardPaths(paths, AAPT2_SHARD_SIZE)
+func aapt2Compile(ctx android.ModuleContext, dir android.Path, paths android.Paths,
+	flags []string) android.WritablePaths {
+
+	shards := android.ShardPaths(paths, AAPT2_SHARD_SIZE)
 
 	ret := make(android.WritablePaths, 0, len(paths))
 
@@ -81,9 +83,7 @@
 			Outputs:     outPaths,
 			Args: map[string]string{
 				"outDir": android.PathForModuleOut(ctx, "aapt2", dir.String()).String(),
-				// Always set --pseudo-localize, it will be stripped out later for release
-				// builds that don't want it.
-				"cFlags": "--pseudo-localize",
+				"cFlags": strings.Join(flags, " "),
 			},
 		})
 	}
@@ -97,14 +97,16 @@
 var aapt2CompileZipRule = pctx.AndroidStaticRule("aapt2CompileZip",
 	blueprint.RuleParams{
 		Command: `${config.ZipSyncCmd} -d $resZipDir $zipSyncFlags $in && ` +
-			`${config.Aapt2Cmd} compile -o $out $cFlags --legacy --dir $resZipDir`,
+			`${config.Aapt2Cmd} compile -o $out $cFlags --dir $resZipDir`,
 		CommandDeps: []string{
 			"${config.Aapt2Cmd}",
 			"${config.ZipSyncCmd}",
 		},
 	}, "cFlags", "resZipDir", "zipSyncFlags")
 
-func aapt2CompileZip(ctx android.ModuleContext, flata android.WritablePath, zip android.Path, zipPrefix string) {
+func aapt2CompileZip(ctx android.ModuleContext, flata android.WritablePath, zip android.Path, zipPrefix string,
+	flags []string) {
+
 	if zipPrefix != "" {
 		zipPrefix = "--zip-prefix " + zipPrefix
 	}
@@ -114,9 +116,7 @@
 		Input:       zip,
 		Output:      flata,
 		Args: map[string]string{
-			// Always set --pseudo-localize, it will be stripped out later for release
-			// builds that don't want it.
-			"cFlags":       "--pseudo-localize",
+			"cFlags":       strings.Join(flags, " "),
 			"resZipDir":    android.PathForModuleOut(ctx, "aapt2", "reszip", flata.Base()).String(),
 			"zipSyncFlags": zipPrefix,
 		},
diff --git a/java/aar.go b/java/aar.go
index 47f6e5f..ae064e5 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -17,6 +17,7 @@
 import (
 	"android/soong/android"
 	"fmt"
+	"path/filepath"
 	"strings"
 
 	"github.com/google/blueprint"
@@ -33,8 +34,12 @@
 }
 
 func init() {
-	android.RegisterModuleType("android_library_import", AARImportFactory)
-	android.RegisterModuleType("android_library", AndroidLibraryFactory)
+	RegisterAARBuildComponents(android.InitRegistrationContext)
+}
+
+func RegisterAARBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("android_library_import", AARImportFactory)
+	ctx.RegisterModuleType("android_library", AndroidLibraryFactory)
 }
 
 //
@@ -68,6 +73,12 @@
 
 	// path to AndroidManifest.xml.  If unset, defaults to "AndroidManifest.xml".
 	Manifest *string `android:"path"`
+
+	// paths to additional manifest files to merge with main manifest.
+	Additional_manifests []string `android:"path"`
+
+	// do not include AndroidManifest from dependent libraries
+	Dont_merge_manifests *bool
 }
 
 type aapt struct {
@@ -80,6 +91,7 @@
 	rTxt                    android.Path
 	extraAaptPackagesFile   android.Path
 	mergedManifestFile      android.Path
+	noticeFile              android.OptionalPath
 	isLibrary               bool
 	useEmbeddedNativeLibs   bool
 	useEmbeddedDex          bool
@@ -111,8 +123,9 @@
 	return a.transitiveManifestPaths
 }
 
-func (a *aapt) aapt2Flags(ctx android.ModuleContext, sdkContext sdkContext, manifestPath android.Path) (flags []string,
-	deps android.Paths, resDirs, overlayDirs []globbedResourceDir, rroDirs []rroDir, resZips android.Paths) {
+func (a *aapt) aapt2Flags(ctx android.ModuleContext, sdkContext sdkContext,
+	manifestPath android.Path) (compileFlags, linkFlags []string, linkDeps android.Paths,
+	resDirs, overlayDirs []globbedResourceDir, rroDirs []rroDir, resZips android.Paths) {
 
 	hasVersionCode := false
 	hasVersionName := false
@@ -124,8 +137,6 @@
 		}
 	}
 
-	var linkFlags []string
-
 	// Flags specified in Android.bp
 	linkFlags = append(linkFlags, a.aaptProperties.Aaptflags...)
 
@@ -136,8 +147,6 @@
 	resourceDirs := android.PathsWithOptionalDefaultForModuleSrc(ctx, a.aaptProperties.Resource_dirs, "res")
 	resourceZips := android.PathsForModuleSrc(ctx, a.aaptProperties.Resource_zips)
 
-	var linkDeps android.Paths
-
 	// Glob directories into lists of paths
 	for _, dir := range resourceDirs {
 		resDirs = append(resDirs, globbedResourceDir{
@@ -154,10 +163,16 @@
 		assetFiles = append(assetFiles, androidResourceGlob(ctx, dir)...)
 	}
 
+	assetDirStrings := assetDirs.Strings()
+	if a.noticeFile.Valid() {
+		assetDirStrings = append(assetDirStrings, filepath.Dir(a.noticeFile.Path().String()))
+		assetFiles = append(assetFiles, a.noticeFile.Path())
+	}
+
 	linkFlags = append(linkFlags, "--manifest "+manifestPath.String())
 	linkDeps = append(linkDeps, manifestPath)
 
-	linkFlags = append(linkFlags, android.JoinWithPrefix(assetDirs.Strings(), "-A "))
+	linkFlags = append(linkFlags, android.JoinWithPrefix(assetDirStrings, "-A "))
 	linkDeps = append(linkDeps, assetFiles...)
 
 	// SDK version flags
@@ -185,7 +200,13 @@
 		linkFlags = append(linkFlags, "--version-name ", versionName)
 	}
 
-	return linkFlags, linkDeps, resDirs, overlayDirs, rroDirs, resourceZips
+	linkFlags, compileFlags = android.FilterList(linkFlags, []string{"--legacy"})
+
+	// Always set --pseudo-localize, it will be stripped out later for release
+	// builds that don't want it.
+	compileFlags = append(compileFlags, "--pseudo-localize")
+
+	return compileFlags, linkFlags, linkDeps, resDirs, overlayDirs, rroDirs, resourceZips
 }
 
 func (a *aapt) deps(ctx android.BottomUpMutatorContext, sdkDep sdkDep) {
@@ -206,10 +227,13 @@
 	manifestPath := manifestFixer(ctx, manifestSrcPath, sdkContext, sdkLibraries,
 		a.isLibrary, a.useEmbeddedNativeLibs, a.usesNonSdkApis, a.useEmbeddedDex, a.hasNoCode)
 
-	a.transitiveManifestPaths = append(android.Paths{manifestPath}, transitiveStaticLibManifests...)
+	// Add additional manifest files to transitive manifests.
+	additionalManifests := android.PathsForModuleSrc(ctx, a.aaptProperties.Additional_manifests)
+	a.transitiveManifestPaths = append(android.Paths{manifestPath}, additionalManifests...)
+	a.transitiveManifestPaths = append(a.transitiveManifestPaths, transitiveStaticLibManifests...)
 
-	if len(transitiveStaticLibManifests) > 0 {
-		a.mergedManifestFile = manifestMerger(ctx, manifestPath, transitiveStaticLibManifests, a.isLibrary)
+	if len(a.transitiveManifestPaths) > 1 && !Bool(a.aaptProperties.Dont_merge_manifests) {
+		a.mergedManifestFile = manifestMerger(ctx, a.transitiveManifestPaths[0], a.transitiveManifestPaths[1:], a.isLibrary)
 		if !a.isLibrary {
 			// Only use the merged manifest for applications.  For libraries, the transitive closure of manifests
 			// will be propagated to the final application and merged there.  The merged manifest for libraries is
@@ -220,7 +244,7 @@
 		a.mergedManifestFile = manifestPath
 	}
 
-	linkFlags, linkDeps, resDirs, overlayDirs, rroDirs, resZips := a.aapt2Flags(ctx, sdkContext, manifestPath)
+	compileFlags, linkFlags, linkDeps, resDirs, overlayDirs, rroDirs, resZips := a.aapt2Flags(ctx, sdkContext, manifestPath)
 
 	rroDirs = append(rroDirs, staticRRODirs...)
 	linkFlags = append(linkFlags, libFlags...)
@@ -231,7 +255,8 @@
 	}
 
 	packageRes := android.PathForModuleOut(ctx, "package-res.apk")
-	srcJar := android.PathForModuleGen(ctx, "R.jar")
+	// the subdir "android" is required to be filtered by package names
+	srcJar := android.PathForModuleGen(ctx, "android", "R.srcjar")
 	proguardOptionsFile := android.PathForModuleGen(ctx, "proguard.options")
 	rTxt := android.PathForModuleOut(ctx, "R.txt")
 	// This file isn't used by Soong, but is generated for exporting
@@ -239,12 +264,12 @@
 
 	var compiledResDirs []android.Paths
 	for _, dir := range resDirs {
-		compiledResDirs = append(compiledResDirs, aapt2Compile(ctx, dir.dir, dir.files).Paths())
+		compiledResDirs = append(compiledResDirs, aapt2Compile(ctx, dir.dir, dir.files, compileFlags).Paths())
 	}
 
 	for i, zip := range resZips {
 		flata := android.PathForModuleOut(ctx, fmt.Sprintf("reszip.%d.flata", i))
-		aapt2CompileZip(ctx, flata, zip, "")
+		aapt2CompileZip(ctx, flata, zip, "", compileFlags)
 		compiledResDirs = append(compiledResDirs, android.Paths{flata})
 	}
 
@@ -273,7 +298,7 @@
 	}
 
 	for _, dir := range overlayDirs {
-		compiledOverlay = append(compiledOverlay, aapt2Compile(ctx, dir.dir, dir.files).Paths()...)
+		compiledOverlay = append(compiledOverlay, aapt2Compile(ctx, dir.dir, dir.files, compileFlags).Paths()...)
 	}
 
 	var splitPackages android.WritablePaths
@@ -502,6 +527,10 @@
 	return String(a.properties.Sdk_version)
 }
 
+func (a *AARImport) systemModules() string {
+	return ""
+}
+
 func (a *AARImport) minSdkVersion() string {
 	if a.properties.Min_sdk_version != nil {
 		return *a.properties.Min_sdk_version
@@ -513,8 +542,8 @@
 	return a.sdkVersion()
 }
 
-func (a *AARImport) noFrameworkLibs() bool {
-	return false
+func (a *AARImport) javaVersion() string {
+	return ""
 }
 
 var _ AndroidLibraryDependency = (*AARImport)(nil)
@@ -547,6 +576,10 @@
 	return a.prebuilt.Name(a.ModuleBase.Name())
 }
 
+func (a *AARImport) JacocoReportClassesFile() android.Path {
+	return nil
+}
+
 func (a *AARImport) DepsMutator(ctx android.BottomUpMutatorContext) {
 	if !ctx.Config().UnbundledBuildUsePrebuiltSdks() {
 		sdkDep := decodeSdkDep(ctx, sdkContext(a))
@@ -598,12 +631,16 @@
 		},
 	})
 
+	// Always set --pseudo-localize, it will be stripped out later for release
+	// builds that don't want it.
+	compileFlags := []string{"--pseudo-localize"}
 	compiledResDir := android.PathForModuleOut(ctx, "flat-res")
 	flata := compiledResDir.Join(ctx, "gen_res.flata")
-	aapt2CompileZip(ctx, flata, aar, "res")
+	aapt2CompileZip(ctx, flata, aar, "res", compileFlags)
 
 	a.exportPackage = android.PathForModuleOut(ctx, "package-res.apk")
-	srcJar := android.PathForModuleGen(ctx, "R.jar")
+	// the subdir "android" is required to be filtered by package names
+	srcJar := android.PathForModuleGen(ctx, "android", "R.srcjar")
 	proguardOptionsFile := android.PathForModuleGen(ctx, "proguard.options")
 	rTxt := android.PathForModuleOut(ctx, "R.txt")
 	a.extraAaptPackagesFile = android.PathForModuleOut(ctx, "extra_packages")
@@ -665,6 +702,10 @@
 	return nil
 }
 
+func (d *AARImport) ExportedPlugins() (android.Paths, []string) {
+	return nil, nil
+}
+
 func (a *AARImport) SrcJarArgs() ([]string, android.Paths) {
 	return nil, nil
 }
diff --git a/java/androidmk.go b/java/androidmk.go
index 39c2d13..04bf15c 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -17,269 +17,302 @@
 import (
 	"fmt"
 	"io"
-	"strings"
 
 	"android/soong/android"
 )
 
-func (library *Library) AndroidMkHostDex(w io.Writer, name string, data android.AndroidMkData) {
-	if Bool(library.deviceProperties.Hostdex) && !library.Host() {
-		fmt.Fprintln(w, "include $(CLEAR_VARS)")
-		fmt.Fprintln(w, "LOCAL_MODULE := "+name+"-hostdex")
-		fmt.Fprintln(w, "LOCAL_IS_HOST_MODULE := true")
-		fmt.Fprintln(w, "LOCAL_MODULE_CLASS := JAVA_LIBRARIES")
-		if library.dexJarFile != nil {
-			fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", library.dexJarFile.String())
-		} else {
-			fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", library.implementationAndResourcesJar.String())
-		}
-		if library.dexJarFile != nil {
-			fmt.Fprintln(w, "LOCAL_SOONG_DEX_JAR :=", library.dexJarFile.String())
-		}
-		fmt.Fprintln(w, "LOCAL_SOONG_HEADER_JAR :=", library.headerJarFile.String())
-		fmt.Fprintln(w, "LOCAL_SOONG_CLASSES_JAR :=", library.implementationAndResourcesJar.String())
-		if len(data.Required) > 0 {
-			fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES :=", strings.Join(data.Required, " "))
-		}
-		if len(data.Host_required) > 0 {
-			fmt.Fprintln(w, "LOCAL_HOST_REQUIRED_MODULES :=", strings.Join(data.Host_required, " "))
-		}
-		if len(data.Target_required) > 0 {
-			fmt.Fprintln(w, "LOCAL_TARGET_REQUIRED_MODULES :=", strings.Join(data.Target_required, " "))
-		}
-		if r := library.deviceProperties.Target.Hostdex.Required; len(r) > 0 {
-			fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES +=", strings.Join(r, " "))
-		}
-		fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_java_prebuilt.mk")
+func (library *Library) AndroidMkEntriesHostDex() android.AndroidMkEntries {
+	hostDexNeeded := Bool(library.deviceProperties.Hostdex) && !library.Host()
+	if !library.IsForPlatform() {
+		// Don't emit hostdex modules from the APEX variants
+		hostDexNeeded = false
 	}
+
+	if hostDexNeeded {
+		var output android.Path
+		if library.dexJarFile != nil {
+			output = library.dexJarFile
+		} else {
+			output = library.implementationAndResourcesJar
+		}
+		return android.AndroidMkEntries{
+			Class:      "JAVA_LIBRARIES",
+			SubName:    "-hostdex",
+			OutputFile: android.OptionalPathForPath(output),
+			Required:   library.deviceProperties.Target.Hostdex.Required,
+			Include:    "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
+			ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+				func(entries *android.AndroidMkEntries) {
+					entries.SetBool("LOCAL_IS_HOST_MODULE", true)
+					entries.SetPath("LOCAL_PREBUILT_MODULE_FILE", output)
+					if library.dexJarFile != nil {
+						entries.SetPath("LOCAL_SOONG_DEX_JAR", library.dexJarFile)
+					}
+					entries.SetPath("LOCAL_SOONG_HEADER_JAR", library.headerJarFile)
+					entries.SetPath("LOCAL_SOONG_CLASSES_JAR", library.implementationAndResourcesJar)
+					entries.SetString("LOCAL_MODULE_STEM", library.Stem()+"-hostdex")
+				},
+			},
+		}
+	}
+	return android.AndroidMkEntries{Disabled: true}
 }
 
-func (library *Library) AndroidMk() android.AndroidMkData {
-	return android.AndroidMkData{
-		Class:      "JAVA_LIBRARIES",
-		OutputFile: android.OptionalPathForPath(library.outputFile),
-		Include:    "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
-		Extra: []android.AndroidMkExtraFunc{
-			func(w io.Writer, outputFile android.Path) {
-				if len(library.logtagsSrcs) > 0 {
-					var logtags []string
-					for _, l := range library.logtagsSrcs {
-						logtags = append(logtags, l.Rel())
-					}
-					fmt.Fprintln(w, "LOCAL_LOGTAGS_FILES :=", strings.Join(logtags, " "))
-				}
+func (library *Library) AndroidMkEntries() []android.AndroidMkEntries {
+	var entriesList []android.AndroidMkEntries
 
-				if library.installFile == nil {
-					fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := true")
-				}
-				if library.dexJarFile != nil {
-					fmt.Fprintln(w, "LOCAL_SOONG_DEX_JAR :=", library.dexJarFile.String())
-				}
-				if len(library.dexpreopter.builtInstalled) > 0 {
-					fmt.Fprintln(w, "LOCAL_SOONG_BUILT_INSTALLED :=", library.dexpreopter.builtInstalled)
-				}
-				fmt.Fprintln(w, "LOCAL_SDK_VERSION :=", library.sdkVersion())
-				fmt.Fprintln(w, "LOCAL_SOONG_CLASSES_JAR :=", library.implementationAndResourcesJar.String())
-				fmt.Fprintln(w, "LOCAL_SOONG_HEADER_JAR :=", library.headerJarFile.String())
+	mainEntries := android.AndroidMkEntries{Disabled: true}
 
-				if library.jacocoReportClassesFile != nil {
-					fmt.Fprintln(w, "LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR :=", library.jacocoReportClassesFile.String())
-				}
-
-				if len(library.exportedSdkLibs) != 0 {
-					fmt.Fprintln(w, "LOCAL_EXPORT_SDK_LIBRARIES :=", strings.Join(library.exportedSdkLibs, " "))
-				}
-
-				if len(library.additionalCheckedModules) != 0 {
-					fmt.Fprintln(w, "LOCAL_ADDITIONAL_CHECKED_MODULE +=", strings.Join(library.additionalCheckedModules.Strings(), " "))
-				}
-
-				if library.proguardDictionary != nil {
-					fmt.Fprintln(w, "LOCAL_SOONG_PROGUARD_DICT :=", library.proguardDictionary.String())
-				}
-			},
-		},
-		Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
-			android.WriteAndroidMkData(w, data)
-			library.AndroidMkHostDex(w, name, data)
-		},
+	// For a java library built for an APEX, we don't need Make module
+	hideFromMake := !library.IsForPlatform()
+	// If not available for platform, don't emit to make.
+	if !library.ApexModuleBase.AvailableFor(android.AvailableToPlatform) {
+		hideFromMake = true
 	}
+	if !hideFromMake {
+		mainEntries = android.AndroidMkEntries{
+			Class:      "JAVA_LIBRARIES",
+			OutputFile: android.OptionalPathForPath(library.outputFile),
+			Include:    "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
+			ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+				func(entries *android.AndroidMkEntries) {
+					if len(library.logtagsSrcs) > 0 {
+						var logtags []string
+						for _, l := range library.logtagsSrcs {
+							logtags = append(logtags, l.Rel())
+						}
+						entries.AddStrings("LOCAL_LOGTAGS_FILES", logtags...)
+					}
+
+					if library.installFile == nil {
+						entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", true)
+					}
+					if library.dexJarFile != nil {
+						entries.SetPath("LOCAL_SOONG_DEX_JAR", library.dexJarFile)
+					}
+					if len(library.dexpreopter.builtInstalled) > 0 {
+						entries.SetString("LOCAL_SOONG_BUILT_INSTALLED", library.dexpreopter.builtInstalled)
+					}
+					entries.SetString("LOCAL_SDK_VERSION", library.sdkVersion())
+					entries.SetPath("LOCAL_SOONG_CLASSES_JAR", library.implementationAndResourcesJar)
+					entries.SetPath("LOCAL_SOONG_HEADER_JAR", library.headerJarFile)
+
+					if library.jacocoReportClassesFile != nil {
+						entries.SetPath("LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR", library.jacocoReportClassesFile)
+					}
+
+					entries.AddStrings("LOCAL_EXPORT_SDK_LIBRARIES", library.exportedSdkLibs...)
+
+					if len(library.additionalCheckedModules) != 0 {
+						entries.AddStrings("LOCAL_ADDITIONAL_CHECKED_MODULE", library.additionalCheckedModules.Strings()...)
+					}
+
+					if library.proguardDictionary != nil {
+						entries.SetPath("LOCAL_SOONG_PROGUARD_DICT", library.proguardDictionary)
+					}
+					entries.SetString("LOCAL_MODULE_STEM", library.Stem())
+				},
+			},
+		}
+	}
+
+	hostDexEntries := library.AndroidMkEntriesHostDex()
+
+	entriesList = append(entriesList, mainEntries, hostDexEntries)
+	return entriesList
 }
 
 // Called for modules that are a component of a test suite.
-func testSuiteComponent(w io.Writer, test_suites []string) {
-	fmt.Fprintln(w, "LOCAL_MODULE_TAGS := tests")
+func testSuiteComponent(entries *android.AndroidMkEntries, test_suites []string) {
+	entries.SetString("LOCAL_MODULE_TAGS", "tests")
 	if len(test_suites) > 0 {
-		fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE :=",
-			strings.Join(test_suites, " "))
+		entries.AddStrings("LOCAL_COMPATIBILITY_SUITE", test_suites...)
 	} else {
-		fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE := null-suite")
+		entries.SetString("LOCAL_COMPATIBILITY_SUITE", "null-suite")
 	}
 }
 
-func (j *Test) AndroidMk() android.AndroidMkData {
-	data := j.Library.AndroidMk()
-	data.Extra = append(data.Extra, func(w io.Writer, outputFile android.Path) {
-		testSuiteComponent(w, j.testProperties.Test_suites)
+func (j *Test) AndroidMkEntries() []android.AndroidMkEntries {
+	entriesList := j.Library.AndroidMkEntries()
+	entries := &entriesList[0]
+	entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+		testSuiteComponent(entries, j.testProperties.Test_suites)
 		if j.testConfig != nil {
-			fmt.Fprintln(w, "LOCAL_FULL_TEST_CONFIG :=", j.testConfig.String())
+			entries.SetPath("LOCAL_FULL_TEST_CONFIG", j.testConfig)
+		}
+		androidMkWriteTestData(j.data, entries)
+		if !BoolDefault(j.testProperties.Auto_gen_config, true) {
+			entries.SetString("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", "true")
 		}
 	})
 
-	androidMkWriteTestData(j.data, &data)
-
-	return data
+	return entriesList
 }
 
-func (j *TestHelperLibrary) AndroidMk() android.AndroidMkData {
-	data := j.Library.AndroidMk()
-	data.Extra = append(data.Extra, func(w io.Writer, outputFile android.Path) {
-		testSuiteComponent(w, j.testHelperLibraryProperties.Test_suites)
+func (j *TestHelperLibrary) AndroidMkEntries() []android.AndroidMkEntries {
+	entriesList := j.Library.AndroidMkEntries()
+	entries := &entriesList[0]
+	entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+		testSuiteComponent(entries, j.testHelperLibraryProperties.Test_suites)
 	})
 
-	return data
+	return entriesList
 }
 
-func (prebuilt *Import) AndroidMk() android.AndroidMkData {
-	return android.AndroidMkData{
+func (prebuilt *Import) AndroidMkEntries() []android.AndroidMkEntries {
+	if !prebuilt.IsForPlatform() || !prebuilt.ContainingSdk().Unversioned() {
+		return []android.AndroidMkEntries{android.AndroidMkEntries{
+			Disabled: true,
+		}}
+	}
+	return []android.AndroidMkEntries{android.AndroidMkEntries{
 		Class:      "JAVA_LIBRARIES",
 		OutputFile: android.OptionalPathForPath(prebuilt.combinedClasspathFile),
 		Include:    "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
-		Extra: []android.AndroidMkExtraFunc{
-			func(w io.Writer, outputFile android.Path) {
-				fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := ", !Bool(prebuilt.properties.Installable))
-				fmt.Fprintln(w, "LOCAL_SOONG_HEADER_JAR :=", prebuilt.combinedClasspathFile.String())
-				fmt.Fprintln(w, "LOCAL_SOONG_CLASSES_JAR :=", prebuilt.combinedClasspathFile.String())
-				fmt.Fprintln(w, "LOCAL_SDK_VERSION :=", prebuilt.sdkVersion())
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(entries *android.AndroidMkEntries) {
+				entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", !Bool(prebuilt.properties.Installable))
+				entries.SetPath("LOCAL_SOONG_HEADER_JAR", prebuilt.combinedClasspathFile)
+				entries.SetPath("LOCAL_SOONG_CLASSES_JAR", prebuilt.combinedClasspathFile)
+				entries.SetString("LOCAL_SDK_VERSION", prebuilt.sdkVersion())
+				entries.SetString("LOCAL_MODULE_STEM", prebuilt.Stem())
 			},
 		},
-	}
+	}}
 }
 
-func (prebuilt *DexImport) AndroidMk() android.AndroidMkData {
-	return android.AndroidMkData{
+func (prebuilt *DexImport) AndroidMkEntries() []android.AndroidMkEntries {
+	if !prebuilt.IsForPlatform() {
+		return []android.AndroidMkEntries{android.AndroidMkEntries{
+			Disabled: true,
+		}}
+	}
+	return []android.AndroidMkEntries{android.AndroidMkEntries{
 		Class:      "JAVA_LIBRARIES",
 		OutputFile: android.OptionalPathForPath(prebuilt.maybeStrippedDexJarFile),
 		Include:    "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
-		Extra: []android.AndroidMkExtraFunc{
-			func(w io.Writer, outputFile android.Path) {
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(entries *android.AndroidMkEntries) {
 				if prebuilt.dexJarFile != nil {
-					fmt.Fprintln(w, "LOCAL_SOONG_DEX_JAR :=", prebuilt.dexJarFile.String())
+					entries.SetPath("LOCAL_SOONG_DEX_JAR", prebuilt.dexJarFile)
 					// TODO(b/125517186): export the dex jar as a classes jar to match some mis-uses in Make until
 					// boot_jars_package_check.mk can check dex jars.
-					fmt.Fprintln(w, "LOCAL_SOONG_HEADER_JAR :=", prebuilt.dexJarFile.String())
-					fmt.Fprintln(w, "LOCAL_SOONG_CLASSES_JAR :=", prebuilt.dexJarFile.String())
+					entries.SetPath("LOCAL_SOONG_HEADER_JAR", prebuilt.dexJarFile)
+					entries.SetPath("LOCAL_SOONG_CLASSES_JAR", prebuilt.dexJarFile)
 				}
 				if len(prebuilt.dexpreopter.builtInstalled) > 0 {
-					fmt.Fprintln(w, "LOCAL_SOONG_BUILT_INSTALLED :=", prebuilt.dexpreopter.builtInstalled)
+					entries.SetString("LOCAL_SOONG_BUILT_INSTALLED", prebuilt.dexpreopter.builtInstalled)
 				}
+				entries.SetString("LOCAL_MODULE_STEM", prebuilt.Stem())
 			},
 		},
-	}
+	}}
 }
 
-func (prebuilt *AARImport) AndroidMk() android.AndroidMkData {
-	return android.AndroidMkData{
+func (prebuilt *AARImport) AndroidMkEntries() []android.AndroidMkEntries {
+	return []android.AndroidMkEntries{android.AndroidMkEntries{
 		Class:      "JAVA_LIBRARIES",
 		OutputFile: android.OptionalPathForPath(prebuilt.classpathFile),
 		Include:    "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
-		Extra: []android.AndroidMkExtraFunc{
-			func(w io.Writer, outputFile android.Path) {
-				fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := true")
-				fmt.Fprintln(w, "LOCAL_SOONG_HEADER_JAR :=", prebuilt.classpathFile.String())
-				fmt.Fprintln(w, "LOCAL_SOONG_CLASSES_JAR :=", prebuilt.classpathFile.String())
-				fmt.Fprintln(w, "LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE :=", prebuilt.exportPackage.String())
-				fmt.Fprintln(w, "LOCAL_SOONG_EXPORT_PROGUARD_FLAGS :=", prebuilt.proguardFlags.String())
-				fmt.Fprintln(w, "LOCAL_SOONG_STATIC_LIBRARY_EXTRA_PACKAGES :=", prebuilt.extraAaptPackagesFile.String())
-				fmt.Fprintln(w, "LOCAL_FULL_MANIFEST_FILE :=", prebuilt.manifest.String())
-				fmt.Fprintln(w, "LOCAL_SDK_VERSION :=", prebuilt.sdkVersion())
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(entries *android.AndroidMkEntries) {
+				entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
+				entries.SetPath("LOCAL_SOONG_HEADER_JAR", prebuilt.classpathFile)
+				entries.SetPath("LOCAL_SOONG_CLASSES_JAR", prebuilt.classpathFile)
+				entries.SetPath("LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE", prebuilt.exportPackage)
+				entries.SetPath("LOCAL_SOONG_EXPORT_PROGUARD_FLAGS", prebuilt.proguardFlags)
+				entries.SetPath("LOCAL_SOONG_STATIC_LIBRARY_EXTRA_PACKAGES", prebuilt.extraAaptPackagesFile)
+				entries.SetPath("LOCAL_FULL_MANIFEST_FILE", prebuilt.manifest)
+				entries.SetString("LOCAL_SDK_VERSION", prebuilt.sdkVersion())
 			},
 		},
-	}
+	}}
 }
 
-func (binary *Binary) AndroidMk() android.AndroidMkData {
+func (binary *Binary) AndroidMkEntries() []android.AndroidMkEntries {
 
 	if !binary.isWrapperVariant {
-		return android.AndroidMkData{
+		return []android.AndroidMkEntries{android.AndroidMkEntries{
 			Class:      "JAVA_LIBRARIES",
 			OutputFile: android.OptionalPathForPath(binary.outputFile),
 			Include:    "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
-			Extra: []android.AndroidMkExtraFunc{
-				func(w io.Writer, outputFile android.Path) {
-					fmt.Fprintln(w, "LOCAL_SOONG_HEADER_JAR :=", binary.headerJarFile.String())
-					fmt.Fprintln(w, "LOCAL_SOONG_CLASSES_JAR :=", binary.implementationAndResourcesJar.String())
+			ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+				func(entries *android.AndroidMkEntries) {
+					entries.SetPath("LOCAL_SOONG_HEADER_JAR", binary.headerJarFile)
+					entries.SetPath("LOCAL_SOONG_CLASSES_JAR", binary.implementationAndResourcesJar)
 					if binary.dexJarFile != nil {
-						fmt.Fprintln(w, "LOCAL_SOONG_DEX_JAR :=", binary.dexJarFile.String())
+						entries.SetPath("LOCAL_SOONG_DEX_JAR", binary.dexJarFile)
 					}
 					if len(binary.dexpreopter.builtInstalled) > 0 {
-						fmt.Fprintln(w, "LOCAL_SOONG_BUILT_INSTALLED :=", binary.dexpreopter.builtInstalled)
+						entries.SetString("LOCAL_SOONG_BUILT_INSTALLED", binary.dexpreopter.builtInstalled)
 					}
 				},
 			},
-			Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
-				android.WriteAndroidMkData(w, data)
-
-				fmt.Fprintln(w, "jar_installed_module := $(LOCAL_INSTALLED_MODULE)")
-			},
-		}
-	} else {
-		return android.AndroidMkData{
-			Class:      "EXECUTABLES",
-			OutputFile: android.OptionalPathForPath(binary.wrapperFile),
-			Extra: []android.AndroidMkExtraFunc{
-				func(w io.Writer, outputFile android.Path) {
-					fmt.Fprintln(w, "LOCAL_STRIP_MODULE := false")
+			ExtraFooters: []android.AndroidMkExtraFootersFunc{
+				func(w io.Writer, name, prefix, moduleDir string, entries *android.AndroidMkEntries) {
+					fmt.Fprintln(w, "jar_installed_module := $(LOCAL_INSTALLED_MODULE)")
 				},
 			},
-			Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
-				android.WriteAndroidMkData(w, data)
-
-				// Ensure that the wrapper script timestamp is always updated when the jar is updated
-				fmt.Fprintln(w, "$(LOCAL_INSTALLED_MODULE): $(jar_installed_module)")
-				fmt.Fprintln(w, "jar_installed_module :=")
+		}}
+	} else {
+		return []android.AndroidMkEntries{android.AndroidMkEntries{
+			Class:      "EXECUTABLES",
+			OutputFile: android.OptionalPathForPath(binary.wrapperFile),
+			ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+				func(entries *android.AndroidMkEntries) {
+					entries.SetBool("LOCAL_STRIP_MODULE", false)
+				},
 			},
-		}
+			ExtraFooters: []android.AndroidMkExtraFootersFunc{
+				func(w io.Writer, name, prefix, moduleDir string, entries *android.AndroidMkEntries) {
+					// Ensure that the wrapper script timestamp is always updated when the jar is updated
+					fmt.Fprintln(w, "$(LOCAL_INSTALLED_MODULE): $(jar_installed_module)")
+					fmt.Fprintln(w, "jar_installed_module :=")
+				},
+			},
+		}}
 	}
 }
 
-func (app *AndroidApp) AndroidMk() android.AndroidMkData {
-	return android.AndroidMkData{
+func (app *AndroidApp) AndroidMkEntries() []android.AndroidMkEntries {
+	if !app.IsForPlatform() {
+		return []android.AndroidMkEntries{android.AndroidMkEntries{
+			Disabled: true,
+		}}
+	}
+	return []android.AndroidMkEntries{android.AndroidMkEntries{
 		Class:      "APPS",
 		OutputFile: android.OptionalPathForPath(app.outputFile),
 		Include:    "$(BUILD_SYSTEM)/soong_app_prebuilt.mk",
-		Extra: []android.AndroidMkExtraFunc{
-			func(w io.Writer, outputFile android.Path) {
-				// TODO(jungjw): This, outputting two LOCAL_MODULE lines, works, but is not ideal. Find a better solution.
-				if app.Name() != app.installApkName {
-					fmt.Fprintln(w, "# Overridden by PRODUCT_PACKAGE_NAME_OVERRIDES")
-					fmt.Fprintln(w, "LOCAL_MODULE :=", app.installApkName)
-				}
-				fmt.Fprintln(w, "LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE :=", app.exportPackage.String())
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(entries *android.AndroidMkEntries) {
+				// App module names can be overridden.
+				entries.SetString("LOCAL_MODULE", app.installApkName)
+				entries.SetPath("LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE", app.exportPackage)
 				if app.dexJarFile != nil {
-					fmt.Fprintln(w, "LOCAL_SOONG_DEX_JAR :=", app.dexJarFile.String())
+					entries.SetPath("LOCAL_SOONG_DEX_JAR", app.dexJarFile)
 				}
 				if app.implementationAndResourcesJar != nil {
-					fmt.Fprintln(w, "LOCAL_SOONG_CLASSES_JAR :=", app.implementationAndResourcesJar.String())
+					entries.SetPath("LOCAL_SOONG_CLASSES_JAR", app.implementationAndResourcesJar)
 				}
 				if app.headerJarFile != nil {
-					fmt.Fprintln(w, "LOCAL_SOONG_HEADER_JAR :=", app.headerJarFile.String())
+					entries.SetPath("LOCAL_SOONG_HEADER_JAR", app.headerJarFile)
 				}
 				if app.bundleFile != nil {
-					fmt.Fprintln(w, "LOCAL_SOONG_BUNDLE :=", app.bundleFile.String())
+					entries.SetPath("LOCAL_SOONG_BUNDLE", app.bundleFile)
 				}
 				if app.jacocoReportClassesFile != nil {
-					fmt.Fprintln(w, "LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR :=", app.jacocoReportClassesFile.String())
+					entries.SetPath("LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR", app.jacocoReportClassesFile)
 				}
 				if app.proguardDictionary != nil {
-					fmt.Fprintln(w, "LOCAL_SOONG_PROGUARD_DICT :=", app.proguardDictionary.String())
+					entries.SetPath("LOCAL_SOONG_PROGUARD_DICT", app.proguardDictionary)
 				}
 
 				if app.Name() == "framework-res" {
-					fmt.Fprintln(w, "LOCAL_MODULE_PATH := $(TARGET_OUT_JAVA_LIBRARIES)")
+					entries.SetString("LOCAL_MODULE_PATH", "$(TARGET_OUT_JAVA_LIBRARIES)")
 					// Make base_rules.mk not put framework-res in a subdirectory called
 					// framework_res.
-					fmt.Fprintln(w, "LOCAL_NO_STANDARD_LIBRARIES := true")
+					entries.SetBoolIfTrue("LOCAL_NO_STANDARD_LIBRARIES", true)
 				}
 
 				filterRRO := func(filter overlayType) android.Paths {
@@ -295,41 +328,51 @@
 				}
 				deviceRRODirs := filterRRO(device)
 				if len(deviceRRODirs) > 0 {
-					fmt.Fprintln(w, "LOCAL_SOONG_DEVICE_RRO_DIRS :=", strings.Join(deviceRRODirs.Strings(), " "))
+					entries.AddStrings("LOCAL_SOONG_DEVICE_RRO_DIRS", deviceRRODirs.Strings()...)
 				}
 				productRRODirs := filterRRO(product)
 				if len(productRRODirs) > 0 {
-					fmt.Fprintln(w, "LOCAL_SOONG_PRODUCT_RRO_DIRS :=", strings.Join(productRRODirs.Strings(), " "))
+					entries.AddStrings("LOCAL_SOONG_PRODUCT_RRO_DIRS", productRRODirs.Strings()...)
 				}
 
-				if Bool(app.appProperties.Export_package_resources) {
-					fmt.Fprintln(w, "LOCAL_EXPORT_PACKAGE_RESOURCES := true")
-				}
+				entries.SetBoolIfTrue("LOCAL_EXPORT_PACKAGE_RESOURCES", Bool(app.appProperties.Export_package_resources))
 
-				fmt.Fprintln(w, "LOCAL_FULL_MANIFEST_FILE :=", app.manifestPath.String())
+				entries.SetPath("LOCAL_FULL_MANIFEST_FILE", app.manifestPath)
 
-				if Bool(app.appProperties.Privileged) {
-					fmt.Fprintln(w, "LOCAL_PRIVILEGED_MODULE := true")
-				}
+				entries.SetBoolIfTrue("LOCAL_PRIVILEGED_MODULE", app.Privileged())
 
-				fmt.Fprintln(w, "LOCAL_CERTIFICATE :=", app.certificate.Pem.String())
-				if overriddenPkgs := app.getOverriddenPackages(); len(overriddenPkgs) > 0 {
-					fmt.Fprintln(w, "LOCAL_OVERRIDES_PACKAGES :=", strings.Join(overriddenPkgs, " "))
-				}
+				entries.SetPath("LOCAL_CERTIFICATE", app.certificate.Pem)
+				entries.AddStrings("LOCAL_OVERRIDES_PACKAGES", app.getOverriddenPackages()...)
 
 				for _, jniLib := range app.installJniLibs {
-					fmt.Fprintln(w, "LOCAL_SOONG_JNI_LIBS_"+jniLib.target.Arch.ArchType.String(), "+=", jniLib.name)
+					entries.AddStrings("LOCAL_SOONG_JNI_LIBS_"+jniLib.target.Arch.ArchType.String(), jniLib.name)
 				}
 				if len(app.dexpreopter.builtInstalled) > 0 {
-					fmt.Fprintln(w, "LOCAL_SOONG_BUILT_INSTALLED :=", app.dexpreopter.builtInstalled)
+					entries.SetString("LOCAL_SOONG_BUILT_INSTALLED", app.dexpreopter.builtInstalled)
 				}
-				for _, split := range app.aapt.splits {
-					install := "$(LOCAL_MODULE_PATH)/" + strings.TrimSuffix(app.installApkName, ".apk") + split.suffix + ".apk"
-					fmt.Fprintln(w, "LOCAL_SOONG_BUILT_INSTALLED +=", split.path.String()+":"+install)
+				for _, extra := range app.extraOutputFiles {
+					install := app.onDeviceDir + "/" + extra.Base()
+					entries.AddStrings("LOCAL_SOONG_BUILT_INSTALLED", extra.String()+":"+install)
 				}
 			},
 		},
-	}
+		ExtraFooters: []android.AndroidMkExtraFootersFunc{
+			func(w io.Writer, name, prefix, moduleDir string, entries *android.AndroidMkEntries) {
+				if app.noticeOutputs.Merged.Valid() {
+					fmt.Fprintf(w, "$(call dist-for-goals,%s,%s:%s)\n",
+						app.installApkName, app.noticeOutputs.Merged.String(), app.installApkName+"_NOTICE")
+				}
+				if app.noticeOutputs.TxtOutput.Valid() {
+					fmt.Fprintf(w, "$(call dist-for-goals,%s,%s:%s)\n",
+						app.installApkName, app.noticeOutputs.TxtOutput.String(), app.installApkName+"_NOTICE.txt")
+				}
+				if app.noticeOutputs.HtmlOutput.Valid() {
+					fmt.Fprintf(w, "$(call dist-for-goals,%s,%s:%s)\n",
+						app.installApkName, app.noticeOutputs.HtmlOutput.String(), app.installApkName+"_NOTICE.html")
+				}
+			},
+		},
+	}}
 }
 
 func (a *AndroidApp) getOverriddenPackages() []string {
@@ -343,85 +386,119 @@
 	return overridden
 }
 
-func (a *AndroidTest) AndroidMk() android.AndroidMkData {
-	data := a.AndroidApp.AndroidMk()
-	data.Extra = append(data.Extra, func(w io.Writer, outputFile android.Path) {
-		testSuiteComponent(w, a.testProperties.Test_suites)
+func (a *AndroidTest) AndroidMkEntries() []android.AndroidMkEntries {
+	entriesList := a.AndroidApp.AndroidMkEntries()
+	entries := &entriesList[0]
+	entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+		testSuiteComponent(entries, a.testProperties.Test_suites)
 		if a.testConfig != nil {
-			fmt.Fprintln(w, "LOCAL_FULL_TEST_CONFIG :=", a.testConfig.String())
+			entries.SetPath("LOCAL_FULL_TEST_CONFIG", a.testConfig)
 		}
-	})
-	androidMkWriteTestData(a.data, &data)
-
-	return data
-}
-
-func (a *AndroidTestHelperApp) AndroidMk() android.AndroidMkData {
-	data := a.AndroidApp.AndroidMk()
-	data.Extra = append(data.Extra, func(w io.Writer, outputFile android.Path) {
-		testSuiteComponent(w, a.appTestHelperAppProperties.Test_suites)
+		androidMkWriteTestData(a.data, entries)
 	})
 
-	return data
+	return entriesList
 }
 
-func (a *AndroidLibrary) AndroidMk() android.AndroidMkData {
-	data := a.Library.AndroidMk()
+func (a *AndroidTestHelperApp) AndroidMkEntries() []android.AndroidMkEntries {
+	entriesList := a.AndroidApp.AndroidMkEntries()
+	entries := &entriesList[0]
+	entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+		testSuiteComponent(entries, a.appTestHelperAppProperties.Test_suites)
+	})
 
-	data.Extra = append(data.Extra, func(w io.Writer, outputFile android.Path) {
+	return entriesList
+}
+
+func (a *AndroidLibrary) AndroidMkEntries() []android.AndroidMkEntries {
+	entriesList := a.Library.AndroidMkEntries()
+	entries := &entriesList[0]
+
+	entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
 		if a.aarFile != nil {
-			fmt.Fprintln(w, "LOCAL_SOONG_AAR :=", a.aarFile.String())
+			entries.SetPath("LOCAL_SOONG_AAR", a.aarFile)
 		}
 
 		if a.Name() == "framework-res" {
-			fmt.Fprintln(w, "LOCAL_MODULE_PATH := $(TARGET_OUT_JAVA_LIBRARIES)")
+			entries.SetString("LOCAL_MODULE_PATH", "$(TARGET_OUT_JAVA_LIBRARIES)")
 			// Make base_rules.mk not put framework-res in a subdirectory called
 			// framework_res.
-			fmt.Fprintln(w, "LOCAL_NO_STANDARD_LIBRARIES := true")
+			entries.SetBoolIfTrue("LOCAL_NO_STANDARD_LIBRARIES", true)
 		}
 
-		fmt.Fprintln(w, "LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE :=", a.exportPackage.String())
-		fmt.Fprintln(w, "LOCAL_SOONG_STATIC_LIBRARY_EXTRA_PACKAGES :=", a.extraAaptPackagesFile.String())
-		fmt.Fprintln(w, "LOCAL_FULL_MANIFEST_FILE :=", a.mergedManifestFile.String())
-		fmt.Fprintln(w, "LOCAL_SOONG_EXPORT_PROGUARD_FLAGS :=",
-			strings.Join(a.exportedProguardFlagFiles.Strings(), " "))
-		fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := true")
+		entries.SetPath("LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE", a.exportPackage)
+		entries.SetPath("LOCAL_SOONG_STATIC_LIBRARY_EXTRA_PACKAGES", a.extraAaptPackagesFile)
+		entries.SetPath("LOCAL_FULL_MANIFEST_FILE", a.mergedManifestFile)
+		entries.AddStrings("LOCAL_SOONG_EXPORT_PROGUARD_FLAGS", a.exportedProguardFlagFiles.Strings()...)
+		entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", true)
 	})
 
-	return data
+	return entriesList
 }
 
-func (jd *Javadoc) AndroidMk() android.AndroidMkData {
-	return android.AndroidMkData{
+func (jd *Javadoc) AndroidMkEntries() []android.AndroidMkEntries {
+	return []android.AndroidMkEntries{android.AndroidMkEntries{
 		Class:      "JAVA_LIBRARIES",
 		OutputFile: android.OptionalPathForPath(jd.stubsSrcJar),
 		Include:    "$(BUILD_SYSTEM)/soong_droiddoc_prebuilt.mk",
-		Extra: []android.AndroidMkExtraFunc{
-			func(w io.Writer, outputFile android.Path) {
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(entries *android.AndroidMkEntries) {
 				if BoolDefault(jd.properties.Installable, true) {
-					fmt.Fprintln(w, "LOCAL_DROIDDOC_DOC_ZIP := ", jd.docZip.String())
+					entries.SetPath("LOCAL_DROIDDOC_DOC_ZIP", jd.docZip)
 				}
 				if jd.stubsSrcJar != nil {
-					fmt.Fprintln(w, "LOCAL_DROIDDOC_STUBS_SRCJAR := ", jd.stubsSrcJar.String())
+					entries.SetPath("LOCAL_DROIDDOC_STUBS_SRCJAR", jd.stubsSrcJar)
 				}
 			},
 		},
-	}
+	}}
 }
 
-func (ddoc *Droiddoc) AndroidMk() android.AndroidMkData {
-	return android.AndroidMkData{
+func (ddoc *Droiddoc) AndroidMkEntries() []android.AndroidMkEntries {
+	return []android.AndroidMkEntries{android.AndroidMkEntries{
 		Class:      "JAVA_LIBRARIES",
 		OutputFile: android.OptionalPathForPath(ddoc.stubsSrcJar),
 		Include:    "$(BUILD_SYSTEM)/soong_droiddoc_prebuilt.mk",
-		Extra: []android.AndroidMkExtraFunc{
-			func(w io.Writer, outputFile android.Path) {
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(entries *android.AndroidMkEntries) {
 				if BoolDefault(ddoc.Javadoc.properties.Installable, true) && ddoc.Javadoc.docZip != nil {
-					fmt.Fprintln(w, "LOCAL_DROIDDOC_DOC_ZIP := ", ddoc.Javadoc.docZip.String())
+					entries.SetPath("LOCAL_DROIDDOC_DOC_ZIP", ddoc.Javadoc.docZip)
 				}
 				if ddoc.Javadoc.stubsSrcJar != nil {
-					fmt.Fprintln(w, "LOCAL_DROIDDOC_STUBS_SRCJAR := ", ddoc.Javadoc.stubsSrcJar.String())
+					entries.SetPath("LOCAL_DROIDDOC_STUBS_SRCJAR", ddoc.Javadoc.stubsSrcJar)
 				}
+				apiFilePrefix := "INTERNAL_PLATFORM_"
+				if String(ddoc.properties.Api_tag_name) != "" {
+					apiFilePrefix += String(ddoc.properties.Api_tag_name) + "_"
+				}
+				if ddoc.apiFile != nil {
+					entries.SetPath(apiFilePrefix+"API_FILE", ddoc.apiFile)
+				}
+				if ddoc.dexApiFile != nil {
+					entries.SetPath(apiFilePrefix+"DEX_API_FILE", ddoc.dexApiFile)
+				}
+				if ddoc.privateApiFile != nil {
+					entries.SetPath(apiFilePrefix+"PRIVATE_API_FILE", ddoc.privateApiFile)
+				}
+				if ddoc.privateDexApiFile != nil {
+					entries.SetPath(apiFilePrefix+"PRIVATE_DEX_API_FILE", ddoc.privateDexApiFile)
+				}
+				if ddoc.removedApiFile != nil {
+					entries.SetPath(apiFilePrefix+"REMOVED_API_FILE", ddoc.removedApiFile)
+				}
+				if ddoc.removedDexApiFile != nil {
+					entries.SetPath(apiFilePrefix+"REMOVED_DEX_API_FILE", ddoc.removedDexApiFile)
+				}
+				if ddoc.exactApiFile != nil {
+					entries.SetPath(apiFilePrefix+"EXACT_API_FILE", ddoc.exactApiFile)
+				}
+				if ddoc.proguardFile != nil {
+					entries.SetPath(apiFilePrefix+"PROGUARD_FILE", ddoc.proguardFile)
+				}
+			},
+		},
+		ExtraFooters: []android.AndroidMkExtraFootersFunc{
+			func(w io.Writer, name, prefix, moduleDir string, entries *android.AndroidMkEntries) {
 				if ddoc.checkCurrentApiTimestamp != nil {
 					fmt.Fprintln(w, ".PHONY:", ddoc.Name()+"-check-current-api")
 					fmt.Fprintln(w, ddoc.Name()+"-check-current-api:",
@@ -457,58 +534,62 @@
 						fmt.Fprintln(w, "droidcore: checkapi")
 					}
 				}
-				apiFilePrefix := "INTERNAL_PLATFORM_"
-				if String(ddoc.properties.Api_tag_name) != "" {
-					apiFilePrefix += String(ddoc.properties.Api_tag_name) + "_"
-				}
-				if ddoc.apiFile != nil {
-					fmt.Fprintln(w, apiFilePrefix+"API_FILE := ", ddoc.apiFile.String())
-				}
-				if ddoc.dexApiFile != nil {
-					fmt.Fprintln(w, apiFilePrefix+"DEX_API_FILE := ", ddoc.dexApiFile.String())
-				}
-				if ddoc.privateApiFile != nil {
-					fmt.Fprintln(w, apiFilePrefix+"PRIVATE_API_FILE := ", ddoc.privateApiFile.String())
-				}
-				if ddoc.privateDexApiFile != nil {
-					fmt.Fprintln(w, apiFilePrefix+"PRIVATE_DEX_API_FILE := ", ddoc.privateDexApiFile.String())
-				}
-				if ddoc.removedApiFile != nil {
-					fmt.Fprintln(w, apiFilePrefix+"REMOVED_API_FILE := ", ddoc.removedApiFile.String())
-				}
-				if ddoc.removedDexApiFile != nil {
-					fmt.Fprintln(w, apiFilePrefix+"REMOVED_DEX_API_FILE := ", ddoc.removedDexApiFile.String())
-				}
-				if ddoc.exactApiFile != nil {
-					fmt.Fprintln(w, apiFilePrefix+"EXACT_API_FILE := ", ddoc.exactApiFile.String())
-				}
-				if ddoc.proguardFile != nil {
-					fmt.Fprintln(w, apiFilePrefix+"PROGUARD_FILE := ", ddoc.proguardFile.String())
-				}
 			},
 		},
-	}
+	}}
 }
 
-func (dstubs *Droidstubs) AndroidMk() android.AndroidMkData {
-	return android.AndroidMkData{
+func (dstubs *Droidstubs) AndroidMkEntries() []android.AndroidMkEntries {
+	return []android.AndroidMkEntries{android.AndroidMkEntries{
 		Class:      "JAVA_LIBRARIES",
 		OutputFile: android.OptionalPathForPath(dstubs.stubsSrcJar),
 		Include:    "$(BUILD_SYSTEM)/soong_droiddoc_prebuilt.mk",
-		Extra: []android.AndroidMkExtraFunc{
-			func(w io.Writer, outputFile android.Path) {
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(entries *android.AndroidMkEntries) {
 				if dstubs.Javadoc.stubsSrcJar != nil {
-					fmt.Fprintln(w, "LOCAL_DROIDDOC_STUBS_SRCJAR := ", dstubs.Javadoc.stubsSrcJar.String())
+					entries.SetPath("LOCAL_DROIDDOC_STUBS_SRCJAR", dstubs.Javadoc.stubsSrcJar)
 				}
 				if dstubs.apiVersionsXml != nil {
-					fmt.Fprintln(w, "LOCAL_DROIDDOC_API_VERSIONS_XML := ", dstubs.apiVersionsXml.String())
+					entries.SetPath("LOCAL_DROIDDOC_API_VERSIONS_XML", dstubs.apiVersionsXml)
 				}
 				if dstubs.annotationsZip != nil {
-					fmt.Fprintln(w, "LOCAL_DROIDDOC_ANNOTATIONS_ZIP := ", dstubs.annotationsZip.String())
+					entries.SetPath("LOCAL_DROIDDOC_ANNOTATIONS_ZIP", dstubs.annotationsZip)
 				}
 				if dstubs.jdiffDocZip != nil {
-					fmt.Fprintln(w, "LOCAL_DROIDDOC_JDIFF_DOC_ZIP := ", dstubs.jdiffDocZip.String())
+					entries.SetPath("LOCAL_DROIDDOC_JDIFF_DOC_ZIP", dstubs.jdiffDocZip)
 				}
+				if dstubs.metadataZip != nil {
+					entries.SetPath("LOCAL_DROIDDOC_METADATA_ZIP", dstubs.metadataZip)
+				}
+				apiFilePrefix := "INTERNAL_PLATFORM_"
+				if String(dstubs.properties.Api_tag_name) != "" {
+					apiFilePrefix += String(dstubs.properties.Api_tag_name) + "_"
+				}
+				if dstubs.apiFile != nil {
+					entries.SetPath(apiFilePrefix+"API_FILE", dstubs.apiFile)
+				}
+				if dstubs.dexApiFile != nil {
+					entries.SetPath(apiFilePrefix+"DEX_API_FILE", dstubs.dexApiFile)
+				}
+				if dstubs.privateApiFile != nil {
+					entries.SetPath(apiFilePrefix+"PRIVATE_API_FILE", dstubs.privateApiFile)
+				}
+				if dstubs.privateDexApiFile != nil {
+					entries.SetPath(apiFilePrefix+"PRIVATE_DEX_API_FILE", dstubs.privateDexApiFile)
+				}
+				if dstubs.removedApiFile != nil {
+					entries.SetPath(apiFilePrefix+"REMOVED_API_FILE", dstubs.removedApiFile)
+				}
+				if dstubs.removedDexApiFile != nil {
+					entries.SetPath(apiFilePrefix+"REMOVED_DEX_API_FILE", dstubs.removedDexApiFile)
+				}
+				if dstubs.exactApiFile != nil {
+					entries.SetPath(apiFilePrefix+"EXACT_API_FILE", dstubs.exactApiFile)
+				}
+			},
+		},
+		ExtraFooters: []android.AndroidMkExtraFootersFunc{
+			func(w io.Writer, name, prefix, moduleDir string, entries *android.AndroidMkEntries) {
 				if dstubs.checkCurrentApiTimestamp != nil {
 					fmt.Fprintln(w, ".PHONY:", dstubs.Name()+"-check-current-api")
 					fmt.Fprintln(w, dstubs.Name()+"-check-current-api:",
@@ -535,7 +616,7 @@
 					fmt.Fprintln(w, dstubs.Name()+"-check-last-released-api:",
 						dstubs.checkLastReleasedApiTimestamp.String())
 
-					if dstubs.Name() == "api-stubs-docs" || dstubs.Name() == "system-api-stubs-docs" {
+					if dstubs.Name() != "android.car-system-stubs-docs" {
 						fmt.Fprintln(w, ".PHONY: checkapi")
 						fmt.Fprintln(w, "checkapi:",
 							dstubs.checkLastReleasedApiTimestamp.String())
@@ -544,6 +625,23 @@
 						fmt.Fprintln(w, "droidcore: checkapi")
 					}
 				}
+				if dstubs.apiLintTimestamp != nil {
+					fmt.Fprintln(w, ".PHONY:", dstubs.Name()+"-api-lint")
+					fmt.Fprintln(w, dstubs.Name()+"-api-lint:",
+						dstubs.apiLintTimestamp.String())
+
+					fmt.Fprintln(w, ".PHONY: checkapi")
+					fmt.Fprintln(w, "checkapi:",
+						dstubs.Name()+"-api-lint")
+
+					fmt.Fprintln(w, ".PHONY: droidcore")
+					fmt.Fprintln(w, "droidcore: checkapi")
+
+					if dstubs.apiLintReport != nil {
+						fmt.Fprintf(w, "$(call dist-for-goals,%s,%s:%s)\n", dstubs.Name()+"-api-lint",
+							dstubs.apiLintReport.String(), "apilint/"+dstubs.Name()+"-lint-report.txt")
+					}
+				}
 				if dstubs.checkNullabilityWarningsTimestamp != nil {
 					fmt.Fprintln(w, ".PHONY:", dstubs.Name()+"-check-nullability-warnings")
 					fmt.Fprintln(w, dstubs.Name()+"-check-nullability-warnings:",
@@ -552,70 +650,48 @@
 					fmt.Fprintln(w, ".PHONY:", "droidcore")
 					fmt.Fprintln(w, "droidcore: ", dstubs.Name()+"-check-nullability-warnings")
 				}
-				apiFilePrefix := "INTERNAL_PLATFORM_"
-				if String(dstubs.properties.Api_tag_name) != "" {
-					apiFilePrefix += String(dstubs.properties.Api_tag_name) + "_"
-				}
-				if dstubs.apiFile != nil {
-					fmt.Fprintln(w, apiFilePrefix+"API_FILE := ", dstubs.apiFile.String())
-				}
-				if dstubs.dexApiFile != nil {
-					fmt.Fprintln(w, apiFilePrefix+"DEX_API_FILE := ", dstubs.dexApiFile.String())
-				}
-				if dstubs.privateApiFile != nil {
-					fmt.Fprintln(w, apiFilePrefix+"PRIVATE_API_FILE := ", dstubs.privateApiFile.String())
-				}
-				if dstubs.privateDexApiFile != nil {
-					fmt.Fprintln(w, apiFilePrefix+"PRIVATE_DEX_API_FILE := ", dstubs.privateDexApiFile.String())
-				}
-				if dstubs.removedApiFile != nil {
-					fmt.Fprintln(w, apiFilePrefix+"REMOVED_API_FILE := ", dstubs.removedApiFile.String())
-				}
-				if dstubs.removedDexApiFile != nil {
-					fmt.Fprintln(w, apiFilePrefix+"REMOVED_DEX_API_FILE := ", dstubs.removedDexApiFile.String())
-				}
-				if dstubs.exactApiFile != nil {
-					fmt.Fprintln(w, apiFilePrefix+"EXACT_API_FILE := ", dstubs.exactApiFile.String())
-				}
 			},
 		},
-	}
+	}}
 }
 
-func (app *AndroidAppImport) AndroidMk() android.AndroidMkData {
-	return android.AndroidMkData{
+func (a *AndroidAppImport) AndroidMkEntries() []android.AndroidMkEntries {
+	return []android.AndroidMkEntries{android.AndroidMkEntries{
 		Class:      "APPS",
-		OutputFile: android.OptionalPathForPath(app.outputFile),
+		OutputFile: android.OptionalPathForPath(a.outputFile),
 		Include:    "$(BUILD_SYSTEM)/soong_app_prebuilt.mk",
-		Extra: []android.AndroidMkExtraFunc{
-			func(w io.Writer, outputFile android.Path) {
-				if Bool(app.properties.Privileged) {
-					fmt.Fprintln(w, "LOCAL_PRIVILEGED_MODULE := true")
-				}
-				if app.certificate != nil {
-					fmt.Fprintln(w, "LOCAL_CERTIFICATE :=", app.certificate.Pem.String())
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(entries *android.AndroidMkEntries) {
+				entries.SetBoolIfTrue("LOCAL_PRIVILEGED_MODULE", a.Privileged())
+				if a.certificate != nil {
+					entries.SetPath("LOCAL_CERTIFICATE", a.certificate.Pem)
 				} else {
-					fmt.Fprintln(w, "LOCAL_CERTIFICATE := PRESIGNED")
+					entries.SetString("LOCAL_CERTIFICATE", "PRESIGNED")
 				}
-				if len(app.properties.Overrides) > 0 {
-					fmt.Fprintln(w, "LOCAL_OVERRIDES_PACKAGES :=", strings.Join(app.properties.Overrides, " "))
+				entries.AddStrings("LOCAL_OVERRIDES_PACKAGES", a.properties.Overrides...)
+				if len(a.dexpreopter.builtInstalled) > 0 {
+					entries.SetString("LOCAL_SOONG_BUILT_INSTALLED", a.dexpreopter.builtInstalled)
 				}
-				if len(app.dexpreopter.builtInstalled) > 0 {
-					fmt.Fprintln(w, "LOCAL_SOONG_BUILT_INSTALLED :=", app.dexpreopter.builtInstalled)
-				}
+				entries.AddStrings("LOCAL_INSTALLED_MODULE_STEM", a.installPath.Rel())
 			},
 		},
-	}
+	}}
 }
 
-func androidMkWriteTestData(data android.Paths, ret *android.AndroidMkData) {
+func (a *AndroidTestImport) AndroidMkEntries() []android.AndroidMkEntries {
+	entriesList := a.AndroidAppImport.AndroidMkEntries()
+	entries := &entriesList[0]
+	entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+		testSuiteComponent(entries, a.testProperties.Test_suites)
+		androidMkWriteTestData(a.data, entries)
+	})
+	return entriesList
+}
+
+func androidMkWriteTestData(data android.Paths, entries *android.AndroidMkEntries) {
 	var testFiles []string
 	for _, d := range data {
 		testFiles = append(testFiles, d.String()+":"+d.Rel())
 	}
-	if len(testFiles) > 0 {
-		ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
-			fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUPPORT_FILES := "+strings.Join(testFiles, " "))
-		})
-	}
+	entries.AddStrings("LOCAL_COMPATIBILITY_SUPPORT_FILES", testFiles...)
 }
diff --git a/java/androidmk_test.go b/java/androidmk_test.go
new file mode 100644
index 0000000..acc6bf0
--- /dev/null
+++ b/java/androidmk_test.go
@@ -0,0 +1,135 @@
+// 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 java
+
+import (
+	"reflect"
+	"testing"
+
+	"android/soong/android"
+)
+
+func TestRequired(t *testing.T) {
+	ctx, config := testJava(t, `
+		java_library {
+			name: "foo",
+			srcs: ["a.java"],
+			required: ["libfoo"],
+		}
+	`)
+
+	mod := ctx.ModuleForTests("foo", "android_common").Module()
+	entries := android.AndroidMkEntriesForTest(t, config, "", mod)[0]
+
+	expected := []string{"libfoo"}
+	actual := entries.EntryMap["LOCAL_REQUIRED_MODULES"]
+	if !reflect.DeepEqual(expected, actual) {
+		t.Errorf("Unexpected required modules - expected: %q, actual: %q", expected, actual)
+	}
+}
+
+func TestHostdex(t *testing.T) {
+	ctx, config := testJava(t, `
+		java_library {
+			name: "foo",
+			srcs: ["a.java"],
+			hostdex: true,
+		}
+	`)
+
+	mod := ctx.ModuleForTests("foo", "android_common").Module()
+	entriesList := android.AndroidMkEntriesForTest(t, config, "", mod)
+	if len(entriesList) != 2 {
+		t.Errorf("two entries are expected, but got %d", len(entriesList))
+	}
+
+	mainEntries := &entriesList[0]
+	expected := []string{"foo"}
+	actual := mainEntries.EntryMap["LOCAL_MODULE"]
+	if !reflect.DeepEqual(expected, actual) {
+		t.Errorf("Unexpected module name - expected: %q, actual: %q", expected, actual)
+	}
+
+	subEntries := &entriesList[1]
+	expected = []string{"foo-hostdex"}
+	actual = subEntries.EntryMap["LOCAL_MODULE"]
+	if !reflect.DeepEqual(expected, actual) {
+		t.Errorf("Unexpected module name - expected: %q, actual: %q", expected, actual)
+	}
+}
+
+func TestHostdexRequired(t *testing.T) {
+	ctx, config := testJava(t, `
+		java_library {
+			name: "foo",
+			srcs: ["a.java"],
+			hostdex: true,
+			required: ["libfoo"],
+		}
+	`)
+
+	mod := ctx.ModuleForTests("foo", "android_common").Module()
+	entriesList := android.AndroidMkEntriesForTest(t, config, "", mod)
+	if len(entriesList) != 2 {
+		t.Errorf("two entries are expected, but got %d", len(entriesList))
+	}
+
+	mainEntries := &entriesList[0]
+	expected := []string{"libfoo"}
+	actual := mainEntries.EntryMap["LOCAL_REQUIRED_MODULES"]
+	if !reflect.DeepEqual(expected, actual) {
+		t.Errorf("Unexpected required modules - expected: %q, actual: %q", expected, actual)
+	}
+
+	subEntries := &entriesList[1]
+	expected = []string{"libfoo"}
+	actual = subEntries.EntryMap["LOCAL_REQUIRED_MODULES"]
+	if !reflect.DeepEqual(expected, actual) {
+		t.Errorf("Unexpected required modules - expected: %q, actual: %q", expected, actual)
+	}
+}
+
+func TestHostdexSpecificRequired(t *testing.T) {
+	ctx, config := testJava(t, `
+		java_library {
+			name: "foo",
+			srcs: ["a.java"],
+			hostdex: true,
+			target: {
+				hostdex: {
+					required: ["libfoo"],
+				},
+			},
+		}
+	`)
+
+	mod := ctx.ModuleForTests("foo", "android_common").Module()
+	entriesList := android.AndroidMkEntriesForTest(t, config, "", mod)
+	if len(entriesList) != 2 {
+		t.Errorf("two entries are expected, but got %d", len(entriesList))
+	}
+
+	mainEntries := &entriesList[0]
+	if r, ok := mainEntries.EntryMap["LOCAL_REQUIRED_MODULES"]; ok {
+		t.Errorf("Unexpected required modules: %q", r)
+	}
+
+	subEntries := &entriesList[1]
+	expected := []string{"libfoo"}
+	actual := subEntries.EntryMap["LOCAL_REQUIRED_MODULES"]
+	if !reflect.DeepEqual(expected, actual) {
+		t.Errorf("Unexpected required modules - expected: %q, actual: %q", expected, actual)
+	}
+}
diff --git a/java/app.go b/java/app.go
old mode 100644
new mode 100755
index cab97de..a6b3408
--- a/java/app.go
+++ b/java/app.go
@@ -19,6 +19,7 @@
 import (
 	"path/filepath"
 	"reflect"
+	"sort"
 	"strings"
 
 	"github.com/google/blueprint"
@@ -29,32 +30,23 @@
 	"android/soong/tradefed"
 )
 
-var supportedDpis = [...]string{"Ldpi", "Mdpi", "Hdpi", "Xhdpi", "Xxhdpi", "Xxxhdpi"}
-var dpiVariantsStruct reflect.Type
+var supportedDpis = []string{"ldpi", "mdpi", "hdpi", "xhdpi", "xxhdpi", "xxxhdpi"}
 
 func init() {
-	android.RegisterModuleType("android_app", AndroidAppFactory)
-	android.RegisterModuleType("android_test", AndroidTestFactory)
-	android.RegisterModuleType("android_test_helper_app", AndroidTestHelperAppFactory)
-	android.RegisterModuleType("android_app_certificate", AndroidAppCertificateFactory)
-	android.RegisterModuleType("override_android_app", OverrideAndroidAppModuleFactory)
-	android.RegisterModuleType("android_app_import", AndroidAppImportFactory)
+	RegisterAppBuildComponents(android.InitRegistrationContext)
 
-	// Dynamically construct a struct for the dpi_variants property in android_app_import.
-	perDpiStruct := reflect.StructOf([]reflect.StructField{
-		{
-			Name: "Apk",
-			Type: reflect.TypeOf((*string)(nil)),
-		},
-	})
-	dpiVariantsFields := make([]reflect.StructField, len(supportedDpis))
-	for i, dpi := range supportedDpis {
-		dpiVariantsFields[i] = reflect.StructField{
-			Name: string(dpi),
-			Type: perDpiStruct,
-		}
-	}
-	dpiVariantsStruct = reflect.StructOf(dpiVariantsFields)
+	initAndroidAppImportVariantGroupTypes()
+}
+
+func RegisterAppBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("android_app", AndroidAppFactory)
+	ctx.RegisterModuleType("android_test", AndroidTestFactory)
+	ctx.RegisterModuleType("android_test_helper_app", AndroidTestHelperAppFactory)
+	ctx.RegisterModuleType("android_app_certificate", AndroidAppCertificateFactory)
+	ctx.RegisterModuleType("override_android_app", OverrideAndroidAppModuleFactory)
+	ctx.RegisterModuleType("override_android_test", OverrideAndroidTestModuleFactory)
+	ctx.RegisterModuleType("android_app_import", AndroidAppImportFactory)
+	ctx.RegisterModuleType("android_test_import", AndroidTestImportFactory)
 }
 
 // AndroidManifest.xml merging
@@ -91,8 +83,9 @@
 
 	// Store native libraries uncompressed in the APK and set the android:extractNativeLibs="false" manifest
 	// flag so that they are used from inside the APK at runtime.  Defaults to true for android_test modules unless
-	// sdk_version or min_sdk_version is set to a version that doesn't support it (<23), defaults to false for other
-	// module types where the native libraries are generally preinstalled outside the APK.
+	// sdk_version or min_sdk_version is set to a version that doesn't support it (<23), defaults to true for
+	// android_app modules that are embedded to APEXes, defaults to false for other module types where the native
+	// libraries are generally preinstalled outside the APK.
 	Use_embedded_native_libs *bool
 
 	// Store dex files uncompressed in the APK and set the android:useEmbeddedDex="true" manifest attribute so that
@@ -103,6 +96,10 @@
 	// Use_embedded_native_libs still selects whether they are stored uncompressed and aligned or compressed.
 	// True for android_test* modules.
 	AlwaysPackageNativeLibs bool `blueprint:"mutated"`
+
+	// If set, find and merge all NOTICE files that this module and its dependencies have and store
+	// it in the APK as an asset.
+	Embed_notices *bool
 }
 
 // android_app properties that can be overridden by override_android_app
@@ -135,7 +132,13 @@
 	// the install APK name is normally the same as the module name, but can be overridden with PRODUCT_PACKAGE_NAME_OVERRIDES.
 	installApkName string
 
+	installDir android.InstallPath
+
+	onDeviceDir string
+
 	additionalAaptFlags []string
+
+	noticeOutputs android.NoticeOutputs
 }
 
 func (a *AndroidApp) ExportedProguardFlagFiles() android.Paths {
@@ -146,6 +149,10 @@
 	return nil
 }
 
+func (a *AndroidApp) OutputFile() android.Path {
+	return a.outputFile
+}
+
 var _ AndroidLibraryDependency = (*AndroidApp)(nil)
 
 type Certificate struct {
@@ -164,21 +171,11 @@
 		a.aapt.deps(ctx, sdkDep)
 	}
 
-	embedJni := a.shouldEmbedJnis(ctx)
+	tag := &jniDependencyTag{}
 	for _, jniTarget := range ctx.MultiTargets() {
-		variation := []blueprint.Variation{
-			{Mutator: "arch", Variation: jniTarget.String()},
-			{Mutator: "link", Variation: "shared"},
-		}
-		tag := &jniDependencyTag{
-			target: jniTarget,
-		}
+		variation := append(jniTarget.Variations(),
+			blueprint.Variation{Mutator: "link", Variation: "shared"})
 		ctx.AddFarVariationDependencies(variation, tag, a.appProperties.Jni_libs...)
-		if String(a.appProperties.Stl) == "c++_shared" {
-			if embedJni {
-				ctx.AddFarVariationDependencies(variation, tag, "ndk_libc++_shared")
-			}
-		}
 	}
 
 	a.usesLibrary.deps(ctx, sdkDep.hasFrameworkLibs())
@@ -201,9 +198,13 @@
 	}
 }
 
+func (a *AndroidTestHelperApp) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	a.generateAndroidBuildActions(ctx)
+}
+
 func (a *AndroidApp) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	a.aapt.useEmbeddedNativeLibs = a.useEmbeddedNativeLibs(ctx)
-	a.aapt.useEmbeddedDex = Bool(a.appProperties.Use_embedded_dex)
+	a.checkPlatformAPI(ctx)
+	a.checkSdkVersion(ctx)
 	a.generateAndroidBuildActions(ctx)
 }
 
@@ -215,7 +216,8 @@
 		ctx.PropertyErrorf("min_sdk_version", "invalid value %q: %s", a.minSdkVersion(), err)
 	}
 
-	return minSdkVersion >= 23 && Bool(a.appProperties.Use_embedded_native_libs)
+	return (minSdkVersion >= 23 && Bool(a.appProperties.Use_embedded_native_libs)) ||
+		!a.IsForPlatform()
 }
 
 // Returns whether this module should have the dex file stored uncompressed in the APK.
@@ -224,13 +226,14 @@
 		return true
 	}
 
-	if ctx.Config().UnbundledBuild() {
-		return false
+	// Uncompress dex in APKs of privileged apps (even for unbundled builds, they may
+	// be preinstalled as prebuilts).
+	if ctx.Config().UncompressPrivAppDex() && a.Privileged() {
+		return true
 	}
 
-	// Uncompress dex in APKs of privileged apps
-	if ctx.Config().UncompressPrivAppDex() && Bool(a.appProperties.Privileged) {
-		return true
+	if ctx.Config().UnbundledBuild() {
+		return false
 	}
 
 	return shouldUncompressDex(ctx, &a.dexpreopter)
@@ -238,7 +241,7 @@
 
 func (a *AndroidApp) shouldEmbedJnis(ctx android.BaseModuleContext) bool {
 	return ctx.Config().UnbundledBuild() || Bool(a.appProperties.Use_embedded_native_libs) ||
-		a.appProperties.AlwaysPackageNativeLibs
+		!a.IsForPlatform() || a.appProperties.AlwaysPackageNativeLibs
 }
 
 func (a *AndroidApp) aaptBuildActions(ctx android.ModuleContext) {
@@ -313,12 +316,11 @@
 	if ctx.ModuleName() == "framework-res" {
 		// framework-res.apk is installed as system/framework/framework-res.apk
 		installDir = "framework"
-	} else if Bool(a.appProperties.Privileged) {
+	} else if a.Privileged() {
 		installDir = filepath.Join("priv-app", a.installApkName)
 	} else {
 		installDir = filepath.Join("app", a.installApkName)
 	}
-
 	a.dexpreopter.installPath = android.PathForModuleInstall(ctx, installDir, a.installApkName+".apk")
 	a.dexpreopter.isInstallable = Bool(a.properties.Installable)
 	a.dexpreopter.uncompressedDex = a.shouldUncompressDex(ctx)
@@ -351,6 +353,49 @@
 	return jniJarFile
 }
 
+func (a *AndroidApp) noticeBuildActions(ctx android.ModuleContext) {
+	// Collect NOTICE files from all dependencies.
+	seenModules := make(map[android.Module]bool)
+	noticePathSet := make(map[android.Path]bool)
+
+	ctx.WalkDeps(func(child android.Module, parent android.Module) bool {
+		// Have we already seen this?
+		if _, ok := seenModules[child]; ok {
+			return false
+		}
+		seenModules[child] = true
+
+		// Skip host modules.
+		if child.Target().Os.Class == android.Host || child.Target().Os.Class == android.HostCross {
+			return false
+		}
+
+		path := child.(android.Module).NoticeFile()
+		if path.Valid() {
+			noticePathSet[path.Path()] = true
+		}
+		return true
+	})
+
+	// If the app has one, add it too.
+	if a.NoticeFile().Valid() {
+		noticePathSet[a.NoticeFile().Path()] = true
+	}
+
+	if len(noticePathSet) == 0 {
+		return
+	}
+	var noticePaths []android.Path
+	for path := range noticePathSet {
+		noticePaths = append(noticePaths, path)
+	}
+	sort.Slice(noticePaths, func(i, j int) bool {
+		return noticePaths[i].String() < noticePaths[j].String()
+	})
+
+	a.noticeOutputs = android.BuildNoticeOutput(ctx, a.installDir, a.installApkName+".apk", noticePaths)
+}
+
 // Reads and prepends a main cert from the default cert dir if it hasn't been set already, i.e. it
 // isn't a cert module reference. Also checks and enforces system cert restriction if applicable.
 func processMainCert(m android.ModuleBase, certPropValue string, certificates []Certificate, ctx android.ModuleContext) []Certificate {
@@ -388,9 +433,29 @@
 func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) {
 	var apkDeps android.Paths
 
+	a.aapt.useEmbeddedNativeLibs = a.useEmbeddedNativeLibs(ctx)
+	a.aapt.useEmbeddedDex = Bool(a.appProperties.Use_embedded_dex)
+
 	// Check if the install APK name needs to be overridden.
 	a.installApkName = ctx.DeviceConfig().OverridePackageNameFor(a.Name())
 
+	if ctx.ModuleName() == "framework-res" {
+		// framework-res.apk is installed as system/framework/framework-res.apk
+		a.installDir = android.PathForModuleInstall(ctx, "framework")
+	} else if a.Privileged() {
+		a.installDir = android.PathForModuleInstall(ctx, "priv-app", a.installApkName)
+	} else if ctx.InstallInTestcases() {
+		a.installDir = android.PathForModuleInstall(ctx, a.installApkName, ctx.DeviceConfig().DeviceArch())
+	} else {
+		a.installDir = android.PathForModuleInstall(ctx, "app", a.installApkName)
+	}
+	a.onDeviceDir = android.InstallPathToOnDevicePath(ctx, a.installDir)
+
+	a.noticeBuildActions(ctx)
+	if Bool(a.appProperties.Embed_notices) || ctx.Config().IsEnvTrue("ALWAYS_EMBED_NOTICES") {
+		a.aapt.noticeFile = a.noticeOutputs.HtmlGzOutput
+	}
+
 	// Process all building blocks, from AAPT to certificates.
 	a.aaptBuildActions(ctx)
 
@@ -403,7 +468,7 @@
 
 	dexJarFile := a.dexBuildActions(ctx)
 
-	jniLibs, certificateDeps := collectAppDeps(ctx)
+	jniLibs, certificateDeps := collectAppDeps(ctx, a.shouldEmbedJnis(ctx))
 	jniJarFile := a.jniBuildActions(jniLibs, ctx)
 
 	if ctx.Failed() {
@@ -414,14 +479,13 @@
 	a.certificate = certificates[0]
 
 	// Build a final signed app package.
-	// TODO(jungjw): Consider changing this to installApkName.
-	packageFile := android.PathForModuleOut(ctx, ctx.ModuleName()+".apk")
+	packageFile := android.PathForModuleOut(ctx, a.installApkName+".apk")
 	CreateAndSignAppPackage(ctx, packageFile, a.exportPackage, jniJarFile, dexJarFile, certificates, apkDeps)
 	a.outputFile = packageFile
 
 	for _, split := range a.aapt.splits {
 		// Sign the split APKs
-		packageFile := android.PathForModuleOut(ctx, ctx.ModuleName()+"_"+split.suffix+".apk")
+		packageFile := android.PathForModuleOut(ctx, a.installApkName+"_"+split.suffix+".apk")
 		CreateAndSignAppPackage(ctx, packageFile, split.path, nil, nil, certificates, apkDeps)
 		a.extraOutputFiles = append(a.extraOutputFiles, packageFile)
 	}
@@ -432,38 +496,41 @@
 	a.bundleFile = bundleFile
 
 	// Install the app package.
-	var installDir android.OutputPath
-	if ctx.ModuleName() == "framework-res" {
-		// framework-res.apk is installed as system/framework/framework-res.apk
-		installDir = android.PathForModuleInstall(ctx, "framework")
-	} else if Bool(a.appProperties.Privileged) {
-		installDir = android.PathForModuleInstall(ctx, "priv-app", a.installApkName)
-	} else {
-		installDir = android.PathForModuleInstall(ctx, "app", a.installApkName)
-	}
-
-	ctx.InstallFile(installDir, a.installApkName+".apk", a.outputFile)
-	for _, split := range a.aapt.splits {
-		ctx.InstallFile(installDir, a.installApkName+"_"+split.suffix+".apk", split.path)
+	if (Bool(a.Module.properties.Installable) || ctx.Host()) && a.IsForPlatform() {
+		ctx.InstallFile(a.installDir, a.outputFile.Base(), a.outputFile)
+		for _, extra := range a.extraOutputFiles {
+			ctx.InstallFile(a.installDir, extra.Base(), extra)
+		}
 	}
 }
 
-func collectAppDeps(ctx android.ModuleContext) ([]jniLib, []Certificate) {
+func collectAppDeps(ctx android.ModuleContext, shouldCollectRecursiveNativeDeps bool) ([]jniLib, []Certificate) {
 	var jniLibs []jniLib
 	var certificates []Certificate
+	seenModulePaths := make(map[string]bool)
 
-	ctx.VisitDirectDeps(func(module android.Module) {
+	ctx.WalkDeps(func(module android.Module, parent android.Module) bool {
 		otherName := ctx.OtherModuleName(module)
 		tag := ctx.OtherModuleDependencyTag(module)
 
-		if jniTag, ok := tag.(*jniDependencyTag); ok {
+		if IsJniDepTag(tag) || tag == cc.SharedDepTag {
 			if dep, ok := module.(*cc.Module); ok {
+				if dep.IsNdk() || dep.IsStubs() {
+					return false
+				}
+
 				lib := dep.OutputFile()
+				path := lib.Path()
+				if seenModulePaths[path.String()] {
+					return false
+				}
+				seenModulePaths[path.String()] = true
+
 				if lib.Valid() {
 					jniLibs = append(jniLibs, jniLib{
 						name:   ctx.OtherModuleName(module),
-						path:   lib.Path(),
-						target: jniTag.target,
+						path:   path,
+						target: module.Target(),
 					})
 				} else {
 					ctx.ModuleErrorf("dependency %q missing output file", otherName)
@@ -471,13 +538,19 @@
 			} else {
 				ctx.ModuleErrorf("jni_libs dependency %q must be a cc library", otherName)
 			}
-		} else if tag == certificateTag {
+
+			return shouldCollectRecursiveNativeDeps
+		}
+
+		if tag == certificateTag {
 			if dep, ok := module.(*AndroidAppCertificate); ok {
 				certificates = append(certificates, dep.Certificate)
 			} else {
 				ctx.ModuleErrorf("certificate dependency %q must be an android_app_certificate module", otherName)
 			}
 		}
+
+		return false
 	})
 
 	return jniLibs, certificates
@@ -491,6 +564,19 @@
 	return String(a.overridableAppProperties.Certificate)
 }
 
+// For OutputFileProducer interface
+func (a *AndroidApp) OutputFiles(tag string) (android.Paths, error) {
+	switch tag {
+	case ".aapt.srcjar":
+		return []android.Path{a.aaptSrcJar}, nil
+	}
+	return a.Library.OutputFiles(tag)
+}
+
+func (a *AndroidApp) Privileged() bool {
+	return Bool(a.appProperties.Privileged)
+}
+
 // android_app compiles sources and Android resources into an Android application package `.apk` file.
 func AndroidAppFactory() android.Module {
 	module := &AndroidApp{}
@@ -518,12 +604,16 @@
 	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
 	android.InitDefaultableModule(module)
 	android.InitOverridableModule(module, &module.appProperties.Overrides)
+	android.InitApexModule(module)
 
 	return module
 }
 
 type appTestProperties struct {
 	Instrumentation_for *string
+
+	// if specified, the instrumentation target package name in the manifest is overwritten by it.
+	Instrumentation_target_package *string
 }
 
 type AndroidTest struct {
@@ -537,24 +627,45 @@
 	data       android.Paths
 }
 
+func (a *AndroidTest) InstallInTestcases() bool {
+	return true
+}
+
 func (a *AndroidTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	// Check if the instrumentation target package is overridden before generating build actions.
-	if a.appTestProperties.Instrumentation_for != nil {
+	if a.appTestProperties.Instrumentation_target_package != nil {
+		a.additionalAaptFlags = append(a.additionalAaptFlags,
+			"--rename-instrumentation-target-package "+*a.appTestProperties.Instrumentation_target_package)
+	} else if a.appTestProperties.Instrumentation_for != nil {
+		// Check if the instrumentation target package is overridden.
 		manifestPackageName, overridden := ctx.DeviceConfig().OverrideManifestPackageNameFor(*a.appTestProperties.Instrumentation_for)
 		if overridden {
 			a.additionalAaptFlags = append(a.additionalAaptFlags, "--rename-instrumentation-target-package "+manifestPackageName)
 		}
 	}
-	a.aapt.useEmbeddedNativeLibs = a.useEmbeddedNativeLibs(ctx)
-	a.aapt.useEmbeddedDex = Bool(a.appProperties.Use_embedded_dex)
 	a.generateAndroidBuildActions(ctx)
 
-	a.testConfig = tradefed.AutoGenInstrumentationTestConfig(ctx, a.testProperties.Test_config, a.testProperties.Test_config_template, a.manifestPath, a.testProperties.Test_suites)
+	a.testConfig = tradefed.AutoGenInstrumentationTestConfig(ctx, a.testProperties.Test_config,
+		a.testProperties.Test_config_template, a.manifestPath, a.testProperties.Test_suites, a.testProperties.Auto_gen_config)
+	if a.overridableAppProperties.Package_name != nil {
+		fixedConfig := android.PathForModuleOut(ctx, "test_config_fixer", "AndroidTest.xml")
+		rule := android.NewRuleBuilder()
+		rule.Command().BuiltTool(ctx, "test_config_fixer").
+			FlagWithInput("--manifest ", a.manifestPath).
+			FlagWithArg("--package-name ", *a.overridableAppProperties.Package_name).
+			Input(a.testConfig).
+			Output(fixedConfig)
+		rule.Build(pctx, ctx, "fix_test_config", "fix test config")
+		a.testConfig = fixedConfig
+	}
 	a.data = android.PathsForModuleSrc(ctx, a.testProperties.Data)
 }
 
 func (a *AndroidTest) DepsMutator(ctx android.BottomUpMutatorContext) {
 	a.AndroidApp.DepsMutator(ctx)
+}
+
+func (a *AndroidTest) OverridablePropertiesDepsMutator(ctx android.BottomUpMutatorContext) {
+	a.AndroidApp.OverridablePropertiesDepsMutator(ctx)
 	if a.appTestProperties.Instrumentation_for != nil {
 		// The android_app dependency listed in instrumentation_for needs to be added to the classpath for javac,
 		// but not added to the aapt2 link includes like a normal android_app or android_library dependency, so
@@ -590,6 +701,7 @@
 
 	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
 	android.InitDefaultableModule(module)
+	android.InitOverridableModule(module, &module.appProperties.Overrides)
 	return module
 }
 
@@ -597,6 +709,11 @@
 	// list of compatibility suites (for example "cts", "vts") that the module should be
 	// installed into.
 	Test_suites []string `android:"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 AndroidTestHelperApp struct {
@@ -605,6 +722,10 @@
 	appTestHelperAppProperties appTestHelperAppProperties
 }
 
+func (a *AndroidTestHelperApp) InstallInTestcases() bool {
+	return true
+}
+
 // android_test_helper_app compiles sources and Android resources into an Android application package `.apk` file that
 // will be used by tests, but does not produce an `AndroidTest.xml` file so the module will not be run directly as a
 // test.
@@ -631,6 +752,7 @@
 
 	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
 	android.InitDefaultableModule(module)
+	android.InitApexModule(module)
 	return module
 }
 
@@ -683,12 +805,36 @@
 	return m
 }
 
+type OverrideAndroidTest struct {
+	android.ModuleBase
+	android.OverrideModuleBase
+}
+
+func (i *OverrideAndroidTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	// All the overrides happen in the base module.
+	// TODO(jungjw): Check the base module type.
+}
+
+// override_android_test is used to create an android_app module based on another android_test by overriding
+// some of its properties.
+func OverrideAndroidTestModuleFactory() android.Module {
+	m := &OverrideAndroidTest{}
+	m.AddProperties(&overridableAppProperties{})
+	m.AddProperties(&appTestProperties{})
+
+	android.InitAndroidMultiTargetsArchModule(m, android.DeviceSupported, android.MultilibCommon)
+	android.InitOverrideModule(m)
+	return m
+}
+
 type AndroidAppImport struct {
 	android.ModuleBase
 	android.DefaultableModuleBase
 	prebuilt android.Prebuilt
 
-	properties AndroidAppImportProperties
+	properties   AndroidAppImportProperties
+	dpiVariants  interface{}
+	archVariants interface{}
 
 	outputFile  android.Path
 	certificate *Certificate
@@ -696,40 +842,26 @@
 	dexpreopter
 
 	usesLibrary usesLibrary
+
+	installPath android.InstallPath
 }
 
 type AndroidAppImportProperties struct {
 	// A prebuilt apk to import
-	Apk string
+	Apk *string
 
-	// Per-DPI settings. This property makes it possible to specify a different source apk path for
-	// each DPI.
-	//
-	// Example:
-	//
-	//     android_app_import {
-	//         name: "example_import",
-	//         apk: "prebuilts/example.apk",
-	//         dpi_variants: {
-	//             mdpi: {
-	//                 apk: "prebuilts/example_mdpi.apk",
-	//             },
-	//             xhdpi: {
-	//                 apk: "prebuilts/example_xhdpi.apk",
-	//             },
-	//         },
-	//         certificate: "PRESIGNED",
-	//     }
-	Dpi_variants interface{}
-
-	// The name of a certificate in the default certificate directory, blank to use the default
-	// product certificate, or an android_app_certificate module name in the form ":module".
+	// The name of a certificate in the default certificate directory or an android_app_certificate
+	// module name in the form ":module". Should be empty if presigned or default_dev_cert is set.
 	Certificate *string
 
 	// Set this flag to true if the prebuilt apk is already signed. The certificate property must not
 	// be set for presigned modules.
 	Presigned *bool
 
+	// Sign with the default system dev certificate. Must be used judiciously. Most imported apps
+	// need to either specify a specific certificate or be presigned.
+	Default_dev_cert *bool
+
 	// Specifies that this app should be installed to the priv-app directory,
 	// where the system will grant it additional privileges not available to
 	// normal apps.
@@ -741,41 +873,46 @@
 	// binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will be removed
 	// from PRODUCT_PACKAGES.
 	Overrides []string
+
+	// Optional name for the installed app. If unspecified, it is derived from the module name.
+	Filename *string
 }
 
-func getApkPathForDpi(dpiVariantsValue reflect.Value, dpi string) string {
-	dpiField := dpiVariantsValue.FieldByName(proptools.FieldNameForProperty(dpi))
-	if !dpiField.IsValid() {
-		return ""
-	}
-	apkValue := dpiField.FieldByName("Apk").Elem()
-	if apkValue.IsValid() {
-		return apkValue.String()
-	}
-	return ""
-}
-
-// Chooses a source APK path to use based on the module's per-DPI settings and the product config.
-func (a *AndroidAppImport) getSrcApkPath(ctx android.ModuleContext) string {
+// Updates properties with variant-specific values.
+func (a *AndroidAppImport) processVariants(ctx android.LoadHookContext) {
 	config := ctx.Config()
-	dpiVariantsValue := reflect.ValueOf(a.properties.Dpi_variants).Elem()
-	if !dpiVariantsValue.IsValid() {
-		return a.properties.Apk
+
+	dpiProps := reflect.ValueOf(a.dpiVariants).Elem().FieldByName("Dpi_variants")
+	// Try DPI variant matches in the reverse-priority order so that the highest priority match
+	// overwrites everything else.
+	// TODO(jungjw): Can we optimize this by making it priority order?
+	for i := len(config.ProductAAPTPrebuiltDPI()) - 1; i >= 0; i-- {
+		MergePropertiesFromVariant(ctx, &a.properties, dpiProps, config.ProductAAPTPrebuiltDPI()[i])
 	}
-	// Match PRODUCT_AAPT_PREF_CONFIG first and then PRODUCT_AAPT_PREBUILT_DPI.
 	if config.ProductAAPTPreferredConfig() != "" {
-		if apk := getApkPathForDpi(dpiVariantsValue, config.ProductAAPTPreferredConfig()); apk != "" {
-			return apk
-		}
-	}
-	for _, dpi := range config.ProductAAPTPrebuiltDPI() {
-		if apk := getApkPathForDpi(dpiVariantsValue, dpi); apk != "" {
-			return apk
-		}
+		MergePropertiesFromVariant(ctx, &a.properties, dpiProps, config.ProductAAPTPreferredConfig())
 	}
 
-	// No match. Use the generic one.
-	return a.properties.Apk
+	archProps := reflect.ValueOf(a.archVariants).Elem().FieldByName("Arch")
+	archType := ctx.Config().Targets[android.Android][0].Arch.ArchType
+	MergePropertiesFromVariant(ctx, &a.properties, archProps, archType.Name)
+}
+
+func MergePropertiesFromVariant(ctx android.EarlyModuleContext,
+	dst interface{}, variantGroup reflect.Value, variant string) {
+	src := variantGroup.FieldByName(proptools.FieldNameForProperty(variant))
+	if !src.IsValid() {
+		return
+	}
+
+	err := proptools.ExtendMatchingProperties([]interface{}{dst}, src.Interface(), nil, proptools.OrderAppend)
+	if err != nil {
+		if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
+			ctx.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
+		} else {
+			panic(err)
+		}
+	}
 }
 
 func (a *AndroidAppImport) DepsMutator(ctx android.BottomUpMutatorContext) {
@@ -792,7 +929,7 @@
 	rule := android.NewRuleBuilder()
 	rule.Command().
 		Textf(`if (zipinfo %s 'lib/*.so' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then`, inputPath).
-		Tool(ctx.Config().HostToolPath(ctx, "zip2zip")).
+		BuiltTool(ctx, "zip2zip").
 		FlagWithInput("-i ", inputPath).
 		FlagWithOutput("-o ", outputPath).
 		FlagWithArg("-0 ", "'lib/**/*.so'").
@@ -807,7 +944,7 @@
 	}
 
 	// Uncompress dex in APKs of privileged apps
-	if ctx.Config().UncompressPrivAppDex() && Bool(a.properties.Privileged) {
+	if ctx.Config().UncompressPrivAppDex() && a.Privileged() {
 		return true
 	}
 
@@ -819,7 +956,7 @@
 	rule := android.NewRuleBuilder()
 	rule.Command().
 		Textf(`if (zipinfo %s '*.dex' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then`, inputPath).
-		Tool(ctx.Config().HostToolPath(ctx, "zip2zip")).
+		BuiltTool(ctx, "zip2zip").
 		FlagWithInput("-i ", inputPath).
 		FlagWithOutput("-o ", outputPath).
 		FlagWithArg("-0 ", "'classes*.dex'").
@@ -828,20 +965,30 @@
 }
 
 func (a *AndroidAppImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	if String(a.properties.Certificate) == "" && !Bool(a.properties.Presigned) {
-		ctx.PropertyErrorf("certificate", "No certificate specified for prebuilt")
+	a.generateAndroidBuildActions(ctx)
+}
+
+func (a *AndroidAppImport) generateAndroidBuildActions(ctx android.ModuleContext) {
+	numCertPropsSet := 0
+	if String(a.properties.Certificate) != "" {
+		numCertPropsSet++
 	}
-	if String(a.properties.Certificate) != "" && Bool(a.properties.Presigned) {
-		ctx.PropertyErrorf("certificate", "Certificate can't be specified for presigned modules")
+	if Bool(a.properties.Presigned) {
+		numCertPropsSet++
+	}
+	if Bool(a.properties.Default_dev_cert) {
+		numCertPropsSet++
+	}
+	if numCertPropsSet != 1 {
+		ctx.ModuleErrorf("One and only one of certficate, presigned, and default_dev_cert properties must be set")
 	}
 
-	_, certificates := collectAppDeps(ctx)
+	_, certificates := collectAppDeps(ctx, false)
 
 	// TODO: LOCAL_EXTRACT_APK/LOCAL_EXTRACT_DPI_APK
 	// TODO: LOCAL_PACKAGE_SPLITS
 
-	var srcApk android.Path
-	srcApk = android.PathForModuleSrc(ctx, a.getSrcApkPath(ctx))
+	srcApk := a.prebuilt.SingleSourcePath(ctx)
 
 	if a.usesLibrary.enforceUsesLibraries() {
 		srcApk = a.usesLibrary.verifyUsesLibrariesAPK(ctx, srcApk)
@@ -853,7 +1000,13 @@
 	jnisUncompressed := android.PathForModuleOut(ctx, "jnis-uncompressed", ctx.ModuleName()+".apk")
 	a.uncompressEmbeddedJniLibs(ctx, srcApk, jnisUncompressed.OutputPath)
 
-	installDir := android.PathForModuleInstall(ctx, "app", a.BaseModuleName())
+	var installDir android.InstallPath
+	if Bool(a.properties.Privileged) {
+		installDir = android.PathForModuleInstall(ctx, "priv-app", a.BaseModuleName())
+	} else {
+		installDir = android.PathForModuleInstall(ctx, "app", a.BaseModuleName())
+	}
+
 	a.dexpreopter.installPath = installDir.Join(ctx, a.BaseModuleName()+".apk")
 	a.dexpreopter.isInstallable = true
 	a.dexpreopter.isPresignedPrebuilt = Bool(a.properties.Presigned)
@@ -874,7 +1027,9 @@
 	// Sign or align the package
 	// TODO: Handle EXTERNAL
 	if !Bool(a.properties.Presigned) {
-		certificates = processMainCert(a.ModuleBase, *a.properties.Certificate, certificates, ctx)
+		// 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.
+		certificates = processMainCert(a.ModuleBase, String(a.properties.Certificate), certificates, ctx)
 		if len(certificates) != 1 {
 			ctx.ModuleErrorf("Unexpected number of certificates were extracted: %q", certificates)
 		}
@@ -890,7 +1045,8 @@
 
 	// TODO: Optionally compress the output apk.
 
-	ctx.InstallFile(installDir, a.BaseModuleName()+".apk", a.outputFile)
+	a.installPath = ctx.InstallFile(installDir,
+		proptools.StringDefault(a.properties.Filename, a.BaseModuleName()+".apk"), a.outputFile)
 
 	// TODO: androidmk converter jni libs
 }
@@ -903,16 +1059,121 @@
 	return a.prebuilt.Name(a.ModuleBase.Name())
 }
 
+func (a *AndroidAppImport) OutputFile() android.Path {
+	return a.outputFile
+}
+
+func (a *AndroidAppImport) JacocoReportClassesFile() android.Path {
+	return nil
+}
+
+var dpiVariantGroupType reflect.Type
+var archVariantGroupType reflect.Type
+
+func initAndroidAppImportVariantGroupTypes() {
+	dpiVariantGroupType = createVariantGroupType(supportedDpis, "Dpi_variants")
+
+	archNames := make([]string, len(android.ArchTypeList()))
+	for i, archType := range android.ArchTypeList() {
+		archNames[i] = archType.Name
+	}
+	archVariantGroupType = createVariantGroupType(archNames, "Arch")
+}
+
+// Populates all variant struct properties at creation time.
+func (a *AndroidAppImport) populateAllVariantStructs() {
+	a.dpiVariants = reflect.New(dpiVariantGroupType).Interface()
+	a.AddProperties(a.dpiVariants)
+
+	a.archVariants = reflect.New(archVariantGroupType).Interface()
+	a.AddProperties(a.archVariants)
+}
+
+func (a *AndroidAppImport) Privileged() bool {
+	return Bool(a.properties.Privileged)
+}
+
+func createVariantGroupType(variants []string, variantGroupName string) reflect.Type {
+	props := reflect.TypeOf((*AndroidAppImportProperties)(nil))
+
+	variantFields := make([]reflect.StructField, len(variants))
+	for i, variant := range variants {
+		variantFields[i] = reflect.StructField{
+			Name: proptools.FieldNameForProperty(variant),
+			Type: props,
+		}
+	}
+
+	variantGroupStruct := reflect.StructOf(variantFields)
+	return reflect.StructOf([]reflect.StructField{
+		{
+			Name: variantGroupName,
+			Type: variantGroupStruct,
+		},
+	})
+}
+
 // android_app_import imports a prebuilt apk with additional processing specified in the module.
+// DPI-specific apk source files can be specified using dpi_variants. Example:
+//
+//     android_app_import {
+//         name: "example_import",
+//         apk: "prebuilts/example.apk",
+//         dpi_variants: {
+//             mdpi: {
+//                 apk: "prebuilts/example_mdpi.apk",
+//             },
+//             xhdpi: {
+//                 apk: "prebuilts/example_xhdpi.apk",
+//             },
+//         },
+//         certificate: "PRESIGNED",
+//     }
 func AndroidAppImportFactory() android.Module {
 	module := &AndroidAppImport{}
-	module.properties.Dpi_variants = reflect.New(dpiVariantsStruct).Interface()
 	module.AddProperties(&module.properties)
 	module.AddProperties(&module.dexpreoptProperties)
 	module.AddProperties(&module.usesLibrary.usesLibraryProperties)
+	module.populateAllVariantStructs()
+	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
+		module.processVariants(ctx)
+	})
 
 	InitJavaModule(module, android.DeviceSupported)
-	android.InitSingleSourcePrebuiltModule(module, &module.properties.Apk)
+	android.InitSingleSourcePrebuiltModule(module, &module.properties, "Apk")
+
+	return module
+}
+
+type AndroidTestImport struct {
+	AndroidAppImport
+
+	testProperties testProperties
+
+	data android.Paths
+}
+
+func (a *AndroidTestImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	a.generateAndroidBuildActions(ctx)
+
+	a.data = android.PathsForModuleSrc(ctx, a.testProperties.Data)
+}
+
+// android_test_import imports a prebuilt test apk with additional processing specified in the
+// module. DPI or arch variant configurations can be made as with android_app_import.
+func AndroidTestImportFactory() android.Module {
+	module := &AndroidTestImport{}
+	module.AddProperties(&module.properties)
+	module.AddProperties(&module.dexpreoptProperties)
+	module.AddProperties(&module.usesLibrary.usesLibraryProperties)
+	module.AddProperties(&module.testProperties)
+	module.populateAllVariantStructs()
+	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
+		module.processVariants(ctx)
+	})
+
+	InitJavaModule(module, android.DeviceSupported)
+	android.InitSingleSourcePrebuiltModule(module, &module.properties, "Apk")
 
 	return module
 }
@@ -1004,7 +1265,7 @@
 	outputFile := android.PathForModuleOut(ctx, "manifest_check", "AndroidManifest.xml")
 
 	rule := android.NewRuleBuilder()
-	cmd := rule.Command().Tool(ctx.Config().HostToolPath(ctx, "manifest_check")).
+	cmd := rule.Command().BuiltTool(ctx, "manifest_check").
 		Flag("--enforce-uses-libraries").
 		Input(manifest).
 		FlagWithOutput("-o ", outputFile)
diff --git a/java/app_builder.go b/java/app_builder.go
index 348c8b4..5e7fbe6 100644
--- a/java/app_builder.go
+++ b/java/app_builder.go
@@ -31,31 +31,13 @@
 var (
 	Signapk = pctx.AndroidStaticRule("signapk",
 		blueprint.RuleParams{
-			Command: `${config.JavaCmd} ${config.JavaVmFlags} -Djava.library.path=$$(dirname $signapkJniLibrary) ` +
-				`-jar $signapkCmd $flags $certificates $in $out`,
-			CommandDeps: []string{"$signapkCmd", "$signapkJniLibrary"},
+			Command: `${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")
-
-	androidManifestMerger = pctx.AndroidStaticRule("androidManifestMerger",
-		blueprint.RuleParams{
-			Command: "java -classpath $androidManifestMergerCmd com.android.manifmerger.Main merge " +
-				"--main $in --libs $libsManifests --out $out",
-			CommandDeps: []string{"$androidManifestMergerCmd"},
-			Description: "merge manifest files",
-		},
-		"libsManifests")
 )
 
-func init() {
-	pctx.SourcePathVariable("androidManifestMergerCmd", "prebuilts/devtools/tools/lib/manifest-merger.jar")
-	pctx.HostBinToolVariable("aaptCmd", "aapt")
-	pctx.HostJavaToolVariable("signapkCmd", "signapk.jar")
-	// TODO(ccross): this should come from the signapk dependencies, but we don't have any way
-	// to express host JNI dependencies yet.
-	pctx.HostJNIToolVariable("signapkJniLibrary", "libconscrypt_openjdk_jni")
-}
-
 var combineApk = pctx.AndroidStaticRule("combineApk",
 	blueprint.RuleParams{
 		Command:     `${config.MergeZipsCmd} $out $in`,
@@ -218,14 +200,14 @@
 	}
 
 	if uncompressJNI {
-		jarArgs = append(jarArgs, "-L 0")
+		jarArgs = append(jarArgs, "-L", "0")
 	}
 
 	for _, j := range jniLibs {
 		deps = append(deps, j.path)
 		jarArgs = append(jarArgs,
-			"-P "+targetToJniDir(j.target),
-			"-f "+j.path.String())
+			"-P", targetToJniDir(j.target),
+			"-f", j.path.String())
 	}
 
 	ctx.Build(pctx, android.BuildParams{
diff --git a/java/app_test.go b/java/app_test.go
index 27802cd..9bdef4e 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -43,7 +43,7 @@
 	}
 )
 
-func testAppContext(config android.Config, bp string, fs map[string][]byte) *android.TestContext {
+func testAppConfig(env map[string]string, bp string, fs map[string][]byte) android.Config {
 	appFS := map[string][]byte{}
 	for k, v := range fs {
 		appFS[k] = v
@@ -53,13 +53,13 @@
 		appFS[file] = nil
 	}
 
-	return testContext(config, bp, appFS)
+	return testConfig(env, bp, appFS)
 }
 
 func testApp(t *testing.T, bp string) *android.TestContext {
-	config := testConfig(nil)
+	config := testAppConfig(nil, bp, nil)
 
-	ctx := testAppContext(config, bp, nil)
+	ctx := testContext()
 
 	run(t, ctx, config)
 
@@ -72,6 +72,7 @@
 			ctx := testApp(t, moduleType+` {
 					name: "foo",
 					srcs: ["a.java"],
+					sdk_version: "current"
 				}
 			`)
 
@@ -117,6 +118,7 @@
 					name: "foo",
 					srcs: ["a.java"],
 					package_splits: ["v4", "v7,hdpi"],
+					sdk_version: "current"
 				}`)
 
 	foo := ctx.ModuleForTests("foo", "android_common")
@@ -139,6 +141,129 @@
 	}
 }
 
+func TestPlatformAPIs(t *testing.T) {
+	testJava(t, `
+		android_app {
+			name: "foo",
+			srcs: ["a.java"],
+			platform_apis: true,
+		}
+	`)
+
+	testJava(t, `
+		android_app {
+			name: "foo",
+			srcs: ["a.java"],
+			sdk_version: "current",
+		}
+	`)
+
+	testJavaError(t, "platform_apis must be true when sdk_version is empty.", `
+		android_app {
+			name: "bar",
+			srcs: ["b.java"],
+		}
+	`)
+
+	testJavaError(t, "platform_apis must be false when sdk_version is not empty.", `
+		android_app {
+			name: "bar",
+			srcs: ["b.java"],
+			sdk_version: "system_current",
+			platform_apis: true,
+		}
+	`)
+}
+
+func TestAndroidAppLinkType(t *testing.T) {
+	testJava(t, `
+		android_app {
+			name: "foo",
+			srcs: ["a.java"],
+			libs: ["bar"],
+			static_libs: ["baz"],
+			platform_apis: true,
+		}
+
+		java_library {
+			name: "bar",
+			sdk_version: "current",
+			srcs: ["b.java"],
+		}
+
+		android_library {
+			name: "baz",
+			sdk_version: "system_current",
+			srcs: ["c.java"],
+		}
+	`)
+
+	testJavaError(t, "Adjust sdk_version: property of the source or target module so that target module is built with the same or smaller API set than the source.", `
+		android_app {
+			name: "foo",
+			srcs: ["a.java"],
+			libs: ["bar"],
+			sdk_version: "current",
+			static_libs: ["baz"],
+		}
+
+		java_library {
+			name: "bar",
+			sdk_version: "current",
+			srcs: ["b.java"],
+		}
+
+		android_library {
+			name: "baz",
+			sdk_version: "system_current",
+			srcs: ["c.java"],
+		}
+	`)
+
+	testJava(t, `
+		android_app {
+			name: "foo",
+			srcs: ["a.java"],
+			libs: ["bar"],
+			sdk_version: "system_current",
+			static_libs: ["baz"],
+		}
+
+		java_library {
+			name: "bar",
+			sdk_version: "current",
+			srcs: ["b.java"],
+		}
+
+		android_library {
+			name: "baz",
+			sdk_version: "system_current",
+			srcs: ["c.java"],
+		}
+	`)
+
+	testJavaError(t, "Adjust sdk_version: property of the source or target module so that target module is built with the same or smaller API set than the source.", `
+		android_app {
+			name: "foo",
+			srcs: ["a.java"],
+			libs: ["bar"],
+			sdk_version: "system_current",
+			static_libs: ["baz"],
+		}
+
+		java_library {
+			name: "bar",
+			sdk_version: "current",
+			srcs: ["b.java"],
+		}
+
+		android_library {
+			name: "baz",
+			srcs: ["c.java"],
+		}
+	`)
+}
+
 func TestResourceDirs(t *testing.T) {
 	testCases := []struct {
 		name      string
@@ -169,14 +294,15 @@
 	bp := `
 			android_app {
 				name: "foo",
+				sdk_version: "current",
 				%s
 			}
 		`
 
 	for _, testCase := range testCases {
 		t.Run(testCase.name, func(t *testing.T) {
-			config := testConfig(nil)
-			ctx := testContext(config, fmt.Sprintf(bp, testCase.prop), fs)
+			config := testConfig(nil, fmt.Sprintf(bp, testCase.prop), fs)
+			ctx := testContext()
 			run(t, ctx, config)
 
 			module := ctx.ModuleForTests("foo", "android_common")
@@ -349,36 +475,41 @@
 	bp := `
 			android_app {
 				name: "foo",
+				sdk_version: "current",
 				resource_dirs: ["foo/res"],
 				static_libs: ["lib", "lib3"],
 			}
 
 			android_app {
 				name: "bar",
+				sdk_version: "current",
 				resource_dirs: ["bar/res"],
 			}
 
 			android_library {
 				name: "lib",
+				sdk_version: "current",
 				resource_dirs: ["lib/res"],
 				static_libs: ["lib2"],
 			}
 
 			android_library {
 				name: "lib2",
+				sdk_version: "current",
 				resource_dirs: ["lib2/res"],
 			}
 
 			// This library has the same resources as lib (should not lead to dupe RROs)
 			android_library {
 				name: "lib3",
+				sdk_version: "current",
 				resource_dirs: ["lib/res"]
 			}
 		`
 
 	for _, testCase := range testCases {
 		t.Run(testCase.name, func(t *testing.T) {
-			config := testConfig(nil)
+			config := testAppConfig(nil, bp, fs)
 			config.TestProductVariables.DeviceResourceOverlays = deviceResourceOverlays
 			config.TestProductVariables.ProductResourceOverlays = productResourceOverlays
 			if testCase.enforceRROTargets != nil {
@@ -388,7 +519,7 @@
 				config.TestProductVariables.EnforceRROExcludedOverlays = testCase.enforceRROExcludedOverlays
 			}
 
-			ctx := testAppContext(config, bp, fs)
+			ctx := testContext()
 			run(t, ctx, config)
 
 			resourceListToFiles := func(module android.TestingModule, list []string) (files []string) {
@@ -461,6 +592,7 @@
 		platformSdkCodename   string
 		platformSdkFinal      bool
 		expectedMinSdkVersion string
+		platformApis          bool
 	}{
 		{
 			name:                  "current final SDK",
@@ -481,6 +613,7 @@
 		{
 			name:                  "default final SDK",
 			sdkVersion:            "",
+			platformApis:          true,
 			platformSdkInt:        27,
 			platformSdkCodename:   "REL",
 			platformSdkFinal:      true,
@@ -489,6 +622,7 @@
 		{
 			name:                  "default non-final SDK",
 			sdkVersion:            "",
+			platformApis:          true,
 			platformSdkInt:        27,
 			platformSdkCodename:   "OMR1",
 			platformSdkFinal:      false,
@@ -504,18 +638,23 @@
 	for _, moduleType := range []string{"android_app", "android_library"} {
 		for _, test := range testCases {
 			t.Run(moduleType+" "+test.name, func(t *testing.T) {
+				platformApiProp := ""
+				if test.platformApis {
+					platformApiProp = "platform_apis: true,"
+				}
 				bp := fmt.Sprintf(`%s {
 					name: "foo",
 					srcs: ["a.java"],
 					sdk_version: "%s",
-				}`, moduleType, test.sdkVersion)
+					%s
+				}`, moduleType, test.sdkVersion, platformApiProp)
 
-				config := testConfig(nil)
+				config := testAppConfig(nil, bp, nil)
 				config.TestProductVariables.Platform_sdk_version = &test.platformSdkInt
 				config.TestProductVariables.Platform_sdk_codename = &test.platformSdkCodename
 				config.TestProductVariables.Platform_sdk_final = &test.platformSdkFinal
 
-				ctx := testAppContext(config, bp, nil)
+				ctx := testContext()
 
 				run(t, ctx, config)
 
@@ -546,81 +685,8 @@
 	}
 }
 
-func TestJNIABI_no_framework_libs_true(t *testing.T) {
-	ctx := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
-		cc_library {
-			name: "libjni",
-			system_shared_libs: [],
-			stl: "none",
-		}
-
-		android_test {
-			name: "test",
-			no_framework_libs: true,
-			jni_libs: ["libjni"],
-		}
-
-		android_test {
-			name: "test_first",
-			no_framework_libs: true,
-			compile_multilib: "first",
-			jni_libs: ["libjni"],
-		}
-
-		android_test {
-			name: "test_both",
-			no_framework_libs: true,
-			compile_multilib: "both",
-			jni_libs: ["libjni"],
-		}
-
-		android_test {
-			name: "test_32",
-			no_framework_libs: true,
-			compile_multilib: "32",
-			jni_libs: ["libjni"],
-		}
-
-		android_test {
-			name: "test_64",
-			no_framework_libs: true,
-			compile_multilib: "64",
-			jni_libs: ["libjni"],
-		}
-		`)
-
-	testCases := []struct {
-		name string
-		abis []string
-	}{
-		{"test", []string{"arm64-v8a"}},
-		{"test_first", []string{"arm64-v8a"}},
-		{"test_both", []string{"arm64-v8a", "armeabi-v7a"}},
-		{"test_32", []string{"armeabi-v7a"}},
-		{"test_64", []string{"arm64-v8a"}},
-	}
-
-	for _, test := range testCases {
-		t.Run(test.name, func(t *testing.T) {
-			app := ctx.ModuleForTests(test.name, "android_common")
-			jniLibZip := app.Output("jnilibs.zip")
-			var abis []string
-			args := strings.Fields(jniLibZip.Args["jarArgs"])
-			for i := 0; i < len(args); i++ {
-				if args[i] == "-P" {
-					abis = append(abis, filepath.Base(args[i+1]))
-					i++
-				}
-			}
-			if !reflect.DeepEqual(abis, test.abis) {
-				t.Errorf("want abis %v, got %v", test.abis, abis)
-			}
-		})
-	}
-}
-
 func TestJNIABI(t *testing.T) {
-	ctx := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
+	ctx, _ := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
 		cc_library {
 			name: "libjni",
 			system_shared_libs: [],
@@ -692,91 +758,46 @@
 	}
 }
 
-func TestJNIPackaging_no_framework_libs_true(t *testing.T) {
-	ctx := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
-		cc_library {
-			name: "libjni",
-			system_shared_libs: [],
-			stl: "none",
-		}
-
+func TestAppSdkVersionByPartition(t *testing.T) {
+	testJavaError(t, "sdk_version must have a value when the module is located at vendor or product", `
 		android_app {
-			name: "app",
-			jni_libs: ["libjni"],
+			name: "foo",
+			srcs: ["a.java"],
+			vendor: true,
+			platform_apis: true,
 		}
+	`)
 
+	testJava(t, `
 		android_app {
-			name: "app_noembed",
-			jni_libs: ["libjni"],
-			use_embedded_native_libs: false,
+			name: "bar",
+			srcs: ["b.java"],
+			platform_apis: true,
 		}
+	`)
 
-		android_app {
-			name: "app_embed",
-			jni_libs: ["libjni"],
-			use_embedded_native_libs: true,
-		}
-
-		android_test {
-			name: "test",
-			no_framework_libs: true,
-			jni_libs: ["libjni"],
-		}
-
-		android_test {
-			name: "test_noembed",
-			no_framework_libs: true,
-			jni_libs: ["libjni"],
-			use_embedded_native_libs: false,
-		}
-
-		android_test_helper_app {
-			name: "test_helper",
-			no_framework_libs: true,
-			jni_libs: ["libjni"],
-		}
-
-		android_test_helper_app {
-			name: "test_helper_noembed",
-			no_framework_libs: true,
-			jni_libs: ["libjni"],
-			use_embedded_native_libs: false,
-		}
-		`)
-
-	testCases := []struct {
-		name       string
-		packaged   bool
-		compressed bool
-	}{
-		{"app", false, false},
-		{"app_noembed", false, false},
-		{"app_embed", true, false},
-		{"test", true, false},
-		{"test_noembed", true, true},
-		{"test_helper", true, false},
-		{"test_helper_noembed", true, true},
-	}
-
-	for _, test := range testCases {
-		t.Run(test.name, func(t *testing.T) {
-			app := ctx.ModuleForTests(test.name, "android_common")
-			jniLibZip := app.MaybeOutput("jnilibs.zip")
-			if g, w := (jniLibZip.Rule != nil), test.packaged; g != w {
-				t.Errorf("expected jni packaged %v, got %v", w, g)
+	for _, enforce := range []bool{true, false} {
+		bp := `
+			android_app {
+				name: "foo",
+				srcs: ["a.java"],
+				product_specific: true,
+				platform_apis: true,
 			}
+		`
 
-			if jniLibZip.Rule != nil {
-				if g, w := !strings.Contains(jniLibZip.Args["jarArgs"], "-L 0"), test.compressed; g != w {
-					t.Errorf("expected jni compressed %v, got %v", w, g)
-				}
-			}
-		})
+		config := testAppConfig(nil, bp, nil)
+		config.TestProductVariables.EnforceProductPartitionInterface = proptools.BoolPtr(enforce)
+		if enforce {
+			testJavaErrorWithConfig(t, "sdk_version must have a value when the module is located at vendor or product", config)
+		} else {
+			testJavaWithConfig(t, config)
+		}
 	}
 }
 
 func TestJNIPackaging(t *testing.T) {
-	ctx := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
+	ctx, _ := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
 		cc_library {
 			name: "libjni",
 			system_shared_libs: [],
@@ -786,18 +807,21 @@
 		android_app {
 			name: "app",
 			jni_libs: ["libjni"],
+			sdk_version: "current",
 		}
 
 		android_app {
 			name: "app_noembed",
 			jni_libs: ["libjni"],
 			use_embedded_native_libs: false,
+			sdk_version: "current",
 		}
 
 		android_app {
 			name: "app_embed",
 			jni_libs: ["libjni"],
 			use_embedded_native_libs: true,
+			sdk_version: "current",
 		}
 
 		android_test {
@@ -871,6 +895,7 @@
 				android_app {
 					name: "foo",
 					srcs: ["a.java"],
+					sdk_version: "current",
 				}
 			`,
 			certificateOverride: "",
@@ -882,7 +907,8 @@
 				android_app {
 					name: "foo",
 					srcs: ["a.java"],
-					certificate: ":new_certificate"
+					certificate: ":new_certificate",
+					sdk_version: "current",
 				}
 
 				android_app_certificate {
@@ -899,7 +925,8 @@
 				android_app {
 					name: "foo",
 					srcs: ["a.java"],
-					certificate: "expiredkey"
+					certificate: "expiredkey",
+					sdk_version: "current",
 				}
 			`,
 			certificateOverride: "",
@@ -911,7 +938,8 @@
 				android_app {
 					name: "foo",
 					srcs: ["a.java"],
-					certificate: "expiredkey"
+					certificate: "expiredkey",
+					sdk_version: "current",
 				}
 
 				android_app_certificate {
@@ -926,11 +954,11 @@
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
-			config := testConfig(nil)
+			config := testAppConfig(nil, test.bp, nil)
 			if test.certificateOverride != "" {
 				config.TestProductVariables.CertificateOverrides = []string{test.certificateOverride}
 			}
-			ctx := testAppContext(config, test.bp, nil)
+			ctx := testContext()
 
 			run(t, ctx, config)
 			foo := ctx.ModuleForTests("foo", "android_common")
@@ -957,6 +985,7 @@
 				android_app {
 					name: "foo",
 					srcs: ["a.java"],
+					sdk_version: "current",
 				}
 			`,
 			packageNameOverride: "",
@@ -971,12 +1000,13 @@
 				android_app {
 					name: "foo",
 					srcs: ["a.java"],
+					sdk_version: "current",
 				}
 			`,
 			packageNameOverride: "foo:bar",
 			expected: []string{
 				// The package apk should be still be the original name for test dependencies.
-				buildDir + "/.intermediates/foo/android_common/foo.apk",
+				buildDir + "/.intermediates/foo/android_common/bar.apk",
 				buildDir + "/target/product/test_device/system/app/bar/bar.apk",
 			},
 		},
@@ -984,11 +1014,11 @@
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
-			config := testConfig(nil)
+			config := testAppConfig(nil, test.bp, nil)
 			if test.packageNameOverride != "" {
 				config.TestProductVariables.PackageNameOverrides = []string{test.packageNameOverride}
 			}
-			ctx := testAppContext(config, test.bp, nil)
+			ctx := testContext()
 
 			run(t, ctx, config)
 			foo := ctx.ModuleForTests("foo", "android_common")
@@ -1012,16 +1042,18 @@
 		android_app {
 			name: "foo",
 			srcs: ["a.java"],
+			sdk_version: "current",
 		}
 
 		android_test {
 			name: "bar",
 			instrumentation_for: "foo",
+			sdk_version: "current",
 		}
 		`
-	config := testConfig(nil)
+	config := testAppConfig(nil, bp, nil)
 	config.TestProductVariables.ManifestPackageNameOverrides = []string{"foo:org.dandroid.bp"}
-	ctx := testAppContext(config, bp, nil)
+	ctx := testContext()
 
 	run(t, ctx, config)
 
@@ -1035,12 +1067,13 @@
 }
 
 func TestOverrideAndroidApp(t *testing.T) {
-	ctx := testJava(t, `
+	ctx, _ := testJava(t, `
 		android_app {
 			name: "foo",
 			srcs: ["a.java"],
 			certificate: "expiredkey",
 			overrides: ["qux"],
+			sdk_version: "current",
 		}
 
 		override_android_app {
@@ -1113,7 +1146,7 @@
 		}
 
 		// Check the certificate paths
-		signapk := variant.Output("foo.apk")
+		signapk := variant.Output(expected.moduleName + ".apk")
 		signFlag := signapk.Args["certificates"]
 		if expected.signFlag != signFlag {
 			t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected.signFlag, signFlag)
@@ -1136,10 +1169,11 @@
 }
 
 func TestOverrideAndroidAppDependency(t *testing.T) {
-	ctx := testJava(t, `
+	ctx, _ := testJava(t, `
 		android_app {
 			name: "foo",
 			srcs: ["a.java"],
+			sdk_version: "current",
 		}
 
 		override_android_app {
@@ -1176,8 +1210,103 @@
 	}
 }
 
+func TestOverrideAndroidTest(t *testing.T) {
+	ctx, _ := testJava(t, `
+		android_app {
+			name: "foo",
+			srcs: ["a.java"],
+			package_name: "com.android.foo",
+			sdk_version: "current",
+		}
+
+		override_android_app {
+			name: "bar",
+			base: "foo",
+			package_name: "com.android.bar",
+		}
+
+		android_test {
+			name: "foo_test",
+			srcs: ["b.java"],
+			instrumentation_for: "foo",
+		}
+
+		override_android_test {
+			name: "bar_test",
+			base: "foo_test",
+			package_name: "com.android.bar.test",
+			instrumentation_for: "bar",
+			instrumentation_target_package: "com.android.bar",
+		}
+		`)
+
+	expectedVariants := []struct {
+		moduleName        string
+		variantName       string
+		apkPath           string
+		overrides         []string
+		targetVariant     string
+		packageFlag       string
+		targetPackageFlag string
+	}{
+		{
+			variantName:       "android_common",
+			apkPath:           "/target/product/test_device/testcases/foo_test/arm64/foo_test.apk",
+			overrides:         nil,
+			targetVariant:     "android_common",
+			packageFlag:       "",
+			targetPackageFlag: "",
+		},
+		{
+			variantName:       "android_common_bar_test",
+			apkPath:           "/target/product/test_device/testcases/bar_test/arm64/bar_test.apk",
+			overrides:         []string{"foo_test"},
+			targetVariant:     "android_common_bar",
+			packageFlag:       "com.android.bar.test",
+			targetPackageFlag: "com.android.bar",
+		},
+	}
+	for _, expected := range expectedVariants {
+		variant := ctx.ModuleForTests("foo_test", expected.variantName)
+
+		// Check the final apk name
+		outputs := variant.AllOutputs()
+		expectedApkPath := buildDir + expected.apkPath
+		found := false
+		for _, o := range outputs {
+			if o == expectedApkPath {
+				found = true
+				break
+			}
+		}
+		if !found {
+			t.Errorf("Can't find %q in output files.\nAll outputs:%v", expectedApkPath, outputs)
+		}
+
+		// Check if the overrides field values are correctly aggregated.
+		mod := variant.Module().(*AndroidTest)
+		if !reflect.DeepEqual(expected.overrides, mod.appProperties.Overrides) {
+			t.Errorf("Incorrect overrides property value, expected: %q, got: %q",
+				expected.overrides, mod.appProperties.Overrides)
+		}
+
+		// Check if javac classpath has the correct jar file path. This checks instrumentation_for overrides.
+		javac := variant.Rule("javac")
+		turbine := filepath.Join(buildDir, ".intermediates", "foo", expected.targetVariant, "turbine-combined", "foo.jar")
+		if !strings.Contains(javac.Args["classpath"], turbine) {
+			t.Errorf("classpath %q does not contain %q", javac.Args["classpath"], turbine)
+		}
+
+		// Check aapt2 flags.
+		res := variant.Output("package-res.apk")
+		aapt2Flags := res.Args["flags"]
+		checkAapt2LinkFlag(t, aapt2Flags, "rename-manifest-package", expected.packageFlag)
+		checkAapt2LinkFlag(t, aapt2Flags, "rename-instrumentation-target-package", expected.targetPackageFlag)
+	}
+}
+
 func TestAndroidAppImport(t *testing.T) {
-	ctx := testJava(t, `
+	ctx, _ := testJava(t, `
 		android_app_import {
 			name: "foo",
 			apk: "prebuilts/apk/app.apk",
@@ -1206,7 +1335,7 @@
 }
 
 func TestAndroidAppImport_NoDexPreopt(t *testing.T) {
-	ctx := testJava(t, `
+	ctx, _ := testJava(t, `
 		android_app_import {
 			name: "foo",
 			apk: "prebuilts/apk/app.apk",
@@ -1227,7 +1356,7 @@
 }
 
 func TestAndroidAppImport_Presigned(t *testing.T) {
-	ctx := testJava(t, `
+	ctx, _ := testJava(t, `
 		android_app_import {
 			name: "foo",
 			apk: "prebuilts/apk/app.apk",
@@ -1245,13 +1374,7 @@
 		variant.MaybeOutput("dexpreopt/oat/arm64/package.odex").Rule == nil {
 		t.Errorf("can't find dexpreopt outputs")
 	}
-	// Make sure stripping wasn't done.
-	stripRule := variant.Output("dexpreopt/foo.apk")
-	if !strings.HasPrefix(stripRule.RuleParams.Command, "cp -f") {
-		t.Errorf("unexpected, non-skipping strip command: %q", stripRule.RuleParams.Command)
-	}
-
-	// Make sure signing was skipped and aligning was done instead.
+	// Make sure signing was skipped and aligning was done.
 	if variant.MaybeOutput("signed/foo.apk").Rule != nil {
 		t.Errorf("signing rule shouldn't be included.")
 	}
@@ -1260,6 +1383,35 @@
 	}
 }
 
+func TestAndroidAppImport_DefaultDevCert(t *testing.T) {
+	ctx, _ := testJava(t, `
+		android_app_import {
+			name: "foo",
+			apk: "prebuilts/apk/app.apk",
+			default_dev_cert: true,
+			dex_preopt: {
+				enabled: true,
+			},
+		}
+		`)
+
+	variant := ctx.ModuleForTests("foo", "android_common")
+
+	// Check dexpreopt outputs.
+	if variant.MaybeOutput("dexpreopt/oat/arm64/package.vdex").Rule == nil ||
+		variant.MaybeOutput("dexpreopt/oat/arm64/package.odex").Rule == nil {
+		t.Errorf("can't find dexpreopt outputs")
+	}
+
+	// Check cert signing flag.
+	signedApk := variant.Output("signed/foo.apk")
+	signingFlag := signedApk.Args["certificates"]
+	expected := "build/make/target/product/security/testkey.x509.pem build/make/target/product/security/testkey.pk8"
+	if expected != signingFlag {
+		t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag)
+	}
+}
+
 func TestAndroidAppImport_DpiVariants(t *testing.T) {
 	bp := `
 		android_app_import {
@@ -1273,7 +1425,7 @@
 					apk: "prebuilts/apk/app_xxhdpi.apk",
 				},
 			},
-			certificate: "PRESIGNED",
+			presigned: true,
 			dex_preopt: {
 				enabled: true,
 			},
@@ -1294,7 +1446,7 @@
 		{
 			name:                "AAPTPreferredConfig matches",
 			aaptPreferredConfig: proptools.StringPtr("xhdpi"),
-			aaptPrebuiltDPI:     []string{"xxhdpi", "lhdpi"},
+			aaptPrebuiltDPI:     []string{"xxhdpi", "ldpi"},
 			expected:            "prebuilts/apk/app_xhdpi.apk",
 		},
 		{
@@ -1319,10 +1471,10 @@
 
 	jniRuleRe := regexp.MustCompile("^if \\(zipinfo (\\S+)")
 	for _, test := range testCases {
-		config := testConfig(nil)
+		config := testAppConfig(nil, bp, nil)
 		config.TestProductVariables.AAPTPreferredConfig = test.aaptPreferredConfig
 		config.TestProductVariables.AAPTPrebuiltDPI = test.aaptPrebuiltDPI
-		ctx := testAppContext(config, bp, nil)
+		ctx := testContext()
 
 		run(t, ctx, config)
 
@@ -1338,10 +1490,186 @@
 	}
 }
 
+func TestAndroidAppImport_Filename(t *testing.T) {
+	ctx, config := testJava(t, `
+		android_app_import {
+			name: "foo",
+			apk: "prebuilts/apk/app.apk",
+			presigned: true,
+		}
+
+		android_app_import {
+			name: "bar",
+			apk: "prebuilts/apk/app.apk",
+			presigned: true,
+			filename: "bar_sample.apk"
+		}
+		`)
+
+	testCases := []struct {
+		name     string
+		expected string
+	}{
+		{
+			name:     "foo",
+			expected: "foo.apk",
+		},
+		{
+			name:     "bar",
+			expected: "bar_sample.apk",
+		},
+	}
+
+	for _, test := range testCases {
+		variant := ctx.ModuleForTests(test.name, "android_common")
+		if variant.MaybeOutput(test.expected).Rule == nil {
+			t.Errorf("can't find output named %q - all outputs: %v", test.expected, variant.AllOutputs())
+		}
+
+		a := variant.Module().(*AndroidAppImport)
+		expectedValues := []string{test.expected}
+		actualValues := android.AndroidMkEntriesForTest(
+			t, config, "", a)[0].EntryMap["LOCAL_INSTALLED_MODULE_STEM"]
+		if !reflect.DeepEqual(actualValues, expectedValues) {
+			t.Errorf("Incorrect LOCAL_INSTALLED_MODULE_STEM value '%s', expected '%s'",
+				actualValues, expectedValues)
+		}
+	}
+}
+
+func TestAndroidAppImport_ArchVariants(t *testing.T) {
+	// The test config's target arch is ARM64.
+	testCases := []struct {
+		name     string
+		bp       string
+		expected string
+	}{
+		{
+			name: "matching arch",
+			bp: `
+				android_app_import {
+					name: "foo",
+					apk: "prebuilts/apk/app.apk",
+					arch: {
+						arm64: {
+							apk: "prebuilts/apk/app_arm64.apk",
+						},
+					},
+					presigned: true,
+					dex_preopt: {
+						enabled: true,
+					},
+				}
+			`,
+			expected: "prebuilts/apk/app_arm64.apk",
+		},
+		{
+			name: "no matching arch",
+			bp: `
+				android_app_import {
+					name: "foo",
+					apk: "prebuilts/apk/app.apk",
+					arch: {
+						arm: {
+							apk: "prebuilts/apk/app_arm.apk",
+						},
+					},
+					presigned: true,
+					dex_preopt: {
+						enabled: true,
+					},
+				}
+			`,
+			expected: "prebuilts/apk/app.apk",
+		},
+	}
+
+	jniRuleRe := regexp.MustCompile("^if \\(zipinfo (\\S+)")
+	for _, test := range testCases {
+		ctx, _ := testJava(t, test.bp)
+
+		variant := ctx.ModuleForTests("foo", "android_common")
+		jniRuleCommand := variant.Output("jnis-uncompressed/foo.apk").RuleParams.Command
+		matches := jniRuleRe.FindStringSubmatch(jniRuleCommand)
+		if len(matches) != 2 {
+			t.Errorf("failed to extract the src apk path from %q", jniRuleCommand)
+		}
+		if test.expected != matches[1] {
+			t.Errorf("wrong src apk, expected: %q got: %q", test.expected, matches[1])
+		}
+	}
+}
+
+func TestAndroidTestImport(t *testing.T) {
+	ctx, config := testJava(t, `
+		android_test_import {
+			name: "foo",
+			apk: "prebuilts/apk/app.apk",
+			presigned: true,
+			data: [
+				"testdata/data",
+			],
+		}
+		`)
+
+	test := ctx.ModuleForTests("foo", "android_common").Module().(*AndroidTestImport)
+
+	// Check android mks.
+	entries := android.AndroidMkEntriesForTest(t, config, "", test)[0]
+	expected := []string{"tests"}
+	actual := entries.EntryMap["LOCAL_MODULE_TAGS"]
+	if !reflect.DeepEqual(expected, actual) {
+		t.Errorf("Unexpected module tags - expected: %q, actual: %q", expected, actual)
+	}
+	expected = []string{"testdata/data:testdata/data"}
+	actual = entries.EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"]
+	if !reflect.DeepEqual(expected, actual) {
+		t.Errorf("Unexpected test data - expected: %q, actual: %q", expected, actual)
+	}
+}
+
 func TestStl(t *testing.T) {
-	ctx := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
+	ctx, _ := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
+		cc_library {
+			name: "ndk_libunwind",
+			sdk_version: "current",
+			stl: "none",
+			system_shared_libs: [],
+		}
+
+		cc_library {
+			name: "libc.ndk.current",
+			sdk_version: "current",
+			stl: "none",
+			system_shared_libs: [],
+		}
+
+		cc_library {
+			name: "libm.ndk.current",
+			sdk_version: "current",
+			stl: "none",
+			system_shared_libs: [],
+		}
+
+		cc_library {
+			name: "libdl.ndk.current",
+			sdk_version: "current",
+			stl: "none",
+			system_shared_libs: [],
+		}
+
+		cc_object {
+			name: "ndk_crtbegin_so.27",
+		}
+
+		cc_object {
+			name: "ndk_crtend_so.27",
+		}
+
 		cc_library {
 			name: "libjni",
+			sdk_version: "current",
+			stl: "c++_shared",
 		}
 
 		android_test {
@@ -1409,18 +1737,21 @@
 			name: "foo",
 			srcs: ["a.java"],
 			api_packages: ["foo"],
+			sdk_version: "current",
 		}
 
 		java_sdk_library {
 			name: "bar",
 			srcs: ["a.java"],
 			api_packages: ["bar"],
+			sdk_version: "current",
 		}
 
 		android_app {
 			name: "app",
 			srcs: ["a.java"],
 			uses_libs: ["foo"],
+			sdk_version: "current",
 			optional_uses_libs: [
 				"bar",
 				"baz",
@@ -1439,10 +1770,10 @@
 		}
 	`
 
-	config := testConfig(nil)
+	config := testAppConfig(nil, bp, nil)
 	config.TestProductVariables.MissingUsesLibraries = []string{"baz"}
 
-	ctx := testAppContext(config, bp, nil)
+	ctx := testContext()
 
 	run(t, ctx, config)
 
@@ -1495,6 +1826,7 @@
 				android_app {
 					name: "foo",
 					srcs: ["a.java"],
+					sdk_version: "current",
 				}
 			`,
 			noCode: false,
@@ -1504,6 +1836,7 @@
 			bp: `
 				android_app {
 					name: "foo",
+					sdk_version: "current",
 				}
 			`,
 			noCode: true,
@@ -1514,11 +1847,13 @@
 				android_app {
 					name: "foo",
 					static_libs: ["lib"],
+					sdk_version: "current",
 				}
 
 				java_library {
 					name: "lib",
 					srcs: ["a.java"],
+					sdk_version: "current",
 				}
 			`,
 			noCode: false,
@@ -1529,10 +1864,12 @@
 				android_app {
 					name: "foo",
 					static_libs: ["lib"],
+					sdk_version: "current",
 				}
 
 				java_library {
 					name: "lib",
+					sdk_version: "current",
 				}
 			`,
 			// TODO(jungjw): this should probably be true
@@ -1552,3 +1889,206 @@
 		})
 	}
 }
+
+func TestEmbedNotice(t *testing.T) {
+	ctx, _ := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
+		android_app {
+			name: "foo",
+			srcs: ["a.java"],
+			static_libs: ["javalib"],
+			jni_libs: ["libjni"],
+			notice: "APP_NOTICE",
+			embed_notices: true,
+			sdk_version: "current",
+		}
+
+		// No embed_notice flag
+		android_app {
+			name: "bar",
+			srcs: ["a.java"],
+			jni_libs: ["libjni"],
+			notice: "APP_NOTICE",
+			sdk_version: "current",
+		}
+
+		// No NOTICE files
+		android_app {
+			name: "baz",
+			srcs: ["a.java"],
+			embed_notices: true,
+			sdk_version: "current",
+		}
+
+		cc_library {
+			name: "libjni",
+			system_shared_libs: [],
+			stl: "none",
+			notice: "LIB_NOTICE",
+		}
+
+		java_library {
+			name: "javalib",
+			srcs: [
+				":gen",
+			],
+			sdk_version: "current",
+		}
+
+		genrule {
+			name: "gen",
+			tools: ["gentool"],
+			out: ["gen.java"],
+			notice: "GENRULE_NOTICE",
+		}
+
+		java_binary_host {
+			name: "gentool",
+			srcs: ["b.java"],
+			notice: "TOOL_NOTICE",
+		}
+	`)
+
+	// foo has NOTICE files to process, and embed_notices is true.
+	foo := ctx.ModuleForTests("foo", "android_common")
+	// verify merge notices rule.
+	mergeNotices := foo.Rule("mergeNoticesRule")
+	noticeInputs := mergeNotices.Inputs.Strings()
+	// TOOL_NOTICE should be excluded as it's a host module.
+	if len(mergeNotices.Inputs) != 3 {
+		t.Errorf("number of input notice files: expected = 3, actual = %q", noticeInputs)
+	}
+	if !inList("APP_NOTICE", noticeInputs) {
+		t.Errorf("APP_NOTICE is missing from notice files, %q", noticeInputs)
+	}
+	if !inList("LIB_NOTICE", noticeInputs) {
+		t.Errorf("LIB_NOTICE is missing from notice files, %q", noticeInputs)
+	}
+	if !inList("GENRULE_NOTICE", noticeInputs) {
+		t.Errorf("GENRULE_NOTICE is missing from notice files, %q", noticeInputs)
+	}
+	// aapt2 flags should include -A <NOTICE dir> so that its contents are put in the APK's /assets.
+	res := foo.Output("package-res.apk")
+	aapt2Flags := res.Args["flags"]
+	e := "-A " + buildDir + "/.intermediates/foo/android_common/NOTICE"
+	if !strings.Contains(aapt2Flags, e) {
+		t.Errorf("asset dir flag for NOTICE, %q is missing in aapt2 link flags, %q", e, aapt2Flags)
+	}
+
+	// bar has NOTICE files to process, but embed_notices is not set.
+	bar := ctx.ModuleForTests("bar", "android_common")
+	res = bar.Output("package-res.apk")
+	aapt2Flags = res.Args["flags"]
+	e = "-A " + buildDir + "/.intermediates/bar/android_common/NOTICE"
+	if strings.Contains(aapt2Flags, e) {
+		t.Errorf("bar shouldn't have the asset dir flag for NOTICE: %q", e)
+	}
+
+	// baz's embed_notice is true, but it doesn't have any NOTICE files.
+	baz := ctx.ModuleForTests("baz", "android_common")
+	res = baz.Output("package-res.apk")
+	aapt2Flags = res.Args["flags"]
+	e = "-A " + buildDir + "/.intermediates/baz/android_common/NOTICE"
+	if strings.Contains(aapt2Flags, e) {
+		t.Errorf("baz shouldn't have the asset dir flag for NOTICE: %q", e)
+	}
+}
+
+func TestUncompressDex(t *testing.T) {
+	testCases := []struct {
+		name string
+		bp   string
+
+		uncompressedPlatform  bool
+		uncompressedUnbundled bool
+	}{
+		{
+			name: "normal",
+			bp: `
+				android_app {
+					name: "foo",
+					srcs: ["a.java"],
+					sdk_version: "current",
+				}
+			`,
+			uncompressedPlatform:  true,
+			uncompressedUnbundled: false,
+		},
+		{
+			name: "use_embedded_dex",
+			bp: `
+				android_app {
+					name: "foo",
+					use_embedded_dex: true,
+					srcs: ["a.java"],
+					sdk_version: "current",
+				}
+			`,
+			uncompressedPlatform:  true,
+			uncompressedUnbundled: true,
+		},
+		{
+			name: "privileged",
+			bp: `
+				android_app {
+					name: "foo",
+					privileged: true,
+					srcs: ["a.java"],
+					sdk_version: "current",
+				}
+			`,
+			uncompressedPlatform:  true,
+			uncompressedUnbundled: true,
+		},
+	}
+
+	test := func(t *testing.T, bp string, want bool, unbundled bool) {
+		t.Helper()
+
+		config := testAppConfig(nil, bp, nil)
+		if unbundled {
+			config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true)
+		}
+
+		ctx := testContext()
+
+		run(t, ctx, config)
+
+		foo := ctx.ModuleForTests("foo", "android_common")
+		dex := foo.Rule("r8")
+		uncompressedInDexJar := strings.Contains(dex.Args["zipFlags"], "-L 0")
+		aligned := foo.MaybeRule("zipalign").Rule != nil
+
+		if uncompressedInDexJar != want {
+			t.Errorf("want uncompressed in dex %v, got %v", want, uncompressedInDexJar)
+		}
+
+		if aligned != want {
+			t.Errorf("want aligned %v, got %v", want, aligned)
+		}
+	}
+
+	for _, tt := range testCases {
+		t.Run(tt.name, func(t *testing.T) {
+			t.Run("platform", func(t *testing.T) {
+				test(t, tt.bp, tt.uncompressedPlatform, false)
+			})
+			t.Run("unbundled", func(t *testing.T) {
+				test(t, tt.bp, tt.uncompressedUnbundled, true)
+			})
+		})
+	}
+}
+
+func checkAapt2LinkFlag(t *testing.T, aapt2Flags, flagName, expectedValue string) {
+	if expectedValue != "" {
+		expectedFlag := "--" + flagName + " " + expectedValue
+		if !strings.Contains(aapt2Flags, expectedFlag) {
+			t.Errorf("%q is missing in aapt2 link flags, %q", expectedFlag, aapt2Flags)
+		}
+	} else {
+		unexpectedFlag := "--" + flagName
+		if strings.Contains(aapt2Flags, unexpectedFlag) {
+			t.Errorf("unexpected flag, %q is found in aapt2 link flags, %q", unexpectedFlag, aapt2Flags)
+		}
+	}
+}
diff --git a/java/builder.go b/java/builder.go
index a48e8b1..417a7fa 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -38,7 +38,7 @@
 	// this, all java rules write into separate directories and then are combined into a .jar file
 	// (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.
-	javac = pctx.AndroidGomaStaticRule("javac",
+	javac = pctx.AndroidRemoteStaticRule("javac", android.SUPPORTS_GOMA,
 		blueprint.RuleParams{
 			Command: `rm -rf "$outDir" "$annoDir" "$srcJarDir" && mkdir -p "$outDir" "$annoDir" "$srcJarDir" && ` +
 				`${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
@@ -62,6 +62,40 @@
 		"javacFlags", "bootClasspath", "classpath", "processorpath", "processor", "srcJars", "srcJarDir",
 		"outDir", "annoDir", "javaVersion")
 
+	_ = pctx.VariableFunc("kytheCorpus",
+		func(ctx android.PackageVarContext) string { return ctx.Config().XrefCorpusName() })
+	_ = pctx.SourcePathVariable("kytheVnames", "build/soong/vnames.json")
+	// Run it with -add-opens=java.base/java.nio=ALL-UNNAMED to avoid JDK9's warning about
+	// "Illegal reflective access by com.google.protobuf.Utf8$UnsafeProcessor ...
+	// to field java.nio.Buffer.address"
+	kytheExtract = pctx.AndroidStaticRule("kythe",
+		blueprint.RuleParams{
+			Command: `${config.ZipSyncCmd} -d $srcJarDir ` +
+				`-l $srcJarDir/list -f "*.java" $srcJars && ` +
+				`( [ ! -s $srcJarDir/list -a ! -s $out.rsp ] || ` +
+				`KYTHE_ROOT_DIRECTORY=. KYTHE_OUTPUT_FILE=$out ` +
+				`KYTHE_CORPUS=${kytheCorpus} ` +
+				`KYTHE_VNAMES=${kytheVnames} ` +
+				`${config.SoongJavacWrapper} ${config.JavaCmd} ` +
+				`--add-opens=java.base/java.nio=ALL-UNNAMED ` +
+				`-jar ${config.JavaKytheExtractorJar} ` +
+				`${config.JavacHeapFlags} ${config.CommonJdkFlags} ` +
+				`$processorpath $processor $javacFlags $bootClasspath $classpath ` +
+				`-source $javaVersion -target $javaVersion ` +
+				`-d $outDir -s $annoDir @$out.rsp @$srcJarDir/list)`,
+			CommandDeps: []string{
+				"${config.JavaCmd}",
+				"${config.JavaKytheExtractorJar}",
+				"${kytheVnames}",
+				"${config.ZipSyncCmd}",
+			},
+			CommandOrderOnly: []string{"${config.SoongJavacWrapper}"},
+			Rspfile:          "$out.rsp",
+			RspfileContent:   "$in",
+		},
+		"javacFlags", "bootClasspath", "classpath", "processorpath", "processor", "srcJars", "srcJarDir",
+		"outDir", "annoDir", "javaVersion")
+
 	turbine = pctx.AndroidStaticRule("turbine",
 		blueprint.RuleParams{
 			Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
@@ -148,16 +182,16 @@
 }
 
 type javaBuilderFlags struct {
-	javacFlags        string
-	bootClasspath     classpath
-	classpath         classpath
-	processorPath     classpath
-	processor         string
-	systemModules     classpath
-	systemModulesDeps android.Paths
-	aidlFlags         string
-	aidlDeps          android.Paths
-	javaVersion       string
+	javacFlags     string
+	bootClasspath  classpath
+	classpath      classpath
+	java9Classpath classpath
+	processorPath  classpath
+	processor      string
+	systemModules  *systemModules
+	aidlFlags      string
+	aidlDeps       android.Paths
+	javaVersion    javaVersion
 
 	errorProneExtraJavacFlags string
 	errorProneProcessorPath   classpath
@@ -197,23 +231,95 @@
 		"errorprone", "errorprone")
 }
 
+// Emits the rule to generate Xref input file (.kzip file) for the given set of source files and source jars
+// to compile with given set of builder flags, etc.
+func emitXrefRule(ctx android.ModuleContext, xrefFile android.WritablePath, idx int,
+	srcFiles, srcJars android.Paths,
+	flags javaBuilderFlags, deps android.Paths) {
+
+	deps = append(deps, srcJars...)
+	classpath := flags.classpath
+
+	var bootClasspath string
+	if flags.javaVersion.usesJavaModules() {
+		var systemModuleDeps android.Paths
+		bootClasspath, systemModuleDeps = flags.systemModules.FormJavaSystemModulesPath(ctx.Device())
+		deps = append(deps, systemModuleDeps...)
+		classpath = append(flags.java9Classpath, classpath...)
+	} else {
+		deps = append(deps, flags.bootClasspath...)
+		if len(flags.bootClasspath) == 0 && ctx.Device() {
+			// explicitly specify -bootclasspath "" if the bootclasspath is empty to
+			// ensure java does not fall back to the default bootclasspath.
+			bootClasspath = `-bootclasspath ""`
+		} else {
+			bootClasspath = flags.bootClasspath.FormJavaClassPath("-bootclasspath")
+		}
+	}
+
+	deps = append(deps, classpath...)
+	deps = append(deps, flags.processorPath...)
+
+	processor := "-proc:none"
+	if flags.processor != "" {
+		processor = "-processor " + flags.processor
+	}
+
+	intermediatesDir := "xref"
+	if idx >= 0 {
+		intermediatesDir += strconv.Itoa(idx)
+	}
+
+	ctx.Build(pctx,
+		android.BuildParams{
+			Rule:        kytheExtract,
+			Description: "Xref Java extractor",
+			Output:      xrefFile,
+			Inputs:      srcFiles,
+			Implicits:   deps,
+			Args: map[string]string{
+				"annoDir":       android.PathForModuleOut(ctx, intermediatesDir, "anno").String(),
+				"bootClasspath": bootClasspath,
+				"classpath":     classpath.FormJavaClassPath("-classpath"),
+				"javacFlags":    flags.javacFlags,
+				"javaVersion":   flags.javaVersion.String(),
+				"outDir":        android.PathForModuleOut(ctx, "javac", "classes.xref").String(),
+				"processorpath": flags.processorPath.FormJavaClassPath("-processorpath"),
+				"processor":     processor,
+				"srcJarDir":     android.PathForModuleOut(ctx, intermediatesDir, "srcjars.xref").String(),
+				"srcJars":       strings.Join(srcJars.Strings(), " "),
+			},
+		})
+}
+
 func TransformJavaToHeaderClasses(ctx android.ModuleContext, outputFile android.WritablePath,
 	srcFiles, srcJars android.Paths, flags javaBuilderFlags) {
 
 	var deps android.Paths
 	deps = append(deps, srcJars...)
-	deps = append(deps, flags.bootClasspath...)
-	deps = append(deps, flags.classpath...)
+
+	classpath := flags.classpath
 
 	var bootClasspath string
-	if len(flags.bootClasspath) == 0 && ctx.Device() {
-		// explicitly specify -bootclasspath "" if the bootclasspath is empty to
-		// ensure java does not fall back to the default bootclasspath.
-		bootClasspath = `--bootclasspath ""`
+	if flags.javaVersion.usesJavaModules() {
+		var systemModuleDeps android.Paths
+		bootClasspath, systemModuleDeps = flags.systemModules.FormTurbineSystemModulesPath(ctx.Device())
+		deps = append(deps, systemModuleDeps...)
+		classpath = append(flags.java9Classpath, classpath...)
 	} else {
-		bootClasspath = strings.Join(flags.bootClasspath.FormTurbineClasspath("--bootclasspath "), " ")
+		deps = append(deps, flags.bootClasspath...)
+		if len(flags.bootClasspath) == 0 && ctx.Device() {
+			// explicitly specify -bootclasspath "" if the bootclasspath is empty to
+			// ensure turbine does not fall back to the default bootclasspath.
+			bootClasspath = `--bootclasspath ""`
+		} else {
+			bootClasspath = flags.bootClasspath.FormTurbineClassPath("--bootclasspath ")
+		}
 	}
 
+	deps = append(deps, classpath...)
+	deps = append(deps, flags.processorPath...)
+
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        turbine,
 		Description: "turbine",
@@ -224,9 +330,9 @@
 			"javacFlags":    flags.javacFlags,
 			"bootClasspath": bootClasspath,
 			"srcJars":       strings.Join(srcJars.Strings(), " "),
-			"classpath":     strings.Join(flags.classpath.FormTurbineClasspath("--classpath "), " "),
+			"classpath":     classpath.FormTurbineClassPath("--classpath "),
 			"outDir":        android.PathForModuleOut(ctx, "turbine", "classes").String(),
-			"javaVersion":   flags.javaVersion,
+			"javaVersion":   flags.javaVersion.String(),
 		},
 	})
 }
@@ -247,10 +353,14 @@
 
 	deps = append(deps, srcJars...)
 
+	classpath := flags.classpath
+
 	var bootClasspath string
-	if flags.javaVersion == "1.9" {
-		deps = append(deps, flags.systemModulesDeps...)
-		bootClasspath = flags.systemModules.FormJavaSystemModulesPath("--system=", ctx.Device())
+	if flags.javaVersion.usesJavaModules() {
+		var systemModuleDeps android.Paths
+		bootClasspath, systemModuleDeps = flags.systemModules.FormJavaSystemModulesPath(ctx.Device())
+		deps = append(deps, systemModuleDeps...)
+		classpath = append(flags.java9Classpath, classpath...)
 	} else {
 		deps = append(deps, flags.bootClasspath...)
 		if len(flags.bootClasspath) == 0 && ctx.Device() {
@@ -262,7 +372,7 @@
 		}
 	}
 
-	deps = append(deps, flags.classpath...)
+	deps = append(deps, classpath...)
 	deps = append(deps, flags.processorPath...)
 
 	processor := "-proc:none"
@@ -288,14 +398,14 @@
 		Args: map[string]string{
 			"javacFlags":    flags.javacFlags,
 			"bootClasspath": bootClasspath,
-			"classpath":     flags.classpath.FormJavaClassPath("-classpath"),
+			"classpath":     classpath.FormJavaClassPath("-classpath"),
 			"processorpath": flags.processorPath.FormJavaClassPath("-processorpath"),
 			"processor":     processor,
 			"srcJars":       strings.Join(srcJars.Strings(), " "),
 			"srcJarDir":     android.PathForModuleOut(ctx, intermediatesDir, srcJarDir).String(),
 			"outDir":        android.PathForModuleOut(ctx, intermediatesDir, outDir).String(),
 			"annoDir":       android.PathForModuleOut(ctx, intermediatesDir, annoDir).String(),
-			"javaVersion":   flags.javaVersion,
+			"javaVersion":   flags.javaVersion.String(),
 		},
 	})
 }
@@ -411,35 +521,28 @@
 	})
 }
 
-type classpath []android.Path
+type classpath android.Paths
 
-func (x *classpath) FormJavaClassPath(optName string) string {
+func (x *classpath) formJoinedClassPath(optName string, sep string) string {
 	if optName != "" && !strings.HasSuffix(optName, "=") && !strings.HasSuffix(optName, " ") {
 		optName += " "
 	}
 	if len(*x) > 0 {
-		return optName + strings.Join(x.Strings(), ":")
+		return optName + strings.Join(x.Strings(), sep)
 	} else {
 		return ""
 	}
 }
-
-// Returns a --system argument in the form javac expects with -source 1.9.  If forceEmpty is true,
-// returns --system=none if the list is empty to ensure javac does not fall back to the default
-// system modules.
-func (x *classpath) FormJavaSystemModulesPath(optName string, forceEmpty bool) string {
-	if len(*x) > 1 {
-		panic("more than one system module")
-	} else if len(*x) == 1 {
-		return optName + (*x)[0].String()
-	} else if forceEmpty {
-		return optName + "none"
-	} else {
-		return ""
-	}
+func (x *classpath) FormJavaClassPath(optName string) string {
+	return x.formJoinedClassPath(optName, ":")
 }
 
-func (x *classpath) FormTurbineClasspath(optName string) []string {
+func (x *classpath) FormTurbineClassPath(optName string) string {
+	return x.formJoinedClassPath(optName, " ")
+}
+
+// FormRepeatedClassPath returns a list of arguments with the given optName prefixed to each element of the classpath.
+func (x *classpath) FormRepeatedClassPath(optName string) []string {
 	if x == nil || *x == nil {
 		return nil
 	}
@@ -466,3 +569,34 @@
 	}
 	return ret
 }
+
+type systemModules struct {
+	dir  android.Path
+	deps android.Paths
+}
+
+// Returns a --system argument in the form javac expects with -source 1.9 and the list of files to
+// depend on.  If forceEmpty is true, returns --system=none if the list is empty to ensure javac
+// does not fall back to the default system modules.
+func (x *systemModules) FormJavaSystemModulesPath(forceEmpty bool) (string, android.Paths) {
+	if x != nil {
+		return "--system=" + x.dir.String(), x.deps
+	} else if forceEmpty {
+		return "--system=none", nil
+	} else {
+		return "", nil
+	}
+}
+
+// Returns a --system argument in the form turbine expects with -source 1.9 and the list of files to
+// depend on.  If forceEmpty is true, returns --bootclasspath "" if the list is empty to ensure turbine
+// does not fall back to the default bootclasspath.
+func (x *systemModules) FormTurbineSystemModulesPath(forceEmpty bool) (string, android.Paths) {
+	if x != nil {
+		return "--system " + x.dir.String(), x.deps
+	} else if forceEmpty {
+		return `--bootclasspath ""`, nil
+	} else {
+		return "", nil
+	}
+}
diff --git a/java/config/config.go b/java/config/config.go
index 6ade649..9738454 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -42,17 +42,25 @@
 		"android.car",
 		"android.car7",
 		"conscrypt",
+		"core-icu4j",
 		"core-oj",
 		"core-libart",
+		"updatable-media",
+		"ike",
 	}
 )
 
+const (
+	JavaVmFlags  = `-XX:OnError="cat hs_err_pid%p.log" -XX:CICompilerCount=6 -XX:+UseDynamicNumberOfGCThreads`
+	JavacVmFlags = `-J-XX:OnError="cat hs_err_pid%p.log" -J-XX:CICompilerCount=6 -J-XX:+UseDynamicNumberOfGCThreads`
+)
+
 func init() {
 	pctx.Import("github.com/google/blueprint/bootstrap")
 
 	pctx.StaticVariable("JavacHeapSize", "2048M")
 	pctx.StaticVariable("JavacHeapFlags", "-J-Xmx${JavacHeapSize}")
-	pctx.StaticVariable("DexFlags", "-JXX:+TieredCompilation -JXX:TieredStopAtLevel=1")
+	pctx.StaticVariable("DexFlags", "-JXX:OnError='cat hs_err_pid%p.log' -JXX:CICompilerCount=6 -JXX:+UseDynamicNumberOfGCThreads")
 
 	pctx.StaticVariable("CommonJdkFlags", strings.Join([]string{
 		`-Xmaxerrs 9999999`,
@@ -69,8 +77,9 @@
 		// b/65004097: prevent using java.lang.invoke.StringConcatFactory when using -target 1.9
 		`-XDstringConcat=inline`,
 	}, " "))
-	pctx.StaticVariable("JavaVmFlags", "-XX:OnError=\"cat hs_err_pid%p.log\" -XX:CICompilerCount=6 -XX:+UseDynamicNumberOfGCThreads")
-	pctx.StaticVariable("JavacVmFlags", "-J-XX:OnError=\"cat hs_err_pid%p.log\" -J-XX:CICompilerCount=6 -J-XX:+UseDynamicNumberOfGCThreads")
+
+	pctx.StaticVariable("JavaVmFlags", JavaVmFlags)
+	pctx.StaticVariable("JavacVmFlags", JavacVmFlags)
 
 	pctx.VariableConfigMethod("hostPrebuiltTag", android.Config.PrebuiltOS)
 
@@ -78,6 +87,12 @@
 		// This is set up and guaranteed by soong_ui
 		return ctx.Config().Getenv("ANDROID_JAVA_HOME")
 	})
+	pctx.VariableFunc("JlinkVersion", func(ctx android.PackageVarContext) string {
+		if override := ctx.Config().Getenv("OVERRIDE_JLINK_VERSION_NUMBER"); override != "" {
+			return override
+		}
+		return "11"
+	})
 
 	pctx.SourcePathVariable("JavaToolchain", "${JavaHome}/bin")
 	pctx.SourcePathVariableWithEnvOverride("JavacCmd",
@@ -88,6 +103,7 @@
 	pctx.SourcePathVariable("JlinkCmd", "${JavaToolchain}/jlink")
 	pctx.SourcePathVariable("JmodCmd", "${JavaToolchain}/jmod")
 	pctx.SourcePathVariable("JrtFsJar", "${JavaHome}/lib/jrt-fs.jar")
+	pctx.SourcePathVariable("JavaKytheExtractorJar", "prebuilts/build-tools/common/framework/javac_extractor.jar")
 	pctx.SourcePathVariable("Ziptime", "prebuilts/build-tools/${hostPrebuiltTag}/bin/ziptime")
 
 	pctx.SourcePathVariable("GenKotlinBuildFileCmd", "build/soong/scripts/gen-kotlin-build-file.sh")
@@ -109,7 +125,7 @@
 		if ctx.Config().UnbundledBuild() {
 			return "prebuilts/build-tools/common/framework/" + turbine
 		} else {
-			return pctx.HostJavaToolPath(ctx, turbine).String()
+			return ctx.Config().HostJavaToolPath(ctx, turbine).String()
 		}
 	})
 
@@ -132,25 +148,102 @@
 
 	pctx.HostJavaToolVariable("JacocoCLIJar", "jacoco-cli.jar")
 
-	hostBinToolVariableWithPrebuilt := func(name, prebuiltDir, tool string) {
-		pctx.VariableFunc(name, func(ctx android.PackageVarContext) string {
-			if ctx.Config().UnbundledBuild() || ctx.Config().IsPdkBuild() {
-				return filepath.Join(prebuiltDir, runtime.GOOS, "bin", tool)
-			} else {
-				return pctx.HostBinToolPath(ctx, tool).String()
-			}
-		})
-	}
-
-	hostBinToolVariableWithPrebuilt("Aapt2Cmd", "prebuilts/sdk/tools", "aapt2")
-
 	pctx.HostBinToolVariable("ManifestCheckCmd", "manifest_check")
 	pctx.HostBinToolVariable("ManifestFixerCmd", "manifest_fixer")
 
 	pctx.HostBinToolVariable("ManifestMergerCmd", "manifest-merger")
 
-	pctx.HostBinToolVariable("ZipAlign", "zipalign")
-
 	pctx.HostBinToolVariable("Class2Greylist", "class2greylist")
 	pctx.HostBinToolVariable("HiddenAPI", "hiddenapi")
+
+	hostBinToolVariableWithSdkToolsPrebuilt("Aapt2Cmd", "aapt2")
+	hostBinToolVariableWithBuildToolsPrebuilt("AidlCmd", "aidl")
+	hostBinToolVariableWithBuildToolsPrebuilt("ZipAlign", "zipalign")
+
+	hostJavaToolVariableWithSdkToolsPrebuilt("SignapkCmd", "signapk")
+	// TODO(ccross): this should come from the signapk dependencies, but we don't have any way
+	// to express host JNI dependencies yet.
+	hostJNIToolVariableWithSdkToolsPrebuilt("SignapkJniLibrary", "libconscrypt_openjdk_jni")
+}
+
+func hostBinToolVariableWithSdkToolsPrebuilt(name, tool string) {
+	pctx.VariableFunc(name, func(ctx android.PackageVarContext) string {
+		if ctx.Config().UnbundledBuild() || ctx.Config().IsPdkBuild() {
+			return filepath.Join("prebuilts/sdk/tools", runtime.GOOS, "bin", tool)
+		} else {
+			return ctx.Config().HostToolPath(ctx, tool).String()
+		}
+	})
+}
+
+func hostJavaToolVariableWithSdkToolsPrebuilt(name, tool string) {
+	pctx.VariableFunc(name, func(ctx android.PackageVarContext) string {
+		if ctx.Config().UnbundledBuild() || ctx.Config().IsPdkBuild() {
+			return filepath.Join("prebuilts/sdk/tools/lib", tool+".jar")
+		} else {
+			return ctx.Config().HostJavaToolPath(ctx, tool+".jar").String()
+		}
+	})
+}
+
+func hostJNIToolVariableWithSdkToolsPrebuilt(name, tool string) {
+	pctx.VariableFunc(name, func(ctx android.PackageVarContext) string {
+		if ctx.Config().UnbundledBuild() || ctx.Config().IsPdkBuild() {
+			ext := ".so"
+			if runtime.GOOS == "darwin" {
+				ext = ".dylib"
+			}
+			return filepath.Join("prebuilts/sdk/tools", runtime.GOOS, "lib64", tool+ext)
+		} else {
+			return ctx.Config().HostJNIToolPath(ctx, tool).String()
+		}
+	})
+}
+
+func hostBinToolVariableWithBuildToolsPrebuilt(name, tool string) {
+	pctx.VariableFunc(name, func(ctx android.PackageVarContext) string {
+		if ctx.Config().UnbundledBuild() || ctx.Config().IsPdkBuild() {
+			return filepath.Join("prebuilts/build-tools", ctx.Config().PrebuiltOS(), "bin", tool)
+		} else {
+			return ctx.Config().HostToolPath(ctx, tool).String()
+		}
+	})
+}
+
+// JavaCmd returns a SourcePath object with the path to the java command.
+func JavaCmd(ctx android.PathContext) android.SourcePath {
+	return javaTool(ctx, "java")
+}
+
+// JavadocCmd returns a SourcePath object with the path to the java command.
+func JavadocCmd(ctx android.PathContext) android.SourcePath {
+	return javaTool(ctx, "javadoc")
+}
+
+func javaTool(ctx android.PathContext, tool string) android.SourcePath {
+	type javaToolKey string
+
+	key := android.NewCustomOnceKey(javaToolKey(tool))
+
+	return ctx.Config().OnceSourcePath(key, func() android.SourcePath {
+		return javaToolchain(ctx).Join(ctx, tool)
+	})
+
+}
+
+var javaToolchainKey = android.NewOnceKey("javaToolchain")
+
+func javaToolchain(ctx android.PathContext) android.SourcePath {
+	return ctx.Config().OnceSourcePath(javaToolchainKey, func() android.SourcePath {
+		return javaHome(ctx).Join(ctx, "bin")
+	})
+}
+
+var javaHomeKey = android.NewOnceKey("javaHome")
+
+func javaHome(ctx android.PathContext) android.SourcePath {
+	return ctx.Config().OnceSourcePath(javaHomeKey, func() android.SourcePath {
+		// This is set up and guaranteed by soong_ui
+		return android.PathForSource(ctx, ctx.Config().Getenv("ANDROID_JAVA_HOME"))
+	})
 }
diff --git a/java/config/makevars.go b/java/config/makevars.go
index ead298a..981a736 100644
--- a/java/config/makevars.go
+++ b/java/config/makevars.go
@@ -29,15 +29,10 @@
 	ctx.Strict("TARGET_DEFAULT_BOOTCLASSPATH_LIBRARIES", strings.Join(DefaultBootclasspathLibraries, " "))
 	ctx.Strict("DEFAULT_SYSTEM_MODULES", DefaultSystemModules)
 
-	if ctx.Config().TargetOpenJDK9() {
-		ctx.Strict("DEFAULT_JAVA_LANGUAGE_VERSION", "1.9")
-	} else {
-		ctx.Strict("DEFAULT_JAVA_LANGUAGE_VERSION", "1.8")
-	}
-
 	ctx.Strict("ANDROID_JAVA_HOME", "${JavaHome}")
 	ctx.Strict("ANDROID_JAVA8_HOME", "prebuilts/jdk/jdk8/${hostPrebuiltTag}")
 	ctx.Strict("ANDROID_JAVA9_HOME", "prebuilts/jdk/jdk9/${hostPrebuiltTag}")
+	ctx.Strict("ANDROID_JAVA11_HOME", "prebuilts/jdk/jdk11/${hostPrebuiltTag}")
 	ctx.Strict("ANDROID_JAVA_TOOLCHAIN", "${JavaToolchain}")
 	ctx.Strict("JAVA", "${JavaCmd} ${JavaVmFlags}")
 	ctx.Strict("JAVAC", "${JavacCmd} ${JavacVmFlags}")
@@ -82,4 +77,17 @@
 	ctx.Strict("HIDDENAPI", "${HiddenAPI}")
 
 	ctx.Strict("DEX_FLAGS", "${DexFlags}")
+
+	ctx.Strict("AIDL", "${AidlCmd}")
+	ctx.Strict("AAPT2", "${Aapt2Cmd}")
+	ctx.Strict("ZIPALIGN", "${ZipAlign}")
+	ctx.Strict("SIGNAPK_JAR", "${SignapkCmd}")
+	ctx.Strict("SIGNAPK_JNI_LIBRARY_PATH", "${SignapkJniLibrary}")
+
+	ctx.Strict("SOONG_ZIP", "${SoongZipCmd}")
+	ctx.Strict("MERGE_ZIPS", "${MergeZipsCmd}")
+	ctx.Strict("ZIP2ZIP", "${Zip2ZipCmd}")
+
+	ctx.Strict("ZIPTIME", "${Ziptime}")
+
 }
diff --git a/java/device_host_converter.go b/java/device_host_converter.go
index 030b010..b40ab93 100644
--- a/java/device_host_converter.go
+++ b/java/device_host_converter.go
@@ -18,8 +18,6 @@
 	"fmt"
 	"io"
 
-	"github.com/google/blueprint"
-
 	"android/soong/android"
 )
 
@@ -83,13 +81,13 @@
 var deviceHostConverterDepTag = dependencyTag{name: "device_host_converter"}
 
 func (d *DeviceForHost) DepsMutator(ctx android.BottomUpMutatorContext) {
-	variation := []blueprint.Variation{{Mutator: "arch", Variation: "android_common"}}
-	ctx.AddFarVariationDependencies(variation, deviceHostConverterDepTag, d.properties.Libs...)
+	ctx.AddFarVariationDependencies(ctx.Config().AndroidCommonTarget.Variations(),
+		deviceHostConverterDepTag, d.properties.Libs...)
 }
 
 func (d *HostForDevice) DepsMutator(ctx android.BottomUpMutatorContext) {
-	variation := []blueprint.Variation{{Mutator: "arch", Variation: ctx.Config().BuildOsCommonVariant}}
-	ctx.AddFarVariationDependencies(variation, deviceHostConverterDepTag, d.properties.Libs...)
+	ctx.AddFarVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(),
+		deviceHostConverterDepTag, d.properties.Libs...)
 }
 
 func (d *DeviceHostConverter) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -164,10 +162,18 @@
 	return nil
 }
 
+func (d *DeviceHostConverter) ExportedPlugins() (android.Paths, []string) {
+	return nil, nil
+}
+
 func (d *DeviceHostConverter) SrcJarArgs() ([]string, android.Paths) {
 	return d.srcJarArgs, d.srcJarDeps
 }
 
+func (d *DeviceHostConverter) JacocoReportClassesFile() android.Path {
+	return nil
+}
+
 func (d *DeviceHostConverter) AndroidMk() android.AndroidMkData {
 	return android.AndroidMkData{
 		Class:      "JAVA_LIBRARIES",
diff --git a/java/device_host_converter_test.go b/java/device_host_converter_test.go
index 146bf6f..3c9a0f3 100644
--- a/java/device_host_converter_test.go
+++ b/java/device_host_converter_test.go
@@ -50,9 +50,7 @@
 		}
 	`
 
-	config := testConfig(nil)
-	ctx := testContext(config, bp, nil)
-	run(t, ctx, config)
+	ctx, config := testJava(t, bp)
 
 	deviceModule := ctx.ModuleForTests("device_module", "android_common")
 	deviceTurbineCombined := deviceModule.Output("turbine-combined/device_module.jar")
@@ -62,7 +60,7 @@
 	deviceImportModule := ctx.ModuleForTests("device_import_module", "android_common")
 	deviceImportCombined := deviceImportModule.Output("combined/device_import_module.jar")
 
-	hostModule := ctx.ModuleForTests("host_module", config.BuildOsCommonVariant)
+	hostModule := ctx.ModuleForTests("host_module", config.BuildOSCommonTarget.String())
 	hostJavac := hostModule.Output("javac/host_module.jar")
 	hostRes := hostModule.Output("res/host_module.jar")
 	combined := hostModule.Output("combined/host_module.jar")
@@ -126,22 +124,20 @@
 
 		java_library {
 			name: "device_module",
-			no_framework_libs: true,
+			sdk_version: "core_platform",
 			srcs: ["b.java"],
 			java_resources: ["java-res/b/b"],
 			static_libs: ["host_for_device_module"],
 		}
 	`
 
-	config := testConfig(nil)
-	ctx := testContext(config, bp, nil)
-	run(t, ctx, config)
+	ctx, config := testJava(t, bp)
 
-	hostModule := ctx.ModuleForTests("host_module", config.BuildOsCommonVariant)
+	hostModule := ctx.ModuleForTests("host_module", config.BuildOSCommonTarget.String())
 	hostJavac := hostModule.Output("javac/host_module.jar")
 	hostRes := hostModule.Output("res/host_module.jar")
 
-	hostImportModule := ctx.ModuleForTests("host_import_module", config.BuildOsCommonVariant)
+	hostImportModule := ctx.ModuleForTests("host_import_module", config.BuildOSCommonTarget.String())
 	hostImportCombined := hostImportModule.Output("combined/host_import_module.jar")
 
 	deviceModule := ctx.ModuleForTests("device_module", "android_common")
diff --git a/java/dex.go b/java/dex.go
index c8a4fa8..cd6d90d 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -85,8 +85,8 @@
 func (j *Module) d8Flags(ctx android.ModuleContext, flags javaBuilderFlags) ([]string, android.Paths) {
 	d8Flags := j.dexCommonFlags(ctx)
 
-	d8Flags = append(d8Flags, flags.bootClasspath.FormTurbineClasspath("--lib ")...)
-	d8Flags = append(d8Flags, flags.classpath.FormTurbineClasspath("--lib ")...)
+	d8Flags = append(d8Flags, flags.bootClasspath.FormRepeatedClassPath("--lib ")...)
+	d8Flags = append(d8Flags, flags.classpath.FormRepeatedClassPath("--lib ")...)
 
 	var d8Deps android.Paths
 	d8Deps = append(d8Deps, flags.bootClasspath...)
@@ -115,7 +115,6 @@
 	r8Flags = append(r8Flags, proguardRaiseDeps.FormJavaClassPath("-libraryjars"))
 	r8Flags = append(r8Flags, flags.bootClasspath.FormJavaClassPath("-libraryjars"))
 	r8Flags = append(r8Flags, flags.classpath.FormJavaClassPath("-libraryjars"))
-	r8Flags = append(r8Flags, "-forceprocessing")
 
 	r8Deps = append(r8Deps, proguardRaiseDeps...)
 	r8Deps = append(r8Deps, flags.bootClasspath...)
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index ed12fe6..da68660 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -22,7 +22,7 @@
 type dexpreopter struct {
 	dexpreoptProperties DexpreoptProperties
 
-	installPath         android.OutputPath
+	installPath         android.InstallPath
 	uncompressedDex     bool
 	isSDKLibrary        bool
 	isTest              bool
@@ -40,13 +40,9 @@
 
 type DexpreoptProperties struct {
 	Dex_preopt struct {
-		// If false, prevent dexpreopting and stripping the dex file from the final jar.  Defaults to
-		// true.
+		// If false, prevent dexpreopting.  Defaults to true.
 		Enabled *bool
 
-		// If true, never strip the dex files from the final jar when dexpreopting.  Defaults to false.
-		No_stripping *bool
-
 		// If true, generate an app image (.art file) for this module.
 		App_image *bool
 
@@ -89,12 +85,17 @@
 		return true
 	}
 
+	// Don't preopt APEX variant module
+	if am, ok := ctx.Module().(android.ApexModule); ok && !am.IsForPlatform() {
+		return true
+	}
+
 	// TODO: contains no java code
 
 	return false
 }
 
-func odexOnSystemOther(ctx android.ModuleContext, installPath android.OutputPath) bool {
+func odexOnSystemOther(ctx android.ModuleContext, installPath android.InstallPath) bool {
 	return dexpreopt.OdexOnSystemOtherByName(ctx.ModuleName(), android.InstallPathToOnDevicePath(ctx, installPath), dexpreoptGlobalConfig(ctx))
 }
 
@@ -105,9 +106,8 @@
 
 	global := dexpreoptGlobalConfig(ctx)
 	bootImage := defaultBootImageConfig(ctx)
-	defaultBootImage := bootImage
 	if global.UseApexImage {
-		bootImage = apexBootImageConfig(ctx)
+		bootImage = frameworkJZBootImageConfig(ctx)
 	}
 
 	var archs []android.ArchType
@@ -126,13 +126,9 @@
 			archs = archs[:1]
 		}
 	}
-	if ctx.Config().SecondArchIsTranslated() {
-		// Only preopt primary arch for translated arch since there is only an image there.
-		archs = archs[:1]
-	}
 
 	var images android.Paths
-	var imagesDeps []android.Paths
+	var imagesDeps []android.OutputPaths
 	for _, arch := range archs {
 		images = append(images, bootImage.images[arch])
 		imagesDeps = append(imagesDeps, bootImage.imagesDeps[arch])
@@ -140,9 +136,8 @@
 
 	dexLocation := android.InstallPathToOnDevicePath(ctx, d.installPath)
 
-	strippedDexJarFile := android.PathForModuleOut(ctx, "dexpreopt", dexJarFile.Base())
-
 	var profileClassListing android.OptionalPath
+	var profileBootListing android.OptionalPath
 	profileIsTextListing := false
 	if BoolDefault(d.dexpreoptProperties.Dex_preopt.Profile_guided, true) {
 		// If dex_preopt.profile_guided is not set, default it based on the existence of the
@@ -150,6 +145,8 @@
 		if String(d.dexpreoptProperties.Dex_preopt.Profile) != "" {
 			profileClassListing = android.OptionalPathForPath(
 				android.PathForModuleSrc(ctx, String(d.dexpreoptProperties.Dex_preopt.Profile)))
+			profileBootListing = android.ExistentPathForSource(ctx,
+				ctx.ModuleDir(), String(d.dexpreoptProperties.Dex_preopt.Profile)+"-boot")
 			profileIsTextListing = true
 		} else {
 			profileClassListing = android.ExistentPathForSource(ctx,
@@ -169,21 +166,20 @@
 
 		ProfileClassListing:  profileClassListing,
 		ProfileIsTextListing: profileIsTextListing,
+		ProfileBootListing:   profileBootListing,
 
 		EnforceUsesLibraries:         d.enforceUsesLibs,
 		PresentOptionalUsesLibraries: d.optionalUsesLibs,
 		UsesLibraries:                d.usesLibs,
 		LibraryPaths:                 d.libraryPaths,
 
-		Archs:               archs,
-		DexPreoptImages:     images,
-		DexPreoptImagesDeps: imagesDeps,
+		Archs:                   archs,
+		DexPreoptImages:         images,
+		DexPreoptImagesDeps:     imagesDeps,
+		DexPreoptImageLocations: bootImage.imageLocations,
 
-		// We use the dex paths and dex locations of the default boot image, as it
-		// contains the full dexpreopt boot classpath. Other images may just contain a subset of
-		// the dexpreopt boot classpath.
-		PreoptBootClassPathDexFiles:     defaultBootImage.dexPaths.Paths(),
-		PreoptBootClassPathDexLocations: defaultBootImage.dexLocations,
+		PreoptBootClassPathDexFiles:     bootImage.dexPathsDeps.Paths(),
+		PreoptBootClassPathDexLocations: bootImage.dexLocationsDeps,
 
 		PreoptExtractedApk: false,
 
@@ -191,10 +187,6 @@
 		ForceCreateAppImage: BoolDefault(d.dexpreoptProperties.Dex_preopt.App_image, false),
 
 		PresignedPrebuilt: d.isPresignedPrebuilt,
-
-		NoStripping:     Bool(d.dexpreoptProperties.Dex_preopt.No_stripping),
-		StripInputPath:  dexJarFile,
-		StripOutputPath: strippedDexJarFile.OutputPath,
 	}
 
 	dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(ctx, global, dexpreoptConfig)
@@ -207,13 +199,5 @@
 
 	d.builtInstalled = dexpreoptRule.Installs().String()
 
-	stripRule, err := dexpreopt.GenerateStripRule(global, dexpreoptConfig)
-	if err != nil {
-		ctx.ModuleErrorf("error generating dexpreopt strip rule: %s", err.Error())
-		return dexJarFile
-	}
-
-	stripRule.Build(pctx, ctx, "dexpreopt_strip", "dexpreopt strip")
-
-	return strippedDexJarFile
+	return dexJarFile
 }
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index eb735c1..66840b5 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -22,7 +22,6 @@
 	"android/soong/android"
 	"android/soong/dexpreopt"
 
-	"github.com/google/blueprint/pathtools"
 	"github.com/google/blueprint/proptools"
 )
 
@@ -50,37 +49,77 @@
 // will then reconstruct the real path, so the rules must have a dependency on the real path.
 
 type bootImageConfig struct {
-	name         string
-	modules      []string
-	dexLocations []string
-	dexPaths     android.WritablePaths
-	dir          android.OutputPath
-	symbolsDir   android.OutputPath
-	targets      []android.Target
-	images       map[android.ArchType]android.OutputPath
-	imagesDeps   map[android.ArchType]android.Paths
-	zip          android.WritablePath
+	// Whether this image is an extension.
+	extension bool
+
+	// Image name (used in directory names and ninja rule names).
+	name string
+
+	// Basename of the image: the resulting filenames are <stem>[-<jar>].{art,oat,vdex}.
+	stem string
+
+	// Output directory for the image files.
+	dir android.OutputPath
+
+	// Output directory for the image files with debug symbols.
+	symbolsDir android.OutputPath
+
+	// Subdirectory where the image files are installed.
+	installSubdir string
+
+	// Targets for which the image is generated.
+	targets []android.Target
+
+	// The names of jars that constitute this image.
+	modules []string
+
+	// The "locations" of jars.
+	dexLocations     []string // for this image
+	dexLocationsDeps []string // for the dependency images and in this image
+
+	// File paths to jars.
+	dexPaths     android.WritablePaths // for this image
+	dexPathsDeps android.WritablePaths // for the dependency images and in this image
+
+	// The "locations" of the dependency images and in this image.
+	imageLocations []string
+
+	// Paths to image files (grouped by target).
+	images     map[android.ArchType]android.OutputPath  // first image file
+	imagesDeps map[android.ArchType]android.OutputPaths // all files
+
+	// File path to a zip archive with all image files (or nil, if not needed).
+	zip android.WritablePath
 }
 
-func (image bootImageConfig) moduleFiles(ctx android.PathContext, dir android.OutputPath, exts ...string) []android.OutputPath {
-	ret := make([]android.OutputPath, 0, len(image.modules)*len(exts))
-
-	// dex preopt on the bootclasspath produces multiple files.  The first dex file
-	// is converted into to 'name'.art (to match the legacy assumption that 'name'.art
+func (image bootImageConfig) moduleName(idx int) string {
+	// Dexpreopt on the boot class path produces multiple files. The first dex file
+	// is converted into 'name'.art (to match the legacy assumption that 'name'.art
 	// exists), and the rest are converted to 'name'-<jar>.art.
-	// In addition, each .art file has an associated .oat and .vdex file, and an
-	// unstripped .oat file
-	for i, m := range image.modules {
-		name := image.name
-		if i != 0 {
-			name += "-" + m
-		}
+	m := image.modules[idx]
+	name := image.stem
+	if idx != 0 || image.extension {
+		name += "-" + stemOf(m)
+	}
+	return name
+}
 
+func (image bootImageConfig) firstModuleNameOrStem() string {
+	if len(image.modules) > 0 {
+		return image.moduleName(0)
+	} else {
+		return image.stem
+	}
+}
+
+func (image bootImageConfig) moduleFiles(ctx android.PathContext, dir android.OutputPath, exts ...string) android.OutputPaths {
+	ret := make(android.OutputPaths, 0, len(image.modules)*len(exts))
+	for i := range image.modules {
+		name := image.moduleName(i)
 		for _, ext := range exts {
 			ret = append(ret, dir.Join(ctx, name+ext))
 		}
 	}
-
 	return ret
 }
 
@@ -123,6 +162,10 @@
 }
 
 func skipDexpreoptBootJars(ctx android.PathContext) bool {
+	if dexpreoptGlobalConfig(ctx).DisablePreopt {
+		return true
+	}
+
 	if ctx.Config().UnbundledBuild() {
 		return true
 	}
@@ -142,6 +185,14 @@
 	dexpreoptConfigForMake android.WritablePath
 }
 
+// Accessor function for the apex package. Returns nil if dexpreopt is disabled.
+func DexpreoptedArtApexJars(ctx android.BuilderContext) map[android.ArchType]android.OutputPaths {
+	if skipDexpreoptBootJars(ctx) {
+		return nil
+	}
+	return artBootImageConfig(ctx).imagesDeps
+}
+
 // dexpreoptBoot singleton rules
 func (d *dexpreoptBootJars) GenerateBuildActions(ctx android.SingletonContext) {
 	if skipDexpreoptBootJars(ctx) {
@@ -165,8 +216,12 @@
 
 	// Always create the default boot image first, to get a unique profile rule for all images.
 	d.defaultBootImage = buildBootImage(ctx, defaultBootImageConfig(ctx))
+	// Create boot image for the ART apex (build artifacts are accessed via the global boot image config).
+	d.otherImages = append(d.otherImages, buildBootImage(ctx, artBootImageConfig(ctx)))
 	if global.GenerateApexImage {
-		d.otherImages = append(d.otherImages, buildBootImage(ctx, apexBootImageConfig(ctx)))
+		// Create boot images for the JIT-zygote experiment.
+		d.otherImages = append(d.otherImages, buildBootImage(ctx, artJZBootImageConfig(ctx)))
+		d.otherImages = append(d.otherImages, buildBootImage(ctx, frameworkJZBootImageConfig(ctx)))
 	}
 
 	dumpOatRules(ctx, d.defaultBootImage)
@@ -174,12 +229,9 @@
 
 // buildBootImage takes a bootImageConfig, creates rules to build it, and returns a *bootImage.
 func buildBootImage(ctx android.SingletonContext, config bootImageConfig) *bootImage {
-	global := dexpreoptGlobalConfig(ctx)
-
 	image := newBootImage(ctx, config)
 
 	bootDexJars := make(android.Paths, len(image.modules))
-
 	ctx.VisitAllModules(func(module android.Module) {
 		// Collect dex jar paths for the modules listed above.
 		if j, ok := module.(interface{ DexJar() android.Path }); ok {
@@ -216,20 +268,18 @@
 	}
 
 	profile := bootImageProfileRule(ctx, image, missingDeps)
+	bootFrameworkProfileRule(ctx, image, missingDeps)
 
 	var allFiles android.Paths
-
-	if !global.DisablePreopt {
-		for _, target := range image.targets {
-			files := buildBootImageRuleForArch(ctx, image, target.Arch.ArchType, profile, missingDeps)
-			allFiles = append(allFiles, files.Paths()...)
-		}
+	for _, target := range image.targets {
+		files := buildBootImageRuleForArch(ctx, image, target.Arch.ArchType, profile, missingDeps)
+		allFiles = append(allFiles, files.Paths()...)
 	}
 
 	if image.zip != nil {
 		rule := android.NewRuleBuilder()
 		rule.Command().
-			Tool(ctx.Config().HostToolPath(ctx, "soong_zip")).
+			BuiltTool(ctx, "soong_zip").
 			FlagWithOutput("-o ", image.zip).
 			FlagWithArg("-C ", image.dir.String()).
 			FlagWithInputList("-f ", allFiles, " -f ")
@@ -245,11 +295,12 @@
 
 	global := dexpreoptGlobalConfig(ctx)
 
-	symbolsDir := image.symbolsDir.Join(ctx, "system/framework", arch.String())
-	symbolsFile := symbolsDir.Join(ctx, image.name+".oat")
-	outputDir := image.dir.Join(ctx, "system/framework", arch.String())
-	outputPath := image.images[arch]
-	oatLocation := pathtools.ReplaceExtension(dexpreopt.PathToLocation(outputPath, arch), "oat")
+	symbolsDir := image.symbolsDir.Join(ctx, image.installSubdir, arch.String())
+	symbolsFile := symbolsDir.Join(ctx, image.stem+".oat")
+	outputDir := image.dir.Join(ctx, image.installSubdir, arch.String())
+	outputPath := outputDir.Join(ctx, image.stem+".oat")
+	oatLocation := dexpreopt.PathToLocation(outputPath, arch)
+	imagePath := outputPath.ReplaceExtension(ctx, "art")
 
 	rule := android.NewRuleBuilder()
 	rule.MissingDeps(missingDeps)
@@ -277,7 +328,7 @@
 
 	invocationPath := outputPath.ReplaceExtension(ctx, "invocation")
 
-	cmd.Tool(global.Tools.Dex2oat).
+	cmd.Tool(global.SoongConfig.Dex2oat).
 		Flag("--avoid-storing-invocation").
 		FlagWithOutput("--write-invocation-to=", invocationPath).ImplicitOutput(invocationPath).
 		Flag("--runtime-arg").FlagWithArg("-Xms", global.Dex2oatImageXms).
@@ -286,25 +337,33 @@
 	if profile != nil {
 		cmd.FlagWithArg("--compiler-filter=", "speed-profile")
 		cmd.FlagWithInput("--profile-file=", profile)
-	} else if global.PreloadedClasses.Valid() {
-		cmd.FlagWithInput("--image-classes=", global.PreloadedClasses.Path())
 	}
 
 	if global.DirtyImageObjects.Valid() {
 		cmd.FlagWithInput("--dirty-image-objects=", global.DirtyImageObjects.Path())
 	}
 
+	if image.extension {
+		artImage := artBootImageConfig(ctx).images[arch]
+		cmd.
+			Flag("--runtime-arg").FlagWithInputList("-Xbootclasspath:", image.dexPathsDeps.Paths(), ":").
+			Flag("--runtime-arg").FlagWithList("-Xbootclasspath-locations:", image.dexLocationsDeps, ":").
+			FlagWithArg("--boot-image=", dexpreopt.PathToLocation(artImage, arch)).Implicit(artImage)
+	} else {
+		cmd.FlagWithArg("--base=", ctx.Config().LibartImgDeviceBaseAddress())
+	}
+
 	cmd.
 		FlagForEachInput("--dex-file=", image.dexPaths.Paths()).
 		FlagForEachArg("--dex-location=", image.dexLocations).
 		Flag("--generate-debug-info").
 		Flag("--generate-build-id").
-		FlagWithOutput("--oat-symbols=", symbolsFile).
+		Flag("--image-format=lz4hc").
+		FlagWithArg("--oat-symbols=", symbolsFile.String()).
 		Flag("--strip").
-		FlagWithOutput("--oat-file=", outputPath.ReplaceExtension(ctx, "oat")).
+		FlagWithArg("--oat-file=", outputPath.String()).
 		FlagWithArg("--oat-location=", oatLocation).
-		FlagWithOutput("--image=", outputPath).
-		FlagWithArg("--base=", ctx.Config().LibartImgDeviceBaseAddress()).
+		FlagWithArg("--image=", imagePath.String()).
 		FlagWithArg("--instruction-set=", arch.String()).
 		FlagWithArg("--instruction-set-variant=", global.CpuVariant[arch]).
 		FlagWithArg("--instruction-set-features=", global.InstructionSetFeatures[arch]).
@@ -322,8 +381,8 @@
 
 	cmd.Textf(`|| ( echo %s ; false )`, proptools.ShellEscape(failureMessage))
 
-	installDir := filepath.Join("/system/framework", arch.String())
-	vdexInstallDir := filepath.Join("/system/framework")
+	installDir := filepath.Join("/", image.installSubdir, arch.String())
+	vdexInstallDir := filepath.Join("/", image.installSubdir)
 
 	var vdexInstalls android.RuleBuilderInstalls
 	var unstrippedInstalls android.RuleBuilderInstalls
@@ -374,11 +433,11 @@
 func bootImageProfileRule(ctx android.SingletonContext, image *bootImage, missingDeps []string) android.WritablePath {
 	global := dexpreoptGlobalConfig(ctx)
 
-	if !global.UseProfileForBootImage || ctx.Config().IsPdkBuild() || ctx.Config().UnbundledBuild() {
+	if global.DisableGenerateProfile || ctx.Config().IsPdkBuild() || ctx.Config().UnbundledBuild() {
 		return nil
 	}
-	return ctx.Config().Once(bootImageProfileRuleKey, func() interface{} {
-		tools := global.Tools
+	profile := ctx.Config().Once(bootImageProfileRuleKey, func() interface{} {
+		defaultProfile := "frameworks/base/config/boot-image-profile.txt"
 
 		rule := android.NewRuleBuilder()
 		rule.MissingDeps(missingDeps)
@@ -390,28 +449,23 @@
 			bootImageProfile = combinedBootImageProfile
 		} else if len(global.BootImageProfiles) == 1 {
 			bootImageProfile = global.BootImageProfiles[0]
+		} else if path := android.ExistentPathForSource(ctx, defaultProfile); path.Valid() {
+			bootImageProfile = path.Path()
 		} else {
-			// If not set, use the default.  Some branches like master-art-host don't have frameworks/base, so manually
-			// handle the case that the default is missing.  Those branches won't attempt to build the profile rule,
-			// and if they do they'll get a missing deps error.
-			defaultProfile := "frameworks/base/config/boot-image-profile.txt"
-			path := android.ExistentPathForSource(ctx, defaultProfile)
-			if path.Valid() {
-				bootImageProfile = path.Path()
-			} else {
-				missingDeps = append(missingDeps, defaultProfile)
-				bootImageProfile = android.PathForOutput(ctx, "missing")
-			}
+			// No profile (not even a default one, which is the case on some branches
+			// like master-art-host that don't have frameworks/base).
+			// Return nil and continue without profile.
+			return nil
 		}
 
 		profile := image.dir.Join(ctx, "boot.prof")
 
 		rule.Command().
 			Text(`ANDROID_LOG_TAGS="*:e"`).
-			Tool(tools.Profman).
+			Tool(global.SoongConfig.Profman).
 			FlagWithInput("--create-profile-from=", bootImageProfile).
-			FlagForEachInput("--apk=", image.dexPaths.Paths()).
-			FlagForEachArg("--dex-location=", image.dexLocations).
+			FlagForEachInput("--apk=", image.dexPathsDeps.Paths()).
+			FlagForEachArg("--dex-location=", image.dexLocationsDeps).
 			FlagWithOutput("--reference-profile-file=", profile)
 
 		rule.Install(profile, "/system/etc/boot-image.prof")
@@ -421,11 +475,59 @@
 		image.profileInstalls = rule.Installs()
 
 		return profile
-	}).(android.WritablePath)
+	})
+	if profile == nil {
+		return nil // wrap nil into a typed pointer with value nil
+	}
+	return profile.(android.WritablePath)
 }
 
 var bootImageProfileRuleKey = android.NewOnceKey("bootImageProfileRule")
 
+func bootFrameworkProfileRule(ctx android.SingletonContext, image *bootImage, missingDeps []string) android.WritablePath {
+	global := dexpreoptGlobalConfig(ctx)
+
+	if global.DisableGenerateProfile || ctx.Config().IsPdkBuild() || ctx.Config().UnbundledBuild() {
+		return nil
+	}
+	return ctx.Config().Once(bootFrameworkProfileRuleKey, func() interface{} {
+		rule := android.NewRuleBuilder()
+		rule.MissingDeps(missingDeps)
+
+		// Some branches like master-art-host don't have frameworks/base, so manually
+		// handle the case that the default is missing.  Those branches won't attempt to build the profile rule,
+		// and if they do they'll get a missing deps error.
+		defaultProfile := "frameworks/base/config/boot-profile.txt"
+		path := android.ExistentPathForSource(ctx, defaultProfile)
+		var bootFrameworkProfile android.Path
+		if path.Valid() {
+			bootFrameworkProfile = path.Path()
+		} else {
+			missingDeps = append(missingDeps, defaultProfile)
+			bootFrameworkProfile = android.PathForOutput(ctx, "missing")
+		}
+
+		profile := image.dir.Join(ctx, "boot.bprof")
+
+		rule.Command().
+			Text(`ANDROID_LOG_TAGS="*:e"`).
+			Tool(global.SoongConfig.Profman).
+			Flag("--generate-boot-profile").
+			FlagWithInput("--create-profile-from=", bootFrameworkProfile).
+			FlagForEachInput("--apk=", image.dexPathsDeps.Paths()).
+			FlagForEachArg("--dex-location=", image.dexLocationsDeps).
+			FlagWithOutput("--reference-profile-file=", profile)
+
+		rule.Install(profile, "/system/etc/boot-image.bprof")
+		rule.Build(pctx, ctx, "bootFrameworkProfile", "profile boot framework jars")
+		image.profileInstalls = append(image.profileInstalls, rule.Installs()...)
+
+		return profile
+	}).(android.WritablePath)
+}
+
+var bootFrameworkProfileRuleKey = android.NewOnceKey("bootFrameworkProfileRule")
+
 func dumpOatRules(ctx android.SingletonContext, image *bootImage) {
 	var archs []android.ArchType
 	for arch := range image.images {
@@ -440,10 +542,10 @@
 		rule := android.NewRuleBuilder()
 		rule.Command().
 			// TODO: for now, use the debug version for better error reporting
-			Tool(ctx.Config().HostToolPath(ctx, "oatdumpd")).
-			FlagWithInputList("--runtime-arg -Xbootclasspath:", image.dexPaths.Paths(), ":").
-			FlagWithList("--runtime-arg -Xbootclasspath-locations:", image.dexLocations, ":").
-			FlagWithArg("--image=", dexpreopt.PathToLocation(image.images[arch], arch)).Implicit(image.images[arch]).
+			BuiltTool(ctx, "oatdumpd").
+			FlagWithInputList("--runtime-arg -Xbootclasspath:", image.dexPathsDeps.Paths(), ":").
+			FlagWithList("--runtime-arg -Xbootclasspath-locations:", image.dexLocationsDeps, ":").
+			FlagWithArg("--image=", strings.Join(image.imageLocations, ":")).Implicits(image.imagesDeps[arch].Paths()).
 			FlagWithOutput("--output=", output).
 			FlagWithArg("--instruction-set=", arch.String())
 		rule.Build(pctx, ctx, "dump-oat-boot-"+arch.String(), "dump oat boot "+arch.String())
@@ -486,14 +588,14 @@
 func (d *dexpreoptBootJars) MakeVars(ctx android.MakeVarsContext) {
 	if d.dexpreoptConfigForMake != nil {
 		ctx.Strict("DEX_PREOPT_CONFIG_FOR_MAKE", d.dexpreoptConfigForMake.String())
+		ctx.Strict("DEX_PREOPT_SOONG_CONFIG_FOR_MAKE", android.PathForOutput(ctx, "dexpreopt_soong.config").String())
 	}
 
 	image := d.defaultBootImage
 	if image != nil {
 		ctx.Strict("DEXPREOPT_IMAGE_PROFILE_BUILT_INSTALLED", image.profileInstalls.String())
-		ctx.Strict("DEXPREOPT_BOOTCLASSPATH_DEX_FILES", strings.Join(image.dexPaths.Strings(), " "))
-		ctx.Strict("DEXPREOPT_BOOTCLASSPATH_DEX_LOCATIONS", strings.Join(image.dexLocations, " "))
-		ctx.Strict("DEXPREOPT_IMAGE_ZIP_"+image.name, image.zip.String())
+		ctx.Strict("DEXPREOPT_BOOTCLASSPATH_DEX_FILES", strings.Join(image.dexPathsDeps.Strings(), " "))
+		ctx.Strict("DEXPREOPT_BOOTCLASSPATH_DEX_LOCATIONS", strings.Join(image.dexLocationsDeps, " "))
 
 		var imageNames []string
 		for _, current := range append(d.otherImages, image) {
@@ -506,14 +608,16 @@
 			sort.Slice(arches, func(i, j int) bool { return arches[i].String() < arches[j].String() })
 
 			for _, arch := range arches {
-				ctx.Strict("DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_"+current.name+"_"+arch.String(), current.vdexInstalls[arch].String())
-				ctx.Strict("DEXPREOPT_IMAGE_"+current.name+"_"+arch.String(), current.images[arch].String())
-				ctx.Strict("DEXPREOPT_IMAGE_DEPS_"+current.name+"_"+arch.String(), strings.Join(current.imagesDeps[arch].Strings(), " "))
-				ctx.Strict("DEXPREOPT_IMAGE_BUILT_INSTALLED_"+current.name+"_"+arch.String(), current.installs[arch].String())
-				ctx.Strict("DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_"+current.name+"_"+arch.String(), current.unstrippedInstalls[arch].String())
-				if current.zip != nil {
-				}
+				sfx := current.name + "_" + arch.String()
+				ctx.Strict("DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_"+sfx, current.vdexInstalls[arch].String())
+				ctx.Strict("DEXPREOPT_IMAGE_"+sfx, current.images[arch].String())
+				ctx.Strict("DEXPREOPT_IMAGE_DEPS_"+sfx, strings.Join(current.imagesDeps[arch].Strings(), " "))
+				ctx.Strict("DEXPREOPT_IMAGE_BUILT_INSTALLED_"+sfx, current.installs[arch].String())
+				ctx.Strict("DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_"+sfx, current.unstrippedInstalls[arch].String())
 			}
+
+			ctx.Strict("DEXPREOPT_IMAGE_LOCATIONS_"+current.name, strings.Join(current.imageLocations, ":"))
+			ctx.Strict("DEXPREOPT_IMAGE_ZIP_"+current.name, current.zip.String())
 		}
 		ctx.Strict("DEXPREOPT_IMAGE_NAMES", strings.Join(imageNames, " "))
 	}
diff --git a/java/dexpreopt_bootjars_test.go b/java/dexpreopt_bootjars_test.go
index cbb52f1..4ce30f6 100644
--- a/java/dexpreopt_bootjars_test.go
+++ b/java/dexpreopt_bootjars_test.go
@@ -44,24 +44,25 @@
 		}
 	`
 
-	config := testConfig(nil)
+	config := testConfig(nil, bp, nil)
 
-	pathCtx := android.PathContextForTesting(config, nil)
+	pathCtx := android.PathContextForTesting(config)
 	dexpreoptConfig := dexpreopt.GlobalConfigForTests(pathCtx)
-	dexpreoptConfig.RuntimeApexJars = []string{"foo", "bar", "baz"}
+	dexpreoptConfig.BootJars = []string{"foo", "bar", "baz"}
 	setDexpreoptTestGlobalConfig(config, dexpreoptConfig)
 
-	ctx := testContext(config, bp, nil)
+	ctx := testContext()
 
-	ctx.RegisterSingletonType("dex_bootjars", android.SingletonFactoryAdaptor(dexpreoptBootJarsFactory))
+	ctx.RegisterSingletonType("dex_bootjars", dexpreoptBootJarsFactory)
 
 	run(t, ctx, config)
 
 	dexpreoptBootJars := ctx.SingletonForTests("dex_bootjars")
 
-	bootArt := dexpreoptBootJars.Output("boot.art")
+	bootArt := dexpreoptBootJars.Output("boot-foo.art")
 
 	expectedInputs := []string{
+		"dex_artjars/apex/com.android.art/javalib/arm64/boot.art",
 		"dex_bootjars_input/foo.jar",
 		"dex_bootjars_input/bar.jar",
 		"dex_bootjars_input/baz.jar",
@@ -82,19 +83,19 @@
 	expectedOutputs := []string{
 		"dex_bootjars/system/framework/arm64/boot.invocation",
 
-		"dex_bootjars/system/framework/arm64/boot.art",
+		"dex_bootjars/system/framework/arm64/boot-foo.art",
 		"dex_bootjars/system/framework/arm64/boot-bar.art",
 		"dex_bootjars/system/framework/arm64/boot-baz.art",
 
-		"dex_bootjars/system/framework/arm64/boot.oat",
+		"dex_bootjars/system/framework/arm64/boot-foo.oat",
 		"dex_bootjars/system/framework/arm64/boot-bar.oat",
 		"dex_bootjars/system/framework/arm64/boot-baz.oat",
 
-		"dex_bootjars/system/framework/arm64/boot.vdex",
+		"dex_bootjars/system/framework/arm64/boot-foo.vdex",
 		"dex_bootjars/system/framework/arm64/boot-bar.vdex",
 		"dex_bootjars/system/framework/arm64/boot-baz.vdex",
 
-		"dex_bootjars_unstripped/system/framework/arm64/boot.oat",
+		"dex_bootjars_unstripped/system/framework/arm64/boot-foo.oat",
 		"dex_bootjars_unstripped/system/framework/arm64/boot-bar.oat",
 		"dex_bootjars_unstripped/system/framework/arm64/boot-baz.oat",
 	}
diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go
index c396d3e..31bec93 100644
--- a/java/dexpreopt_config.go
+++ b/java/dexpreopt_config.go
@@ -36,9 +36,11 @@
 
 func dexpreoptGlobalConfigRaw(ctx android.PathContext) globalConfigAndRaw {
 	return ctx.Config().Once(dexpreoptGlobalConfigKey, func() interface{} {
-		if f := ctx.Config().DexpreoptGlobalConfig(); f != "" {
-			ctx.AddNinjaFileDeps(f)
-			globalConfig, data, err := dexpreopt.LoadGlobalConfig(ctx, f)
+		if data, err := ctx.Config().DexpreoptGlobalConfig(ctx); err != nil {
+			panic(err)
+		} else if data != nil {
+			soongConfig := dexpreopt.CreateGlobalSoongConfig(ctx)
+			globalConfig, err := dexpreopt.LoadGlobalConfig(ctx, data, soongConfig)
 			if err != nil {
 				panic(err)
 			}
@@ -49,7 +51,8 @@
 		return ctx.Config().Once(dexpreoptTestGlobalConfigKey, func() interface{} {
 			// Nope, return a config with preopting disabled
 			return globalConfigAndRaw{dexpreopt.GlobalConfig{
-				DisablePreopt: true,
+				DisablePreopt:          true,
+				DisableGenerateProfile: true,
 			}, nil}
 		})
 	}).(globalConfigAndRaw)
@@ -76,6 +79,10 @@
 			systemServerClasspathLocations = append(systemServerClasspathLocations,
 				filepath.Join("/system/framework", m+".jar"))
 		}
+		for _, m := range global.UpdatableSystemServerJars {
+			systemServerClasspathLocations = append(systemServerClasspathLocations,
+				dexpreopt.GetJarLocationFromApexJarPair(m))
+		}
 		return systemServerClasspathLocations
 	})
 }
@@ -86,11 +93,7 @@
 // supported through native bridge.
 func dexpreoptTargets(ctx android.PathContext) []android.Target {
 	var targets []android.Target
-	for i, target := range ctx.Config().Targets[android.Android] {
-		if ctx.Config().SecondArchIsTranslated() && i > 0 {
-			break
-		}
-
+	for _, target := range ctx.Config().Targets[android.Android] {
 		if target.NativeBridge == android.NativeBridgeDisabled {
 			targets = append(targets, target)
 		}
@@ -99,148 +102,187 @@
 	return targets
 }
 
-// defaultBootImageConfig returns the bootImageConfig that will be used to dexpreopt modules.  It is computed once the
-// first time it is called for any ctx.Config(), and returns the same slice for all future calls with the same
-// ctx.Config().
+func stemOf(moduleName string) string {
+	// b/139391334: the stem of framework-minus-apex is framework
+	// This is hard coded here until we find a good way to query the stem
+	// of a module before any other mutators are run
+	if moduleName == "framework-minus-apex" {
+		return "framework"
+	}
+	return moduleName
+}
+
+func getJarsFromApexJarPairs(apexJarPairs []string) []string {
+	modules := make([]string, len(apexJarPairs))
+	for i, p := range apexJarPairs {
+		_, jar := dexpreopt.SplitApexJarPair(p)
+		modules[i] = jar
+	}
+	return modules
+}
+
+var (
+	bootImageConfigKey       = android.NewOnceKey("bootImageConfig")
+	artBootImageName         = "art"
+	frameworkBootImageName   = "boot"
+	artJZBootImageName       = "jitzygote-art"
+	frameworkJZBootImageName = "jitzygote-boot"
+)
+
+// Construct the global boot image configs.
+func genBootImageConfigs(ctx android.PathContext) map[string]*bootImageConfig {
+	return ctx.Config().Once(bootImageConfigKey, func() interface{} {
+
+		global := dexpreoptGlobalConfig(ctx)
+		targets := dexpreoptTargets(ctx)
+		deviceDir := android.PathForOutput(ctx, ctx.Config().DeviceName())
+
+		artModules := global.ArtApexJars
+		// With EMMA_INSTRUMENT_FRAMEWORK=true the Core libraries depend on jacoco.
+		if ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") {
+			artModules = append(artModules, "jacocoagent")
+		}
+		frameworkModules := android.RemoveListFromList(global.BootJars,
+			concat(artModules, getJarsFromApexJarPairs(global.UpdatableBootJars)))
+
+		artSubdir := "apex/com.android.art/javalib"
+		frameworkSubdir := "system/framework"
+
+		var artLocations, frameworkLocations []string
+		for _, m := range artModules {
+			artLocations = append(artLocations, filepath.Join("/"+artSubdir, stemOf(m)+".jar"))
+		}
+		for _, m := range frameworkModules {
+			frameworkLocations = append(frameworkLocations, filepath.Join("/"+frameworkSubdir, stemOf(m)+".jar"))
+		}
+
+		// ART config for the primary boot image in the ART apex.
+		// It includes the Core Libraries.
+		artCfg := bootImageConfig{
+			extension:        false,
+			name:             artBootImageName,
+			stem:             "boot",
+			installSubdir:    artSubdir,
+			modules:          artModules,
+			dexLocations:     artLocations,
+			dexLocationsDeps: artLocations,
+		}
+
+		// Framework config for the boot image extension.
+		// It includes framework libraries and depends on the ART config.
+		frameworkCfg := bootImageConfig{
+			extension:        true,
+			name:             frameworkBootImageName,
+			stem:             "boot",
+			installSubdir:    frameworkSubdir,
+			modules:          frameworkModules,
+			dexLocations:     frameworkLocations,
+			dexLocationsDeps: append(artLocations, frameworkLocations...),
+		}
+
+		// ART config for JIT-zygote boot image.
+		artJZCfg := bootImageConfig{
+			extension:        false,
+			name:             artJZBootImageName,
+			stem:             "apex",
+			installSubdir:    artSubdir,
+			modules:          artModules,
+			dexLocations:     artLocations,
+			dexLocationsDeps: artLocations,
+		}
+
+		// Framework config for JIT-zygote boot image extension.
+		frameworkJZCfg := bootImageConfig{
+			extension:        true,
+			name:             frameworkJZBootImageName,
+			stem:             "apex",
+			installSubdir:    frameworkSubdir,
+			modules:          frameworkModules,
+			dexLocations:     frameworkLocations,
+			dexLocationsDeps: append(artLocations, frameworkLocations...),
+		}
+
+		configs := map[string]*bootImageConfig{
+			artBootImageName:         &artCfg,
+			frameworkBootImageName:   &frameworkCfg,
+			artJZBootImageName:       &artJZCfg,
+			frameworkJZBootImageName: &frameworkJZCfg,
+		}
+
+		// common to all configs
+		for _, c := range configs {
+			c.targets = targets
+
+			c.dir = deviceDir.Join(ctx, "dex_"+c.name+"jars")
+			c.symbolsDir = deviceDir.Join(ctx, "dex_"+c.name+"jars_unstripped")
+
+			// expands to <stem>.art for primary image and <stem>-<1st module>.art for extension
+			imageName := c.firstModuleNameOrStem() + ".art"
+
+			c.imageLocations = []string{c.dir.Join(ctx, c.installSubdir, imageName).String()}
+
+			// The path to bootclasspath dex files needs to be known at module
+			// GenerateAndroidBuildAction time, before the bootclasspath modules have been compiled.
+			// Set up known paths for them, the singleton rules will copy them there.
+			// TODO(b/143682396): use module dependencies instead
+			inputDir := deviceDir.Join(ctx, "dex_"+c.name+"jars_input")
+			for _, m := range c.modules {
+				c.dexPaths = append(c.dexPaths, inputDir.Join(ctx, stemOf(m)+".jar"))
+			}
+			c.dexPathsDeps = c.dexPaths
+
+			c.images = make(map[android.ArchType]android.OutputPath)
+			c.imagesDeps = make(map[android.ArchType]android.OutputPaths)
+
+			for _, target := range targets {
+				arch := target.Arch.ArchType
+				imageDir := c.dir.Join(ctx, c.installSubdir, arch.String())
+				c.images[arch] = imageDir.Join(ctx, imageName)
+				c.imagesDeps[arch] = c.moduleFiles(ctx, imageDir, ".art", ".oat", ".vdex")
+			}
+
+			c.zip = c.dir.Join(ctx, c.name+".zip")
+		}
+
+		// specific to the framework config
+		frameworkCfg.dexPathsDeps = append(artCfg.dexPathsDeps, frameworkCfg.dexPathsDeps...)
+		frameworkCfg.imageLocations = append(artCfg.imageLocations, frameworkCfg.imageLocations...)
+
+		// specific to the jitzygote-framework config
+		frameworkJZCfg.dexPathsDeps = append(artJZCfg.dexPathsDeps, frameworkJZCfg.dexPathsDeps...)
+		frameworkJZCfg.imageLocations = append(artJZCfg.imageLocations, frameworkJZCfg.imageLocations...)
+
+		return configs
+	}).(map[string]*bootImageConfig)
+}
+
+func artBootImageConfig(ctx android.PathContext) bootImageConfig {
+	return *genBootImageConfigs(ctx)[artBootImageName]
+}
+
 func defaultBootImageConfig(ctx android.PathContext) bootImageConfig {
-	return ctx.Config().Once(defaultBootImageConfigKey, func() interface{} {
-		global := dexpreoptGlobalConfig(ctx)
-
-		runtimeModules := global.RuntimeApexJars
-		nonFrameworkModules := concat(runtimeModules, global.ProductUpdatableBootModules)
-		frameworkModules := android.RemoveListFromList(global.BootJars, nonFrameworkModules)
-
-		var nonUpdatableBootModules []string
-		var nonUpdatableBootLocations []string
-
-		for _, m := range runtimeModules {
-			nonUpdatableBootModules = append(nonUpdatableBootModules, m)
-			nonUpdatableBootLocations = append(nonUpdatableBootLocations,
-				filepath.Join("/apex/com.android.runtime/javalib", m+".jar"))
-		}
-
-		for _, m := range frameworkModules {
-			nonUpdatableBootModules = append(nonUpdatableBootModules, m)
-			nonUpdatableBootLocations = append(nonUpdatableBootLocations,
-				filepath.Join("/system/framework", m+".jar"))
-		}
-
-		// The path to bootclasspath dex files needs to be known at module GenerateAndroidBuildAction time, before
-		// the bootclasspath modules have been compiled.  Set up known paths for them, the singleton rules will copy
-		// them there.
-		// TODO: use module dependencies instead
-		var nonUpdatableBootDexPaths android.WritablePaths
-		for _, m := range nonUpdatableBootModules {
-			nonUpdatableBootDexPaths = append(nonUpdatableBootDexPaths,
-				android.PathForOutput(ctx, ctx.Config().DeviceName(), "dex_bootjars_input", m+".jar"))
-		}
-
-		dir := android.PathForOutput(ctx, ctx.Config().DeviceName(), "dex_bootjars")
-		symbolsDir := android.PathForOutput(ctx, ctx.Config().DeviceName(), "dex_bootjars_unstripped")
-		zip := dir.Join(ctx, "boot.zip")
-
-		targets := dexpreoptTargets(ctx)
-
-		imageConfig := bootImageConfig{
-			name:         "boot",
-			modules:      nonUpdatableBootModules,
-			dexLocations: nonUpdatableBootLocations,
-			dexPaths:     nonUpdatableBootDexPaths,
-			dir:          dir,
-			symbolsDir:   symbolsDir,
-			images:       make(map[android.ArchType]android.OutputPath),
-			imagesDeps:   make(map[android.ArchType]android.Paths),
-			targets:      targets,
-			zip:          zip,
-		}
-
-		for _, target := range targets {
-			imageDir := dir.Join(ctx, "system/framework", target.Arch.ArchType.String())
-			imageConfig.images[target.Arch.ArchType] = imageDir.Join(ctx, "boot.art")
-
-			imagesDeps := make([]android.Path, 0, len(imageConfig.modules)*3)
-			for _, dep := range imageConfig.moduleFiles(ctx, imageDir, ".art", ".oat", ".vdex") {
-				imagesDeps = append(imagesDeps, dep)
-			}
-			imageConfig.imagesDeps[target.Arch.ArchType] = imagesDeps
-		}
-
-		return imageConfig
-	}).(bootImageConfig)
+	return *genBootImageConfigs(ctx)[frameworkBootImageName]
 }
 
-var defaultBootImageConfigKey = android.NewOnceKey("defaultBootImageConfig")
-
-func apexBootImageConfig(ctx android.PathContext) bootImageConfig {
-	return ctx.Config().Once(apexBootImageConfigKey, func() interface{} {
-		global := dexpreoptGlobalConfig(ctx)
-
-		runtimeModules := global.RuntimeApexJars
-		nonFrameworkModules := concat(runtimeModules, global.ProductUpdatableBootModules)
-		frameworkModules := android.RemoveListFromList(global.BootJars, nonFrameworkModules)
-		imageModules := concat(runtimeModules, frameworkModules)
-
-		var bootLocations []string
-
-		for _, m := range runtimeModules {
-			bootLocations = append(bootLocations,
-				filepath.Join("/apex/com.android.runtime/javalib", m+".jar"))
-		}
-
-		for _, m := range frameworkModules {
-			bootLocations = append(bootLocations,
-				filepath.Join("/system/framework", m+".jar"))
-		}
-
-		// The path to bootclasspath dex files needs to be known at module GenerateAndroidBuildAction time, before
-		// the bootclasspath modules have been compiled.  Set up known paths for them, the singleton rules will copy
-		// them there.
-		// TODO: use module dependencies instead
-		var bootDexPaths android.WritablePaths
-		for _, m := range imageModules {
-			bootDexPaths = append(bootDexPaths,
-				android.PathForOutput(ctx, ctx.Config().DeviceName(), "dex_apexjars_input", m+".jar"))
-		}
-
-		dir := android.PathForOutput(ctx, ctx.Config().DeviceName(), "dex_apexjars")
-		symbolsDir := android.PathForOutput(ctx, ctx.Config().DeviceName(), "dex_apexjars_unstripped")
-
-		targets := dexpreoptTargets(ctx)
-
-		imageConfig := bootImageConfig{
-			name:         "apex",
-			modules:      imageModules,
-			dexLocations: bootLocations,
-			dexPaths:     bootDexPaths,
-			dir:          dir,
-			symbolsDir:   symbolsDir,
-			targets:      targets,
-			images:       make(map[android.ArchType]android.OutputPath),
-			imagesDeps:   make(map[android.ArchType]android.Paths),
-		}
-
-		for _, target := range targets {
-			imageDir := dir.Join(ctx, "system/framework", target.Arch.ArchType.String())
-			imageConfig.images[target.Arch.ArchType] = imageDir.Join(ctx, "apex.art")
-
-			imagesDeps := make([]android.Path, 0, len(imageConfig.modules)*3)
-			for _, dep := range imageConfig.moduleFiles(ctx, imageDir, ".art", ".oat", ".vdex") {
-				imagesDeps = append(imagesDeps, dep)
-			}
-			imageConfig.imagesDeps[target.Arch.ArchType] = imagesDeps
-		}
-
-		return imageConfig
-	}).(bootImageConfig)
+func artJZBootImageConfig(ctx android.PathContext) bootImageConfig {
+	return *genBootImageConfigs(ctx)[artJZBootImageName]
 }
 
-var apexBootImageConfigKey = android.NewOnceKey("apexBootImageConfig")
+func frameworkJZBootImageConfig(ctx android.PathContext) bootImageConfig {
+	return *genBootImageConfigs(ctx)[frameworkJZBootImageName]
+}
 
 func defaultBootclasspath(ctx android.PathContext) []string {
 	return ctx.Config().OnceStringSlice(defaultBootclasspathKey, func() []string {
 		global := dexpreoptGlobalConfig(ctx)
 		image := defaultBootImageConfig(ctx)
-		bootclasspath := append(copyOf(image.dexLocations), global.ProductUpdatableBootLocations...)
+
+		updatableBootclasspath := make([]string, len(global.UpdatableBootJars))
+		for i, p := range global.UpdatableBootJars {
+			updatableBootclasspath[i] = dexpreopt.GetJarLocationFromApexJarPair(p)
+		}
+
+		bootclasspath := append(copyOf(image.dexLocationsDeps), updatableBootclasspath...)
 		return bootclasspath
 	})
 }
@@ -255,7 +297,7 @@
 
 func dexpreoptConfigMakevars(ctx android.MakeVarsContext) {
 	ctx.Strict("PRODUCT_BOOTCLASSPATH", strings.Join(defaultBootclasspath(ctx), ":"))
-	ctx.Strict("PRODUCT_DEX2OAT_BOOTCLASSPATH", strings.Join(defaultBootImageConfig(ctx).dexLocations, ":"))
+	ctx.Strict("PRODUCT_DEX2OAT_BOOTCLASSPATH", strings.Join(defaultBootImageConfig(ctx).dexLocationsDeps, ":"))
 	ctx.Strict("PRODUCT_SYSTEM_SERVER_CLASSPATH", strings.Join(systemServerClasspath(ctx), ":"))
 
 	ctx.Strict("DEXPREOPT_BOOT_JARS_MODULES", strings.Join(defaultBootImageConfig(ctx).modules, ":"))
diff --git a/java/dexpreopt_test.go b/java/dexpreopt_test.go
index 7d0109f..5550a4c 100644
--- a/java/dexpreopt_test.go
+++ b/java/dexpreopt_test.go
@@ -30,6 +30,7 @@
 				android_app {
 					name: "foo",
 					srcs: ["a.java"],
+					sdk_version: "current",
 				}`,
 			enabled: true,
 		},
@@ -57,6 +58,7 @@
 			bp: `
 				android_app {
 					name: "foo",
+					sdk_version: "current",
 				}`,
 			enabled: false,
 		},
@@ -66,11 +68,13 @@
 				android_app {
 					name: "foo",
 					static_libs: ["lib"],
+					sdk_version: "current",
 				}
 
 				java_library {
 					name: "lib",
 					srcs: ["a.java"],
+					sdk_version: "current",
 				}`,
 			enabled: true,
 		},
@@ -142,7 +146,7 @@
 
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
-			ctx := testJava(t, test.bp)
+			ctx, _ := testJava(t, test.bp)
 
 			dexpreopt := ctx.ModuleForTests("foo", "android_common").MaybeDescription("dexpreopt")
 			enabled := dexpreopt.Rule != nil
diff --git a/java/droiddoc.go b/java/droiddoc.go
index be1b281..f62f5f9 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -15,139 +15,49 @@
 package java
 
 import (
-	"android/soong/android"
-	"android/soong/java/config"
 	"fmt"
 	"path/filepath"
-	"runtime"
 	"strings"
 
 	"github.com/google/blueprint"
-)
+	"github.com/google/blueprint/proptools"
 
-var (
-	javadoc = pctx.AndroidStaticRule("javadoc",
-		blueprint.RuleParams{
-			Command: `rm -rf "$outDir" "$srcJarDir" "$stubsDir" && mkdir -p "$outDir" "$srcJarDir" "$stubsDir" && ` +
-				`${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
-				`${config.SoongJavacWrapper} ${config.JavadocCmd} -encoding UTF-8 @$out.rsp @$srcJarDir/list ` +
-				`$opts $bootclasspathArgs $classpathArgs $sourcepathArgs ` +
-				`-d $outDir -quiet  && ` +
-				`${config.SoongZipCmd} -write_if_changed -d -o $docZip -C $outDir -D $outDir && ` +
-				`${config.SoongZipCmd} -write_if_changed -jar -o $out -C $stubsDir -D $stubsDir $postDoclavaCmds && ` +
-				`rm -rf "$srcJarDir"`,
-
-			CommandDeps: []string{
-				"${config.ZipSyncCmd}",
-				"${config.JavadocCmd}",
-				"${config.SoongZipCmd}",
-			},
-			CommandOrderOnly: []string{"${config.SoongJavacWrapper}"},
-			Rspfile:          "$out.rsp",
-			RspfileContent:   "$in",
-			Restat:           true,
-		},
-		"outDir", "srcJarDir", "stubsDir", "srcJars", "opts",
-		"bootclasspathArgs", "classpathArgs", "sourcepathArgs", "docZip", "postDoclavaCmds")
-
-	apiCheck = pctx.AndroidStaticRule("apiCheck",
-		blueprint.RuleParams{
-			Command: `( ${config.ApiCheckCmd} -JXmx1024m -J"classpath $classpath" $opts ` +
-				`$apiFile $apiFileToCheck $removedApiFile $removedApiFileToCheck ` +
-				`&& touch $out ) || (echo -e "$msg" ; exit 38)`,
-			CommandDeps: []string{
-				"${config.ApiCheckCmd}",
-			},
-		},
-		"classpath", "opts", "apiFile", "apiFileToCheck", "removedApiFile", "removedApiFileToCheck", "msg")
-
-	updateApi = pctx.AndroidStaticRule("updateApi",
-		blueprint.RuleParams{
-			Command: `( ( cp -f $srcApiFile $destApiFile && cp -f $srcRemovedApiFile $destRemovedApiFile ) ` +
-				`&& touch $out ) || (echo failed to update public API ; exit 38)`,
-		},
-		"srcApiFile", "destApiFile", "srcRemovedApiFile", "destRemovedApiFile")
-
-	metalava = pctx.AndroidStaticRule("metalava",
-		blueprint.RuleParams{
-			Command: `rm -rf "$outDir" "$srcJarDir" "$stubsDir" && ` +
-				`mkdir -p "$outDir" "$srcJarDir" "$stubsDir" && ` +
-				`${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
-				`${config.JavaCmd}  ${config.JavaVmFlags} -jar ${config.MetalavaJar} -encoding UTF-8 -source $javaVersion @$out.rsp @$srcJarDir/list ` +
-				`$bootclasspathArgs $classpathArgs $sourcepathArgs --no-banner --color --quiet --format=v2 ` +
-				`$opts && ` +
-				`${config.SoongZipCmd} -write_if_changed -jar -o $out -C $stubsDir -D $stubsDir && ` +
-				`rm -rf "$srcJarDir"`,
-			CommandDeps: []string{
-				"${config.ZipSyncCmd}",
-				"${config.JavaCmd}",
-				"${config.MetalavaJar}",
-				"${config.SoongZipCmd}",
-			},
-			Rspfile:        "$out.rsp",
-			RspfileContent: "$in",
-			Restat:         true,
-		},
-		"outDir", "srcJarDir", "stubsDir", "srcJars", "javaVersion", "bootclasspathArgs",
-		"classpathArgs", "sourcepathArgs", "opts")
-
-	metalavaApiCheck = pctx.AndroidStaticRule("metalavaApiCheck",
-		blueprint.RuleParams{
-			Command: `( rm -rf "$srcJarDir" && mkdir -p "$srcJarDir" && ` +
-				`${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
-				`${config.JavaCmd}  ${config.JavaVmFlags} -jar ${config.MetalavaJar} -encoding UTF-8 -source $javaVersion @$out.rsp @$srcJarDir/list ` +
-				`$bootclasspathArgs $classpathArgs $sourcepathArgs --no-banner --color --quiet --format=v2 ` +
-				`$opts && touch $out && rm -rf "$srcJarDir") || ` +
-				`( echo -e "$msg" ; exit 38 )`,
-			CommandDeps: []string{
-				"${config.ZipSyncCmd}",
-				"${config.JavaCmd}",
-				"${config.MetalavaJar}",
-			},
-			Rspfile:        "$out.rsp",
-			RspfileContent: "$in",
-		},
-		"srcJarDir", "srcJars", "javaVersion", "bootclasspathArgs", "classpathArgs", "sourcepathArgs", "opts", "msg")
-
-	nullabilityWarningsCheck = pctx.AndroidStaticRule("nullabilityWarningsCheck",
-		blueprint.RuleParams{
-			Command: `( diff $expected $actual && touch $out ) || ( echo -e "$msg" ; exit 38 )`,
-		},
-		"expected", "actual", "msg")
-
-	dokka = pctx.AndroidStaticRule("dokka",
-		blueprint.RuleParams{
-			Command: `rm -rf "$outDir" "$srcJarDir" "$stubsDir" && ` +
-				`mkdir -p "$outDir" "$srcJarDir" "$stubsDir" && ` +
-				`${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
-				`${config.JavaCmd} ${config.JavaVmFlags} -jar ${config.DokkaJar} $srcJarDir ` +
-				`$classpathArgs -format dac -dacRoot /reference/kotlin -output $outDir $opts && ` +
-				`${config.SoongZipCmd} -write_if_changed -d -o $docZip -C $outDir -D $outDir && ` +
-				`${config.SoongZipCmd} -write_if_changed -jar -o $out -C $stubsDir -D $stubsDir && ` +
-				`rm -rf "$srcJarDir"`,
-			CommandDeps: []string{
-				"${config.ZipSyncCmd}",
-				"${config.DokkaJar}",
-				"${config.MetalavaJar}",
-				"${config.SoongZipCmd}",
-			},
-			Restat: true,
-		},
-		"outDir", "srcJarDir", "stubsDir", "srcJars", "classpathArgs", "opts", "docZip")
+	"android/soong/android"
+	"android/soong/java/config"
 )
 
 func init() {
-	android.RegisterModuleType("doc_defaults", DocDefaultsFactory)
-	android.RegisterModuleType("stubs_defaults", StubsDefaultsFactory)
+	RegisterDocsBuildComponents(android.InitRegistrationContext)
+	RegisterStubsBuildComponents(android.InitRegistrationContext)
 
-	android.RegisterModuleType("droiddoc", DroiddocFactory)
-	android.RegisterModuleType("droiddoc_host", DroiddocHostFactory)
-	android.RegisterModuleType("droiddoc_exported_dir", ExportedDroiddocDirFactory)
-	android.RegisterModuleType("javadoc", JavadocFactory)
-	android.RegisterModuleType("javadoc_host", JavadocHostFactory)
+	// Register sdk member type.
+	android.RegisterSdkMemberType(&droidStubsSdkMemberType{
+		SdkMemberTypeBase: android.SdkMemberTypeBase{
+			PropertyName: "stubs_sources",
+			// stubs_sources can be used with sdk to provide the source stubs for APIs provided by
+			// the APEX.
+			SupportsSdk: true,
+		},
+	})
+}
 
-	android.RegisterModuleType("droidstubs", DroidstubsFactory)
-	android.RegisterModuleType("droidstubs_host", DroidstubsHostFactory)
+func RegisterDocsBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("doc_defaults", DocDefaultsFactory)
+
+	ctx.RegisterModuleType("droiddoc", DroiddocFactory)
+	ctx.RegisterModuleType("droiddoc_host", DroiddocHostFactory)
+	ctx.RegisterModuleType("droiddoc_exported_dir", ExportedDroiddocDirFactory)
+	ctx.RegisterModuleType("javadoc", JavadocFactory)
+	ctx.RegisterModuleType("javadoc_host", JavadocHostFactory)
+}
+
+func RegisterStubsBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("stubs_defaults", StubsDefaultsFactory)
+
+	ctx.RegisterModuleType("droidstubs", DroidstubsFactory)
+	ctx.RegisterModuleType("droidstubs_host", DroidstubsHostFactory)
+
+	ctx.RegisterModuleType("prebuilt_stubs_sources", PrebuiltStubsSourcesFactory)
 }
 
 var (
@@ -168,27 +78,25 @@
 	// filegroup or genrule can be included within this property.
 	Exclude_srcs []string `android:"path,arch_variant"`
 
+	// list of package names that should actually be used. If this property is left unspecified,
+	// all the sources from the srcs property is used.
+	Filter_packages []string
+
 	// list of java libraries that will be in the classpath.
 	Libs []string `android:"arch_variant"`
 
-	// don't build against the framework libraries (ext, and framework for device targets)
-	No_framework_libs *bool
-
-	// the java library (in classpath) for documentation that provides java srcs and srcjars.
-	Srcs_lib *string
-
-	// the base dirs under srcs_lib will be scanned for java srcs.
-	Srcs_lib_whitelist_dirs []string
-
-	// the sub dirs under srcs_lib_whitelist_dirs will be scanned for java srcs.
-	Srcs_lib_whitelist_pkgs []string
-
 	// If set to false, don't allow this module(-docs.zip) to be exported. Defaults to true.
 	Installable *bool
 
-	// if not blank, set to the version of the sdk to compile against
+	// if not blank, set to the version of the sdk to compile against.
+	// Defaults to compiling against the current platform.
 	Sdk_version *string `android:"arch_variant"`
 
+	// When targeting 1.9 and above, override the modules to use with --system,
+	// otherwise provides defaults libraries to add to the bootclasspath.
+	// Defaults to "none"
+	System_modules *string
+
 	Aidl struct {
 		// Top level directories to pass to aidl tool
 		Include_dirs []string
@@ -224,6 +132,9 @@
 	// :module syntax).
 	Removed_api_file *string `android:"path"`
 
+	// If not blank, path to the baseline txt file for approved API check violations.
+	Baseline_file *string `android:"path"`
+
 	// Arguments to the apicheck tool.
 	Args *string
 }
@@ -240,7 +151,7 @@
 
 	// proofread file contains all of the text content of the javadocs concatenated into one file,
 	// suitable for spell-checking and other goodness.
-	Proofread_file *string `android:"path"`
+	Proofread_file *string
 
 	// a todo file lists the program elements that are missing documentation.
 	// At some point, this might be improved to show more warnings.
@@ -354,6 +265,16 @@
 		// do not perform API check against Last_released, in the case that both two specified API
 		// files by Last_released are modules which don't exist.
 		Ignore_missing_latest_api *bool `blueprint:"mutated"`
+
+		Api_lint struct {
+			Enabled *bool
+
+			// If set, performs api_lint on any new APIs not found in the given signature file
+			New_since *string `android:"path"`
+
+			// If not blank, path to the baseline txt file for approved API lint violations.
+			Baseline_file *string `android:"path"`
+		}
 	}
 
 	// user can specify the version of previous released API file in order to do compatibility check.
@@ -406,14 +327,6 @@
 	doclavaStubsFlags string
 	doclavaDocsFlags  string
 	postDoclavaCmds   string
-
-	metalavaStubsFlags                string
-	metalavaAnnotationsFlags          string
-	metalavaMergeAnnoDirFlags         string
-	metalavaInclusionAnnotationsFlags string
-	metalavaApiLevelsAnnotationsFlags string
-
-	metalavaApiToXmlFlags string
 }
 
 func InitDroiddocModule(module android.DefaultableModule, hod android.HostOrDeviceSupported) {
@@ -421,8 +334,10 @@
 	android.InitDefaultableModule(module)
 }
 
-func apiCheckEnabled(apiToCheck ApiToCheck, apiVersionTag string) bool {
-	if String(apiToCheck.Api_file) != "" && String(apiToCheck.Removed_api_file) != "" {
+func apiCheckEnabled(ctx android.ModuleContext, apiToCheck ApiToCheck, apiVersionTag string) bool {
+	if ctx.Config().IsEnvTrue("WITHOUT_CHECK_API") {
+		return false
+	} else if String(apiToCheck.Api_file) != "" && String(apiToCheck.Removed_api_file) != "" {
 		return true
 	} else if String(apiToCheck.Api_file) != "" {
 		panic("for " + apiVersionTag + " removed_api_file has to be non-empty!")
@@ -456,23 +371,6 @@
 	ApiFilePath() android.Path
 }
 
-func transformUpdateApi(ctx android.ModuleContext, destApiFile, destRemovedApiFile,
-	srcApiFile, srcRemovedApiFile android.Path, output android.WritablePath) {
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        updateApi,
-		Description: "Update API",
-		Output:      output,
-		Implicits: append(android.Paths{}, srcApiFile, srcRemovedApiFile,
-			destApiFile, destRemovedApiFile),
-		Args: map[string]string{
-			"destApiFile":        destApiFile.String(),
-			"srcApiFile":         srcApiFile.String(),
-			"destRemovedApiFile": destRemovedApiFile.String(),
-			"srcRemovedApiFile":  srcRemovedApiFile.String(),
-		},
-	})
-}
-
 //
 // Javadoc
 //
@@ -497,11 +395,14 @@
 	switch tag {
 	case "":
 		return android.Paths{j.stubsSrcJar}, nil
+	case ".docs.zip":
+		return android.Paths{j.docZip}, nil
 	default:
 		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
 	}
 }
 
+// javadoc converts .java source files to documentation using javadoc.
 func JavadocFactory() android.Module {
 	module := &Javadoc{}
 
@@ -511,6 +412,7 @@
 	return module
 }
 
+// javadoc_host converts .java source files to documentation using javadoc.
 func JavadocHostFactory() android.Module {
 	module := &Javadoc{}
 
@@ -526,6 +428,10 @@
 	return String(j.properties.Sdk_version)
 }
 
+func (j *Javadoc) systemModules() string {
+	return proptools.String(j.properties.System_modules)
+}
+
 func (j *Javadoc) minSdkVersion() string {
 	return j.sdkVersion()
 }
@@ -534,48 +440,23 @@
 	return j.sdkVersion()
 }
 
-func (j *Javadoc) noFrameworkLibs() bool {
-	return Bool(j.properties.No_framework_libs)
-}
-
 func (j *Javadoc) addDeps(ctx android.BottomUpMutatorContext) {
 	if ctx.Device() {
 		sdkDep := decodeSdkDep(ctx, sdkContext(j))
-		if sdkDep.hasStandardLibs() {
-			if sdkDep.useDefaultLibs {
-				ctx.AddVariationDependencies(nil, bootClasspathTag, config.DefaultBootclasspathLibraries...)
-				if ctx.Config().TargetOpenJDK9() {
-					ctx.AddVariationDependencies(nil, systemModulesTag, config.DefaultSystemModules)
-				}
-				if sdkDep.hasFrameworkLibs() {
-					ctx.AddVariationDependencies(nil, libTag, config.DefaultLibraries...)
-				}
-			} else if sdkDep.useModule {
-				if ctx.Config().TargetOpenJDK9() {
-					ctx.AddVariationDependencies(nil, systemModulesTag, sdkDep.systemModules)
-				}
-				ctx.AddVariationDependencies(nil, bootClasspathTag, sdkDep.modules...)
+		if sdkDep.useDefaultLibs {
+			ctx.AddVariationDependencies(nil, bootClasspathTag, config.DefaultBootclasspathLibraries...)
+			ctx.AddVariationDependencies(nil, systemModulesTag, config.DefaultSystemModules)
+			if sdkDep.hasFrameworkLibs() {
+				ctx.AddVariationDependencies(nil, libTag, config.DefaultLibraries...)
 			}
+		} else if sdkDep.useModule {
+			ctx.AddVariationDependencies(nil, bootClasspathTag, sdkDep.bootclasspath...)
+			ctx.AddVariationDependencies(nil, systemModulesTag, sdkDep.systemModules)
+			ctx.AddVariationDependencies(nil, java9LibTag, sdkDep.java9Classpath...)
 		}
 	}
 
 	ctx.AddVariationDependencies(nil, libTag, j.properties.Libs...)
-	if j.properties.Srcs_lib != nil {
-		ctx.AddVariationDependencies(nil, srcsLibTag, *j.properties.Srcs_lib)
-	}
-}
-
-func (j *Javadoc) genWhitelistPathPrefixes(whitelistPathPrefixes map[string]bool) {
-	for _, dir := range j.properties.Srcs_lib_whitelist_dirs {
-		for _, pkg := range j.properties.Srcs_lib_whitelist_pkgs {
-			// convert foo.bar.baz to foo/bar/baz
-			pkgAsPath := filepath.Join(strings.Split(pkg, ".")...)
-			prefix := filepath.Join(dir, pkgAsPath)
-			if _, found := whitelistPathPrefixes[prefix]; !found {
-				whitelistPathPrefixes[prefix] = true
-			}
-		}
-	}
 }
 
 func (j *Javadoc) collectAidlFlags(ctx android.ModuleContext, deps deps) droiddocBuilderFlags {
@@ -611,24 +492,33 @@
 	return strings.Join(flags, " "), deps
 }
 
+// TODO: remove the duplication between this and the one in gen.go
 func (j *Javadoc) genSources(ctx android.ModuleContext, srcFiles android.Paths,
 	flags droiddocBuilderFlags) android.Paths {
 
 	outSrcFiles := make(android.Paths, 0, len(srcFiles))
+	var aidlSrcs android.Paths
+
+	aidlIncludeFlags := genAidlIncludeFlags(srcFiles)
 
 	for _, srcFile := range srcFiles {
 		switch srcFile.Ext() {
 		case ".aidl":
-			javaFile := genAidl(ctx, srcFile, flags.aidlFlags, flags.aidlDeps)
-			outSrcFiles = append(outSrcFiles, javaFile)
-		case ".sysprop":
-			javaFile := genSysprop(ctx, srcFile)
+			aidlSrcs = append(aidlSrcs, srcFile)
+		case ".logtags":
+			javaFile := genLogtags(ctx, srcFile)
 			outSrcFiles = append(outSrcFiles, javaFile)
 		default:
 			outSrcFiles = append(outSrcFiles, srcFile)
 		}
 	}
 
+	// Process all aidl files together to support sharding them into one or more rules that produce srcjars.
+	if len(aidlSrcs) > 0 {
+		srcJarFiles := genAidl(ctx, aidlSrcs, flags.aidlFlags+aidlIncludeFlags, flags.aidlDeps)
+		outSrcFiles = append(outSrcFiles, srcJarFiles...)
+	}
+
 	return outSrcFiles
 }
 
@@ -637,7 +527,8 @@
 
 	sdkDep := decodeSdkDep(ctx, sdkContext(j))
 	if sdkDep.invalidVersion {
-		ctx.AddMissingDependencies(sdkDep.modules)
+		ctx.AddMissingDependencies(sdkDep.bootclasspath)
+		ctx.AddMissingDependencies(sdkDep.java9Classpath)
 	} else if sdkDep.useFiles {
 		deps.bootClasspath = append(deps.bootClasspath, sdkDep.jars...)
 	}
@@ -650,6 +541,10 @@
 		case bootClasspathTag:
 			if dep, ok := module.(Dependency); ok {
 				deps.bootClasspath = append(deps.bootClasspath, dep.ImplementationJars()...)
+			} else if sm, ok := module.(*SystemModules); ok {
+				// A system modules dependency has been added to the bootclasspath
+				// so add its libs to the bootclasspath.
+				deps.bootClasspath = append(deps.bootClasspath, sm.headerJars...)
 			} else {
 				panic(fmt.Errorf("unknown dependency %q for %q", otherName, ctx.ModuleName()))
 			}
@@ -659,31 +554,17 @@
 				deps.classpath = append(deps.classpath, dep.SdkImplementationJars(ctx, j.sdkVersion())...)
 			case Dependency:
 				deps.classpath = append(deps.classpath, dep.HeaderJars()...)
+				deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs()...)
 			case android.SourceFileProducer:
 				checkProducesJars(ctx, dep)
 				deps.classpath = append(deps.classpath, dep.Srcs()...)
 			default:
 				ctx.ModuleErrorf("depends on non-java module %q", otherName)
 			}
-		case srcsLibTag:
+		case java9LibTag:
 			switch dep := module.(type) {
 			case Dependency:
-				srcs := dep.(SrcDependency).CompiledSrcs()
-				whitelistPathPrefixes := make(map[string]bool)
-				j.genWhitelistPathPrefixes(whitelistPathPrefixes)
-				for _, src := range srcs {
-					if _, ok := src.(android.WritablePath); ok { // generated sources
-						deps.srcs = append(deps.srcs, src)
-					} else { // select source path for documentation based on whitelist path prefixs.
-						for k := range whitelistPathPrefixes {
-							if strings.HasPrefix(src.Rel(), k) {
-								deps.srcs = append(deps.srcs, src)
-								break
-							}
-						}
-					}
-				}
-				deps.srcJars = append(deps.srcJars, dep.(SrcDependency).CompiledSrcJars()...)
+				deps.java9Classpath = append(deps.java9Classpath, dep.HeaderJars()...)
 			default:
 				ctx.ModuleErrorf("depends on non-java module %q", otherName)
 			}
@@ -695,13 +576,40 @@
 			if sm.outputDir == nil && len(sm.outputDeps) == 0 {
 				panic("Missing directory for system module dependency")
 			}
-			deps.systemModules = sm.outputDir
-			deps.systemModulesDeps = sm.outputDeps
+			deps.systemModules = &systemModules{sm.outputDir, sm.outputDeps}
 		}
 	})
 	// do not pass exclude_srcs directly when expanding srcFiles since exclude_srcs
 	// may contain filegroup or genrule.
 	srcFiles := android.PathsForModuleSrcExcludes(ctx, j.properties.Srcs, j.properties.Exclude_srcs)
+
+	filterByPackage := func(srcs []android.Path, filterPackages []string) []android.Path {
+		if filterPackages == nil {
+			return srcs
+		}
+		filtered := []android.Path{}
+		for _, src := range srcs {
+			if src.Ext() != ".java" {
+				// Don't filter-out non-Java (=generated sources) by package names. This is not ideal,
+				// but otherwise metalava emits stub sources having references to the generated AIDL classes
+				// in filtered-out pacages (e.g. com.android.internal.*).
+				// TODO(b/141149570) We need to fix this by introducing default private constructors or
+				// fixing metalava to not emit constructors having references to unknown classes.
+				filtered = append(filtered, src)
+				continue
+			}
+			packageName := strings.ReplaceAll(filepath.Dir(src.Rel()), "/", ".")
+			for _, pkg := range filterPackages {
+				if strings.HasPrefix(packageName, pkg) {
+					filtered = append(filtered, src)
+					break
+				}
+			}
+		}
+		return filtered
+	}
+	srcFiles = filterByPackage(srcFiles, j.properties.Filter_packages)
+
 	flags := j.collectAidlFlags(ctx, deps)
 	srcFiles = j.genSources(ctx, srcFiles, flags)
 
@@ -712,9 +620,6 @@
 	j.srcFiles = srcFiles.FilterOutByExt(".srcjar")
 	j.srcFiles = append(j.srcFiles, deps.srcs...)
 
-	j.docZip = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"docs.zip")
-	j.stubsSrcJar = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"stubs.srcjar")
-
 	if j.properties.Local_sourcepaths == nil && len(j.srcFiles) > 0 {
 		j.properties.Local_sourcepaths = append(j.properties.Local_sourcepaths, ".")
 	}
@@ -765,52 +670,43 @@
 func (j *Javadoc) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	deps := j.collectDeps(ctx)
 
-	var implicits android.Paths
-	implicits = append(implicits, deps.bootClasspath...)
-	implicits = append(implicits, deps.classpath...)
+	j.docZip = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"docs.zip")
 
-	var bootClasspathArgs, classpathArgs, sourcepathArgs string
+	outDir := android.PathForModuleOut(ctx, "out")
+	srcJarDir := android.PathForModuleOut(ctx, "srcjars")
+
+	j.stubsSrcJar = nil
+
+	rule := android.NewRuleBuilder()
+
+	rule.Command().Text("rm -rf").Text(outDir.String())
+	rule.Command().Text("mkdir -p").Text(outDir.String())
+
+	srcJarList := zipSyncCmd(ctx, rule, srcJarDir, j.srcJars)
 
 	javaVersion := getJavaVersion(ctx, String(j.properties.Java_version), sdkContext(j))
-	if len(deps.bootClasspath) > 0 {
-		var systemModules classpath
-		if deps.systemModules != nil {
-			systemModules = append(systemModules, deps.systemModules)
-		}
-		implicits = append(implicits, deps.systemModulesDeps...)
-		bootClasspathArgs = systemModules.FormJavaSystemModulesPath("--system ", ctx.Device())
-		bootClasspathArgs = bootClasspathArgs + " --patch-module java.base=."
-	}
-	if len(deps.classpath.Strings()) > 0 {
-		classpathArgs = "-classpath " + strings.Join(deps.classpath.Strings(), ":")
-	}
 
-	implicits = append(implicits, j.srcJars...)
-	implicits = append(implicits, j.argFiles...)
+	cmd := javadocSystemModulesCmd(ctx, rule, j.srcFiles, outDir, srcJarDir, srcJarList,
+		deps.systemModules, deps.classpath, j.sourcepaths)
 
-	opts := "-source " + javaVersion + " -J-Xmx1024m -XDignore.symbol.file -Xdoclint:none"
+	cmd.FlagWithArg("-source ", javaVersion.String()).
+		Flag("-J-Xmx1024m").
+		Flag("-XDignore.symbol.file").
+		Flag("-Xdoclint:none")
 
-	sourcepathArgs = "-sourcepath " + strings.Join(j.sourcepaths.Strings(), ":")
+	rule.Command().
+		BuiltTool(ctx, "soong_zip").
+		Flag("-write_if_changed").
+		Flag("-d").
+		FlagWithOutput("-o ", j.docZip).
+		FlagWithArg("-C ", outDir.String()).
+		FlagWithArg("-D ", outDir.String())
 
-	ctx.Build(pctx, android.BuildParams{
-		Rule:           javadoc,
-		Description:    "Javadoc",
-		Output:         j.stubsSrcJar,
-		ImplicitOutput: j.docZip,
-		Inputs:         j.srcFiles,
-		Implicits:      implicits,
-		Args: map[string]string{
-			"outDir":            android.PathForModuleOut(ctx, "out").String(),
-			"srcJarDir":         android.PathForModuleOut(ctx, "srcjars").String(),
-			"stubsDir":          android.PathForModuleOut(ctx, "stubsDir").String(),
-			"srcJars":           strings.Join(j.srcJars.Strings(), " "),
-			"opts":              opts,
-			"bootclasspathArgs": bootClasspathArgs,
-			"classpathArgs":     classpathArgs,
-			"sourcepathArgs":    sourcepathArgs,
-			"docZip":            j.docZip.String(),
-		},
-	})
+	rule.Restat()
+
+	zipSyncCleanupCmd(rule, srcJarDir)
+
+	rule.Build(pctx, ctx, "javadoc", "javadoc")
 }
 
 //
@@ -837,6 +733,7 @@
 	apiFilePath android.Path
 }
 
+// droiddoc converts .java source files to documentation using doclava or dokka.
 func DroiddocFactory() android.Module {
 	module := &Droiddoc{}
 
@@ -847,6 +744,7 @@
 	return module
 }
 
+// droiddoc_host converts .java source files to documentation using doclava or dokka.
 func DroiddocHostFactory() android.Module {
 	module := &Droiddoc{}
 
@@ -873,58 +771,18 @@
 	}
 }
 
-func (d *Droiddoc) initBuilderFlags(ctx android.ModuleContext, implicits *android.Paths,
-	deps deps) (droiddocBuilderFlags, error) {
-	var flags droiddocBuilderFlags
-
-	*implicits = append(*implicits, deps.bootClasspath...)
-	*implicits = append(*implicits, deps.classpath...)
-
-	if len(deps.bootClasspath.Strings()) > 0 {
-		// For OpenJDK 8 we can use -bootclasspath to define the core libraries code.
-		flags.bootClasspathArgs = deps.bootClasspath.FormJavaClassPath("-bootclasspath")
-	}
-	flags.classpathArgs = deps.classpath.FormJavaClassPath("-classpath")
-	// Dokka doesn't support bootClasspath, so combine these two classpath vars for Dokka.
-	dokkaClasspath := classpath{}
-	dokkaClasspath = append(dokkaClasspath, deps.bootClasspath...)
-	dokkaClasspath = append(dokkaClasspath, deps.classpath...)
-	flags.dokkaClasspathArgs = dokkaClasspath.FormJavaClassPath("-classpath")
-
-	// TODO(nanzhang): Remove this if- statement once we finish migration for all Doclava
-	// based stubs generation.
-	// In the future, all the docs generation depends on Metalava stubs (droidstubs) srcjar
-	// dir. We need add the srcjar dir to -sourcepath arg, so that Javadoc can figure out
-	// the correct package name base path.
-	if len(d.Javadoc.properties.Local_sourcepaths) > 0 {
-		flags.sourcepathArgs = "-sourcepath " + strings.Join(d.Javadoc.sourcepaths.Strings(), ":")
-	} else {
-		flags.sourcepathArgs = "-sourcepath " + android.PathForModuleOut(ctx, "srcjars").String()
-	}
-
-	return flags, nil
-}
-
-func (d *Droiddoc) collectDoclavaDocsFlags(ctx android.ModuleContext, implicits *android.Paths,
-	jsilver, doclava android.Path) string {
-
-	*implicits = append(*implicits, jsilver)
-	*implicits = append(*implicits, doclava)
-
-	var date string
-	if runtime.GOOS == "darwin" {
-		date = `date -r`
-	} else {
-		date = `date -d`
-	}
-
+func (d *Droiddoc) doclavaDocsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, docletPath classpath) {
 	// Droiddoc always gets "-source 1.8" because it doesn't support 1.9 sources.  For modules with 1.9
 	// sources, droiddoc will get sources produced by metalava which will have already stripped out the
 	// 1.9 language features.
-	args := " -source 1.8 -J-Xmx1600m -J-XX:-OmitStackTraceInFastThrow -XDignore.symbol.file " +
-		"-doclet com.google.doclava.Doclava -docletpath " + jsilver.String() + ":" + doclava.String() + " " +
-		"-hdf page.build " + ctx.Config().BuildId() + "-" + ctx.Config().BuildNumberFromFile() + " " +
-		`-hdf page.now "$$(` + date + ` @$$(cat ` + ctx.Config().Getenv("BUILD_DATETIME_FILE") + `) "+%d %b %Y %k:%M")" `
+	cmd.FlagWithArg("-source ", "1.8").
+		Flag("-J-Xmx1600m").
+		Flag("-J-XX:-OmitStackTraceInFastThrow").
+		Flag("-XDignore.symbol.file").
+		FlagWithArg("-doclet ", "com.google.doclava.Doclava").
+		FlagWithInputList("-docletpath ", docletPath.Paths(), ":").
+		FlagWithArg("-hdf page.build ", ctx.Config().BuildId()+"-"+ctx.Config().BuildNumberFromFile()).
+		FlagWithArg("-hdf page.now ", `"$(date -d @$(cat `+ctx.Config().Getenv("BUILD_DATETIME_FILE")+`) "+%d %b %Y %k:%M")" `)
 
 	if String(d.properties.Custom_template) == "" {
 		// TODO: This is almost always droiddoc-templates-sdk
@@ -933,23 +791,22 @@
 
 	ctx.VisitDirectDepsWithTag(droiddocTemplateTag, func(m android.Module) {
 		if t, ok := m.(*ExportedDroiddocDir); ok {
-			*implicits = append(*implicits, t.deps...)
-			args = args + " -templatedir " + t.dir.String()
+			cmd.FlagWithArg("-templatedir ", t.dir.String()).Implicits(t.deps)
 		} else {
-			ctx.PropertyErrorf("custom_template", "module %q is not a droiddoc_template", ctx.OtherModuleName(m))
+			ctx.PropertyErrorf("custom_template", "module %q is not a droiddoc_exported_dir", ctx.OtherModuleName(m))
 		}
 	})
 
 	if len(d.properties.Html_dirs) > 0 {
-		htmlDir := d.properties.Html_dirs[0]
-		*implicits = append(*implicits, android.PathsForModuleSrc(ctx, []string{filepath.Join(d.properties.Html_dirs[0], "**/*")})...)
-		args = args + " -htmldir " + htmlDir
+		htmlDir := android.PathForModuleSrc(ctx, d.properties.Html_dirs[0])
+		cmd.FlagWithArg("-htmldir ", htmlDir.String()).
+			Implicits(android.PathsForModuleSrc(ctx, []string{filepath.Join(d.properties.Html_dirs[0], "**/*")}))
 	}
 
 	if len(d.properties.Html_dirs) > 1 {
-		htmlDir2 := d.properties.Html_dirs[1]
-		*implicits = append(*implicits, android.PathsForModuleSrc(ctx, []string{filepath.Join(htmlDir2, "**/*")})...)
-		args = args + " -htmldir2 " + htmlDir2
+		htmlDir2 := android.PathForModuleSrc(ctx, d.properties.Html_dirs[1])
+		cmd.FlagWithArg("-htmldir2 ", htmlDir2.String()).
+			Implicits(android.PathsForModuleSrc(ctx, []string{filepath.Join(d.properties.Html_dirs[1], "**/*")}))
 	}
 
 	if len(d.properties.Html_dirs) > 2 {
@@ -957,275 +814,368 @@
 	}
 
 	knownTags := android.PathsForModuleSrc(ctx, d.properties.Knowntags)
-	*implicits = append(*implicits, knownTags...)
+	cmd.FlagForEachInput("-knowntags ", knownTags)
 
-	for _, kt := range knownTags {
-		args = args + " -knowntags " + kt.String()
-	}
-
-	for _, hdf := range d.properties.Hdf {
-		args = args + " -hdf " + hdf
-	}
+	cmd.FlagForEachArg("-hdf ", d.properties.Hdf)
 
 	if String(d.properties.Proofread_file) != "" {
 		proofreadFile := android.PathForModuleOut(ctx, String(d.properties.Proofread_file))
-		args = args + " -proofread " + proofreadFile.String()
+		cmd.FlagWithOutput("-proofread ", proofreadFile)
 	}
 
 	if String(d.properties.Todo_file) != "" {
 		// tricky part:
 		// we should not compute full path for todo_file through PathForModuleOut().
 		// the non-standard doclet will get the full path relative to "-o".
-		args = args + " -todo " + String(d.properties.Todo_file)
+		cmd.FlagWithArg("-todo ", String(d.properties.Todo_file)).
+			ImplicitOutput(android.PathForModuleOut(ctx, String(d.properties.Todo_file)))
 	}
 
 	if String(d.properties.Resourcesdir) != "" {
 		// TODO: should we add files under resourcesDir to the implicits? It seems that
 		// resourcesDir is one sub dir of htmlDir
 		resourcesDir := android.PathForModuleSrc(ctx, String(d.properties.Resourcesdir))
-		args = args + " -resourcesdir " + resourcesDir.String()
+		cmd.FlagWithArg("-resourcesdir ", resourcesDir.String())
 	}
 
 	if String(d.properties.Resourcesoutdir) != "" {
 		// TODO: it seems -resourceoutdir reference/android/images/ didn't get generated anywhere.
-		args = args + " -resourcesoutdir " + String(d.properties.Resourcesoutdir)
+		cmd.FlagWithArg("-resourcesoutdir ", String(d.properties.Resourcesoutdir))
 	}
-	return args
 }
 
-func (d *Droiddoc) collectStubsFlags(ctx android.ModuleContext,
-	implicitOutputs *android.WritablePaths) string {
-	var doclavaFlags string
-	if apiCheckEnabled(d.properties.Check_api.Current, "current") ||
-		apiCheckEnabled(d.properties.Check_api.Last_released, "last_released") ||
+func (d *Droiddoc) stubsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsDir android.WritablePath) {
+	if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") ||
+		apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") ||
 		String(d.properties.Api_filename) != "" {
+
 		d.apiFile = android.PathForModuleOut(ctx, ctx.ModuleName()+"_api.txt")
-		doclavaFlags += " -api " + d.apiFile.String()
-		*implicitOutputs = append(*implicitOutputs, d.apiFile)
+		cmd.FlagWithOutput("-api ", d.apiFile)
 		d.apiFilePath = d.apiFile
 	}
 
-	if apiCheckEnabled(d.properties.Check_api.Current, "current") ||
-		apiCheckEnabled(d.properties.Check_api.Last_released, "last_released") ||
+	if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") ||
+		apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") ||
 		String(d.properties.Removed_api_filename) != "" {
 		d.removedApiFile = android.PathForModuleOut(ctx, ctx.ModuleName()+"_removed.txt")
-		doclavaFlags += " -removedApi " + d.removedApiFile.String()
-		*implicitOutputs = append(*implicitOutputs, d.removedApiFile)
+		cmd.FlagWithOutput("-removedApi ", d.removedApiFile)
 	}
 
 	if String(d.properties.Private_api_filename) != "" {
 		d.privateApiFile = android.PathForModuleOut(ctx, String(d.properties.Private_api_filename))
-		doclavaFlags += " -privateApi " + d.privateApiFile.String()
-		*implicitOutputs = append(*implicitOutputs, d.privateApiFile)
+		cmd.FlagWithOutput("-privateApi ", d.privateApiFile)
 	}
 
 	if String(d.properties.Dex_api_filename) != "" {
 		d.dexApiFile = android.PathForModuleOut(ctx, String(d.properties.Dex_api_filename))
-		doclavaFlags += " -dexApi " + d.dexApiFile.String()
-		*implicitOutputs = append(*implicitOutputs, d.dexApiFile)
+		cmd.FlagWithOutput("-dexApi ", d.dexApiFile)
 	}
 
 	if String(d.properties.Private_dex_api_filename) != "" {
 		d.privateDexApiFile = android.PathForModuleOut(ctx, String(d.properties.Private_dex_api_filename))
-		doclavaFlags += " -privateDexApi " + d.privateDexApiFile.String()
-		*implicitOutputs = append(*implicitOutputs, d.privateDexApiFile)
+		cmd.FlagWithOutput("-privateDexApi ", d.privateDexApiFile)
 	}
 
 	if String(d.properties.Removed_dex_api_filename) != "" {
 		d.removedDexApiFile = android.PathForModuleOut(ctx, String(d.properties.Removed_dex_api_filename))
-		doclavaFlags += " -removedDexApi " + d.removedDexApiFile.String()
-		*implicitOutputs = append(*implicitOutputs, d.removedDexApiFile)
+		cmd.FlagWithOutput("-removedDexApi ", d.removedDexApiFile)
 	}
 
 	if String(d.properties.Exact_api_filename) != "" {
 		d.exactApiFile = android.PathForModuleOut(ctx, String(d.properties.Exact_api_filename))
-		doclavaFlags += " -exactApi " + d.exactApiFile.String()
-		*implicitOutputs = append(*implicitOutputs, d.exactApiFile)
+		cmd.FlagWithOutput("-exactApi ", d.exactApiFile)
 	}
 
 	if String(d.properties.Dex_mapping_filename) != "" {
 		d.apiMappingFile = android.PathForModuleOut(ctx, String(d.properties.Dex_mapping_filename))
-		doclavaFlags += " -apiMapping " + d.apiMappingFile.String()
-		*implicitOutputs = append(*implicitOutputs, d.apiMappingFile)
+		cmd.FlagWithOutput("-apiMapping ", d.apiMappingFile)
 	}
 
 	if String(d.properties.Proguard_filename) != "" {
 		d.proguardFile = android.PathForModuleOut(ctx, String(d.properties.Proguard_filename))
-		doclavaFlags += " -proguard " + d.proguardFile.String()
-		*implicitOutputs = append(*implicitOutputs, d.proguardFile)
+		cmd.FlagWithOutput("-proguard ", d.proguardFile)
 	}
 
 	if BoolDefault(d.properties.Create_stubs, true) {
-		doclavaFlags += " -stubs " + android.PathForModuleOut(ctx, "stubsDir").String()
+		cmd.FlagWithArg("-stubs ", stubsDir.String())
 	}
 
 	if Bool(d.properties.Write_sdk_values) {
-		doclavaFlags += " -sdkvalues " + android.PathForModuleOut(ctx, "out").String()
+		cmd.FlagWithArg("-sdkvalues ", android.PathForModuleOut(ctx, "out").String())
 	}
-
-	return doclavaFlags
 }
 
-func (d *Droiddoc) getPostDoclavaCmds(ctx android.ModuleContext, implicits *android.Paths) string {
-	var cmds string
+func (d *Droiddoc) postDoclavaCmds(ctx android.ModuleContext, rule *android.RuleBuilder) {
 	if String(d.properties.Static_doc_index_redirect) != "" {
-		static_doc_index_redirect := ctx.ExpandSource(String(d.properties.Static_doc_index_redirect),
-			"static_doc_index_redirect")
-		*implicits = append(*implicits, static_doc_index_redirect)
-		cmds = cmds + " && cp " + static_doc_index_redirect.String() + " " +
-			android.PathForModuleOut(ctx, "out", "index.html").String()
+		staticDocIndexRedirect := android.PathForModuleSrc(ctx, String(d.properties.Static_doc_index_redirect))
+		rule.Command().Text("cp").
+			Input(staticDocIndexRedirect).
+			Output(android.PathForModuleOut(ctx, "out", "index.html"))
 	}
 
 	if String(d.properties.Static_doc_properties) != "" {
-		static_doc_properties := ctx.ExpandSource(String(d.properties.Static_doc_properties),
-			"static_doc_properties")
-		*implicits = append(*implicits, static_doc_properties)
-		cmds = cmds + " && cp " + static_doc_properties.String() + " " +
-			android.PathForModuleOut(ctx, "out", "source.properties").String()
+		staticDocProperties := android.PathForModuleSrc(ctx, String(d.properties.Static_doc_properties))
+		rule.Command().Text("cp").
+			Input(staticDocProperties).
+			Output(android.PathForModuleOut(ctx, "out", "source.properties"))
 	}
-	return cmds
 }
 
-func (d *Droiddoc) transformDoclava(ctx android.ModuleContext, implicits android.Paths,
-	implicitOutputs android.WritablePaths,
-	bootclasspathArgs, classpathArgs, sourcepathArgs, opts, postDoclavaCmds string) {
-	ctx.Build(pctx, android.BuildParams{
-		Rule:            javadoc,
-		Description:     "Doclava",
-		Output:          d.Javadoc.stubsSrcJar,
-		Inputs:          d.Javadoc.srcFiles,
-		Implicits:       implicits,
-		ImplicitOutputs: implicitOutputs,
-		Args: map[string]string{
-			"outDir":            android.PathForModuleOut(ctx, "out").String(),
-			"srcJarDir":         android.PathForModuleOut(ctx, "srcjars").String(),
-			"stubsDir":          android.PathForModuleOut(ctx, "stubsDir").String(),
-			"srcJars":           strings.Join(d.Javadoc.srcJars.Strings(), " "),
-			"opts":              opts,
-			"bootclasspathArgs": bootclasspathArgs,
-			"classpathArgs":     classpathArgs,
-			"sourcepathArgs":    sourcepathArgs,
-			"docZip":            d.Javadoc.docZip.String(),
-			"postDoclavaCmds":   postDoclavaCmds,
-		},
-	})
+func javadocCmd(ctx android.ModuleContext, rule *android.RuleBuilder, srcs android.Paths,
+	outDir, srcJarDir, srcJarList android.Path, sourcepaths android.Paths) *android.RuleBuilderCommand {
+
+	cmd := rule.Command().
+		BuiltTool(ctx, "soong_javac_wrapper").Tool(config.JavadocCmd(ctx)).
+		Flag(config.JavacVmFlags).
+		FlagWithArg("-encoding ", "UTF-8").
+		FlagWithRspFileInputList("@", srcs).
+		FlagWithInput("@", srcJarList)
+
+	// TODO(ccross): Remove this if- statement once we finish migration for all Doclava
+	// based stubs generation.
+	// In the future, all the docs generation depends on Metalava stubs (droidstubs) srcjar
+	// dir. We need add the srcjar dir to -sourcepath arg, so that Javadoc can figure out
+	// the correct package name base path.
+	if len(sourcepaths) > 0 {
+		cmd.FlagWithList("-sourcepath ", sourcepaths.Strings(), ":")
+	} else {
+		cmd.FlagWithArg("-sourcepath ", srcJarDir.String())
+	}
+
+	cmd.FlagWithArg("-d ", outDir.String()).
+		Flag("-quiet")
+
+	return cmd
 }
 
-func (d *Droiddoc) transformCheckApi(ctx android.ModuleContext, apiFile, removedApiFile android.Path,
-	checkApiClasspath classpath, msg, opts string, output android.WritablePath) {
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        apiCheck,
-		Description: "Doclava Check API",
-		Output:      output,
-		Inputs:      nil,
-		Implicits: append(android.Paths{apiFile, removedApiFile, d.apiFile, d.removedApiFile},
-			checkApiClasspath...),
-		Args: map[string]string{
-			"msg":                   msg,
-			"classpath":             checkApiClasspath.FormJavaClassPath(""),
-			"opts":                  opts,
-			"apiFile":               apiFile.String(),
-			"apiFileToCheck":        d.apiFile.String(),
-			"removedApiFile":        removedApiFile.String(),
-			"removedApiFileToCheck": d.removedApiFile.String(),
-		},
-	})
+func javadocSystemModulesCmd(ctx android.ModuleContext, rule *android.RuleBuilder, srcs android.Paths,
+	outDir, srcJarDir, srcJarList android.Path, systemModules *systemModules,
+	classpath classpath, sourcepaths android.Paths) *android.RuleBuilderCommand {
+
+	cmd := javadocCmd(ctx, rule, srcs, outDir, srcJarDir, srcJarList, sourcepaths)
+
+	flag, deps := systemModules.FormJavaSystemModulesPath(ctx.Device())
+	cmd.Flag(flag).Implicits(deps)
+
+	cmd.FlagWithArg("--patch-module ", "java.base=.")
+
+	if len(classpath) > 0 {
+		cmd.FlagWithInputList("-classpath ", classpath.Paths(), ":")
+	}
+
+	return cmd
 }
 
-func (d *Droiddoc) transformDokka(ctx android.ModuleContext, implicits android.Paths,
-	classpathArgs, opts string) {
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        dokka,
-		Description: "Dokka",
-		Output:      d.Javadoc.stubsSrcJar,
-		Inputs:      d.Javadoc.srcFiles,
-		Implicits:   implicits,
-		Args: map[string]string{
-			"outDir":        android.PathForModuleOut(ctx, "dokka-out").String(),
-			"srcJarDir":     android.PathForModuleOut(ctx, "dokka-srcjars").String(),
-			"stubsDir":      android.PathForModuleOut(ctx, "dokka-stubsDir").String(),
-			"srcJars":       strings.Join(d.Javadoc.srcJars.Strings(), " "),
-			"classpathArgs": classpathArgs,
-			"opts":          opts,
-			"docZip":        d.Javadoc.docZip.String(),
-		},
-	})
+func javadocBootclasspathCmd(ctx android.ModuleContext, rule *android.RuleBuilder, srcs android.Paths,
+	outDir, srcJarDir, srcJarList android.Path, bootclasspath, classpath classpath,
+	sourcepaths android.Paths) *android.RuleBuilderCommand {
+
+	cmd := javadocCmd(ctx, rule, srcs, outDir, srcJarDir, srcJarList, sourcepaths)
+
+	if len(bootclasspath) == 0 && ctx.Device() {
+		// explicitly specify -bootclasspath "" if the bootclasspath is empty to
+		// ensure java does not fall back to the default bootclasspath.
+		cmd.FlagWithArg("-bootclasspath ", `""`)
+	} else if len(bootclasspath) > 0 {
+		cmd.FlagWithInputList("-bootclasspath ", bootclasspath.Paths(), ":")
+	}
+
+	if len(classpath) > 0 {
+		cmd.FlagWithInputList("-classpath ", classpath.Paths(), ":")
+	}
+
+	return cmd
+}
+
+func dokkaCmd(ctx android.ModuleContext, rule *android.RuleBuilder,
+	outDir, srcJarDir android.Path, bootclasspath, classpath classpath) *android.RuleBuilderCommand {
+
+	// Dokka doesn't support bootClasspath, so combine these two classpath vars for Dokka.
+	dokkaClasspath := append(bootclasspath.Paths(), classpath.Paths()...)
+
+	return rule.Command().
+		BuiltTool(ctx, "dokka").
+		Flag(config.JavacVmFlags).
+		Flag(srcJarDir.String()).
+		FlagWithInputList("-classpath ", dokkaClasspath, ":").
+		FlagWithArg("-format ", "dac").
+		FlagWithArg("-dacRoot ", "/reference/kotlin").
+		FlagWithArg("-output ", outDir.String())
 }
 
 func (d *Droiddoc) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	deps := d.Javadoc.collectDeps(ctx)
 
+	d.Javadoc.docZip = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"docs.zip")
+	d.Javadoc.stubsSrcJar = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"stubs.srcjar")
+
 	jsilver := android.PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "framework", "jsilver.jar")
 	doclava := android.PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "framework", "doclava.jar")
 	java8Home := ctx.Config().Getenv("ANDROID_JAVA8_HOME")
 	checkApiClasspath := classpath{jsilver, doclava, android.PathForSource(ctx, java8Home, "lib/tools.jar")}
 
-	var implicits android.Paths
-	implicits = append(implicits, d.Javadoc.srcJars...)
-	implicits = append(implicits, d.Javadoc.argFiles...)
+	outDir := android.PathForModuleOut(ctx, "out")
+	srcJarDir := android.PathForModuleOut(ctx, "srcjars")
+	stubsDir := android.PathForModuleOut(ctx, "stubsDir")
 
-	var implicitOutputs android.WritablePaths
-	implicitOutputs = append(implicitOutputs, d.Javadoc.docZip)
-	for _, o := range d.Javadoc.properties.Out {
-		implicitOutputs = append(implicitOutputs, android.PathForModuleGen(ctx, o))
-	}
+	rule := android.NewRuleBuilder()
 
-	flags, err := d.initBuilderFlags(ctx, &implicits, deps)
-	if err != nil {
-		return
-	}
+	rule.Command().Text("rm -rf").Text(outDir.String()).Text(stubsDir.String())
+	rule.Command().Text("mkdir -p").Text(outDir.String()).Text(stubsDir.String())
 
-	flags.doclavaStubsFlags = d.collectStubsFlags(ctx, &implicitOutputs)
+	srcJarList := zipSyncCmd(ctx, rule, srcJarDir, d.Javadoc.srcJars)
+
+	var cmd *android.RuleBuilderCommand
 	if Bool(d.properties.Dokka_enabled) {
-		d.transformDokka(ctx, implicits, flags.classpathArgs, d.Javadoc.args)
+		cmd = dokkaCmd(ctx, rule, outDir, srcJarDir, deps.bootClasspath, deps.classpath)
 	} else {
-		flags.doclavaDocsFlags = d.collectDoclavaDocsFlags(ctx, &implicits, jsilver, doclava)
-		flags.postDoclavaCmds = d.getPostDoclavaCmds(ctx, &implicits)
-		d.transformDoclava(ctx, implicits, implicitOutputs, flags.bootClasspathArgs, flags.classpathArgs,
-			flags.sourcepathArgs, flags.doclavaDocsFlags+flags.doclavaStubsFlags+" "+d.Javadoc.args,
-			flags.postDoclavaCmds)
+		cmd = javadocBootclasspathCmd(ctx, rule, d.Javadoc.srcFiles, outDir, srcJarDir, srcJarList,
+			deps.bootClasspath, deps.classpath, d.Javadoc.sourcepaths)
 	}
 
-	if apiCheckEnabled(d.properties.Check_api.Current, "current") &&
+	d.stubsFlags(ctx, cmd, stubsDir)
+
+	cmd.Flag(d.Javadoc.args).Implicits(d.Javadoc.argFiles)
+
+	var desc string
+	if Bool(d.properties.Dokka_enabled) {
+		desc = "dokka"
+	} else {
+		d.doclavaDocsFlags(ctx, cmd, classpath{jsilver, doclava})
+
+		for _, o := range d.Javadoc.properties.Out {
+			cmd.ImplicitOutput(android.PathForModuleGen(ctx, o))
+		}
+
+		d.postDoclavaCmds(ctx, rule)
+		desc = "doclava"
+	}
+
+	rule.Command().
+		BuiltTool(ctx, "soong_zip").
+		Flag("-write_if_changed").
+		Flag("-d").
+		FlagWithOutput("-o ", d.docZip).
+		FlagWithArg("-C ", outDir.String()).
+		FlagWithArg("-D ", outDir.String())
+
+	rule.Command().
+		BuiltTool(ctx, "soong_zip").
+		Flag("-write_if_changed").
+		Flag("-jar").
+		FlagWithOutput("-o ", d.stubsSrcJar).
+		FlagWithArg("-C ", stubsDir.String()).
+		FlagWithArg("-D ", stubsDir.String())
+
+	rule.Restat()
+
+	zipSyncCleanupCmd(rule, srcJarDir)
+
+	rule.Build(pctx, ctx, "javadoc", desc)
+
+	if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") &&
 		!ctx.Config().IsPdkBuild() {
-		apiFile := ctx.ExpandSource(String(d.properties.Check_api.Current.Api_file),
-			"check_api.current.api_file")
-		removedApiFile := ctx.ExpandSource(String(d.properties.Check_api.Current.Removed_api_file),
-			"check_api.current_removed_api_file")
+
+		apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Api_file))
+		removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Removed_api_file))
 
 		d.checkCurrentApiTimestamp = android.PathForModuleOut(ctx, "check_current_api.timestamp")
-		d.transformCheckApi(ctx, apiFile, removedApiFile, checkApiClasspath,
-			fmt.Sprintf(`\n******************************\n`+
-				`You have tried to change the API from what has been previously approved.\n\n`+
-				`To make these errors go away, you have two choices:\n`+
-				`   1. You can add '@hide' javadoc comments to the methods, etc. listed in the\n`+
-				`      errors above.\n\n`+
-				`   2. You can update current.txt by executing the following command:\n`+
-				`         make %s-update-current-api\n\n`+
-				`      To submit the revised current.txt to the main Android repository,\n`+
-				`      you will need approval.\n`+
-				`******************************\n`, ctx.ModuleName()), String(d.properties.Check_api.Current.Args),
-			d.checkCurrentApiTimestamp)
+
+		rule := android.NewRuleBuilder()
+
+		rule.Command().Text("( true")
+
+		rule.Command().
+			BuiltTool(ctx, "apicheck").
+			Flag("-JXmx1024m").
+			FlagWithInputList("-Jclasspath\\ ", checkApiClasspath.Paths(), ":").
+			OptionalFlag(d.properties.Check_api.Current.Args).
+			Input(apiFile).
+			Input(d.apiFile).
+			Input(removedApiFile).
+			Input(d.removedApiFile)
+
+		msg := fmt.Sprintf(`\n******************************\n`+
+			`You have tried to change the API from what has been previously approved.\n\n`+
+			`To make these errors go away, you have two choices:\n`+
+			`   1. You can add '@hide' javadoc comments to the methods, etc. listed in the\n`+
+			`      errors above.\n\n`+
+			`   2. You can update current.txt by executing the following command:\n`+
+			`         make %s-update-current-api\n\n`+
+			`      To submit the revised current.txt to the main Android repository,\n`+
+			`      you will need approval.\n`+
+			`******************************\n`, ctx.ModuleName())
+
+		rule.Command().
+			Text("touch").Output(d.checkCurrentApiTimestamp).
+			Text(") || (").
+			Text("echo").Flag("-e").Flag(`"` + msg + `"`).
+			Text("; exit 38").
+			Text(")")
+
+		rule.Build(pctx, ctx, "doclavaCurrentApiCheck", "check current API")
 
 		d.updateCurrentApiTimestamp = android.PathForModuleOut(ctx, "update_current_api.timestamp")
-		transformUpdateApi(ctx, apiFile, removedApiFile, d.apiFile, d.removedApiFile,
-			d.updateCurrentApiTimestamp)
+
+		// update API rule
+		rule = android.NewRuleBuilder()
+
+		rule.Command().Text("( true")
+
+		rule.Command().
+			Text("cp").Flag("-f").
+			Input(d.apiFile).Flag(apiFile.String())
+
+		rule.Command().
+			Text("cp").Flag("-f").
+			Input(d.removedApiFile).Flag(removedApiFile.String())
+
+		msg = "failed to update public API"
+
+		rule.Command().
+			Text("touch").Output(d.updateCurrentApiTimestamp).
+			Text(") || (").
+			Text("echo").Flag("-e").Flag(`"` + msg + `"`).
+			Text("; exit 38").
+			Text(")")
+
+		rule.Build(pctx, ctx, "doclavaCurrentApiUpdate", "update current API")
 	}
 
-	if apiCheckEnabled(d.properties.Check_api.Last_released, "last_released") &&
+	if apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") &&
 		!ctx.Config().IsPdkBuild() {
-		apiFile := ctx.ExpandSource(String(d.properties.Check_api.Last_released.Api_file),
-			"check_api.last_released.api_file")
-		removedApiFile := ctx.ExpandSource(String(d.properties.Check_api.Last_released.Removed_api_file),
-			"check_api.last_released.removed_api_file")
+
+		apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Api_file))
+		removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Removed_api_file))
 
 		d.checkLastReleasedApiTimestamp = android.PathForModuleOut(ctx, "check_last_released_api.timestamp")
-		d.transformCheckApi(ctx, apiFile, removedApiFile, checkApiClasspath,
-			`\n******************************\n`+
-				`You have tried to change the API from what has been previously released in\n`+
-				`an SDK.  Please fix the errors listed above.\n`+
-				`******************************\n`, String(d.properties.Check_api.Last_released.Args),
-			d.checkLastReleasedApiTimestamp)
+
+		rule := android.NewRuleBuilder()
+
+		rule.Command().
+			Text("(").
+			BuiltTool(ctx, "apicheck").
+			Flag("-JXmx1024m").
+			FlagWithInputList("-Jclasspath\\ ", checkApiClasspath.Paths(), ":").
+			OptionalFlag(d.properties.Check_api.Last_released.Args).
+			Input(apiFile).
+			Input(d.apiFile).
+			Input(removedApiFile).
+			Input(d.removedApiFile)
+
+		msg := `\n******************************\n` +
+			`You have tried to change the API from what has been previously released in\n` +
+			`an SDK.  Please fix the errors listed above.\n` +
+			`******************************\n`
+
+		rule.Command().
+			Text("touch").Output(d.checkLastReleasedApiTimestamp).
+			Text(") || (").
+			Text("echo").Flag("-e").Flag(`"` + msg + `"`).
+			Text("; exit 38").
+			Text(")")
+
+		rule.Build(pctx, ctx, "doclavaLastApiCheck", "check last API")
 	}
 }
 
@@ -1234,6 +1184,7 @@
 //
 type Droidstubs struct {
 	Javadoc
+	android.SdkBase
 
 	properties              DroidstubsProperties
 	apiFile                 android.WritablePath
@@ -1252,6 +1203,8 @@
 	checkCurrentApiTimestamp      android.WritablePath
 	updateCurrentApiTimestamp     android.WritablePath
 	checkLastReleasedApiTimestamp android.WritablePath
+	apiLintTimestamp              android.WritablePath
+	apiLintReport                 android.WritablePath
 
 	checkNullabilityWarningsTimestamp android.WritablePath
 
@@ -1262,8 +1215,14 @@
 
 	jdiffDocZip      android.WritablePath
 	jdiffStubsSrcJar android.WritablePath
+
+	metadataZip android.WritablePath
+	metadataDir android.WritablePath
 }
 
+// droidstubs passes sources files through Metalava to generate stub .java files that only contain the API to be
+// documented, filtering out hidden classes and methods.  The resulting .java files are intended to be passed to
+// a droiddoc module to generate documentation.
 func DroidstubsFactory() android.Module {
 	module := &Droidstubs{}
 
@@ -1271,9 +1230,14 @@
 		&module.Javadoc.properties)
 
 	InitDroiddocModule(module, android.HostAndDeviceSupported)
+	android.InitSdkAwareModule(module)
 	return module
 }
 
+// droidstubs_host passes sources files through Metalava to generate stub .java files that only contain the API
+// to be documented, filtering out hidden classes and methods.  The resulting .java files are intended to be
+// passed to a droiddoc_host module to generate documentation.  Use a droidstubs_host instead of a droidstubs
+// module when symbols needed by the source files are provided by java_library_host modules.
 func DroidstubsHostFactory() android.Module {
 	module := &Droidstubs{}
 
@@ -1314,194 +1278,153 @@
 	}
 }
 
-func (d *Droidstubs) initBuilderFlags(ctx android.ModuleContext, implicits *android.Paths,
-	deps deps) (droiddocBuilderFlags, error) {
-	var flags droiddocBuilderFlags
-
-	*implicits = append(*implicits, deps.bootClasspath...)
-	*implicits = append(*implicits, deps.classpath...)
-
-	// continue to use -bootclasspath even if Metalava under -source 1.9 is enabled
-	// since it doesn't support system modules yet.
-	if len(deps.bootClasspath.Strings()) > 0 {
-		// For OpenJDK 8 we can use -bootclasspath to define the core libraries code.
-		flags.bootClasspathArgs = deps.bootClasspath.FormJavaClassPath("-bootclasspath")
-	}
-	flags.classpathArgs = deps.classpath.FormJavaClassPath("-classpath")
-
-	flags.sourcepathArgs = "-sourcepath \"" + strings.Join(d.Javadoc.sourcepaths.Strings(), ":") + "\""
-	return flags, nil
-}
-
-func (d *Droidstubs) collectStubsFlags(ctx android.ModuleContext,
-	implicitOutputs *android.WritablePaths) string {
-	var metalavaFlags string
-	if apiCheckEnabled(d.properties.Check_api.Current, "current") ||
-		apiCheckEnabled(d.properties.Check_api.Last_released, "last_released") ||
+func (d *Droidstubs) stubsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsDir android.WritablePath) {
+	if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") ||
+		apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") ||
 		String(d.properties.Api_filename) != "" {
 		d.apiFile = android.PathForModuleOut(ctx, ctx.ModuleName()+"_api.txt")
-		metalavaFlags = metalavaFlags + " --api " + d.apiFile.String()
-		*implicitOutputs = append(*implicitOutputs, d.apiFile)
+		cmd.FlagWithOutput("--api ", d.apiFile)
 		d.apiFilePath = d.apiFile
 	}
 
-	if apiCheckEnabled(d.properties.Check_api.Current, "current") ||
-		apiCheckEnabled(d.properties.Check_api.Last_released, "last_released") ||
+	if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") ||
+		apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") ||
 		String(d.properties.Removed_api_filename) != "" {
 		d.removedApiFile = android.PathForModuleOut(ctx, ctx.ModuleName()+"_removed.txt")
-		metalavaFlags = metalavaFlags + " --removed-api " + d.removedApiFile.String()
-		*implicitOutputs = append(*implicitOutputs, d.removedApiFile)
+		cmd.FlagWithOutput("--removed-api ", d.removedApiFile)
 	}
 
 	if String(d.properties.Private_api_filename) != "" {
 		d.privateApiFile = android.PathForModuleOut(ctx, String(d.properties.Private_api_filename))
-		metalavaFlags = metalavaFlags + " --private-api " + d.privateApiFile.String()
-		*implicitOutputs = append(*implicitOutputs, d.privateApiFile)
+		cmd.FlagWithOutput("--private-api ", d.privateApiFile)
 	}
 
 	if String(d.properties.Dex_api_filename) != "" {
 		d.dexApiFile = android.PathForModuleOut(ctx, String(d.properties.Dex_api_filename))
-		metalavaFlags += " --dex-api " + d.dexApiFile.String()
-		*implicitOutputs = append(*implicitOutputs, d.dexApiFile)
+		cmd.FlagWithOutput("--dex-api ", d.dexApiFile)
 	}
 
 	if String(d.properties.Private_dex_api_filename) != "" {
 		d.privateDexApiFile = android.PathForModuleOut(ctx, String(d.properties.Private_dex_api_filename))
-		metalavaFlags = metalavaFlags + " --private-dex-api " + d.privateDexApiFile.String()
-		*implicitOutputs = append(*implicitOutputs, d.privateDexApiFile)
+		cmd.FlagWithOutput("--private-dex-api ", d.privateDexApiFile)
 	}
 
 	if String(d.properties.Removed_dex_api_filename) != "" {
 		d.removedDexApiFile = android.PathForModuleOut(ctx, String(d.properties.Removed_dex_api_filename))
-		metalavaFlags = metalavaFlags + " --removed-dex-api " + d.removedDexApiFile.String()
-		*implicitOutputs = append(*implicitOutputs, d.removedDexApiFile)
+		cmd.FlagWithOutput("--removed-dex-api ", d.removedDexApiFile)
 	}
 
 	if String(d.properties.Exact_api_filename) != "" {
 		d.exactApiFile = android.PathForModuleOut(ctx, String(d.properties.Exact_api_filename))
-		metalavaFlags = metalavaFlags + " --exact-api " + d.exactApiFile.String()
-		*implicitOutputs = append(*implicitOutputs, d.exactApiFile)
+		cmd.FlagWithOutput("--exact-api ", d.exactApiFile)
 	}
 
 	if String(d.properties.Dex_mapping_filename) != "" {
 		d.apiMappingFile = android.PathForModuleOut(ctx, String(d.properties.Dex_mapping_filename))
-		metalavaFlags = metalavaFlags + " --dex-api-mapping " + d.apiMappingFile.String()
-		*implicitOutputs = append(*implicitOutputs, d.apiMappingFile)
+		cmd.FlagWithOutput("--dex-api-mapping ", d.apiMappingFile)
 	}
 
 	if String(d.properties.Proguard_filename) != "" {
 		d.proguardFile = android.PathForModuleOut(ctx, String(d.properties.Proguard_filename))
-		metalavaFlags += " --proguard " + d.proguardFile.String()
-		*implicitOutputs = append(*implicitOutputs, d.proguardFile)
+		cmd.FlagWithOutput("--proguard ", d.proguardFile)
 	}
 
 	if Bool(d.properties.Write_sdk_values) {
-		metalavaFlags = metalavaFlags + " --sdk-values " + android.PathForModuleOut(ctx, "out").String()
+		d.metadataDir = android.PathForModuleOut(ctx, "metadata")
+		cmd.FlagWithArg("--sdk-values ", d.metadataDir.String())
 	}
 
 	if Bool(d.properties.Create_doc_stubs) {
-		metalavaFlags += " --doc-stubs " + android.PathForModuleOut(ctx, "stubsDir").String()
+		cmd.FlagWithArg("--doc-stubs ", stubsDir.String())
 	} else {
-		metalavaFlags += " --stubs " + android.PathForModuleOut(ctx, "stubsDir").String()
+		cmd.FlagWithArg("--stubs ", stubsDir.String())
 	}
-	return metalavaFlags
 }
 
-func (d *Droidstubs) collectAnnotationsFlags(ctx android.ModuleContext,
-	implicits *android.Paths, implicitOutputs *android.WritablePaths) (string, string) {
-	var flags, mergeAnnoDirFlags string
+func (d *Droidstubs) annotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
 	if Bool(d.properties.Annotations_enabled) {
-		flags += " --include-annotations"
+		cmd.Flag("--include-annotations")
+
 		validatingNullability :=
 			strings.Contains(d.Javadoc.args, "--validate-nullability-from-merged-stubs") ||
 				String(d.properties.Validate_nullability_from_list) != ""
+
 		migratingNullability := String(d.properties.Previous_api) != ""
-		if !(migratingNullability || validatingNullability) {
-			ctx.PropertyErrorf("previous_api",
-				"has to be non-empty if annotations was enabled (unless validating nullability)")
-		}
 		if migratingNullability {
 			previousApi := android.PathForModuleSrc(ctx, String(d.properties.Previous_api))
-			*implicits = append(*implicits, previousApi)
-			flags += " --migrate-nullness " + previousApi.String()
+			cmd.FlagWithInput("--migrate-nullness ", previousApi)
 		}
+
 		if s := String(d.properties.Validate_nullability_from_list); s != "" {
-			flags += " --validate-nullability-from-list " + android.PathForModuleSrc(ctx, s).String()
+			cmd.FlagWithInput("--validate-nullability-from-list ", android.PathForModuleSrc(ctx, s))
 		}
+
 		if validatingNullability {
 			d.nullabilityWarningsFile = android.PathForModuleOut(ctx, ctx.ModuleName()+"_nullability_warnings.txt")
-			*implicitOutputs = append(*implicitOutputs, d.nullabilityWarningsFile)
-			flags += " --nullability-warnings-txt " + d.nullabilityWarningsFile.String()
+			cmd.FlagWithOutput("--nullability-warnings-txt ", d.nullabilityWarningsFile)
 		}
 
 		d.annotationsZip = android.PathForModuleOut(ctx, ctx.ModuleName()+"_annotations.zip")
-		*implicitOutputs = append(*implicitOutputs, d.annotationsZip)
-
-		flags += " --extract-annotations " + d.annotationsZip.String()
+		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!")
 		}
-		ctx.VisitDirectDepsWithTag(metalavaMergeAnnotationsDirTag, func(m android.Module) {
-			if t, ok := m.(*ExportedDroiddocDir); ok {
-				*implicits = append(*implicits, t.deps...)
-				mergeAnnoDirFlags += " --merge-qualifier-annotations " + t.dir.String()
-			} else {
-				ctx.PropertyErrorf("merge_annotations_dirs",
-					"module %q is not a metalava merge-annotations dir", ctx.OtherModuleName(m))
-			}
-		})
-		flags += mergeAnnoDirFlags
-		// TODO(tnorbye): find owners to fix these warnings when annotation was enabled.
-		flags += " --hide HiddenTypedefConstant --hide SuperfluousPrefix --hide AnnotationExtraction"
-	}
 
-	return flags, mergeAnnoDirFlags
+		d.mergeAnnoDirFlags(ctx, cmd)
+
+		// TODO(tnorbye): find owners to fix these warnings when annotation was enabled.
+		cmd.FlagWithArg("--hide ", "HiddenTypedefConstant").
+			FlagWithArg("--hide ", "SuperfluousPrefix").
+			FlagWithArg("--hide ", "AnnotationExtraction")
+	}
 }
 
-func (d *Droidstubs) collectInclusionAnnotationsFlags(ctx android.ModuleContext,
-	implicits *android.Paths, implicitOutputs *android.WritablePaths) string {
-	var flags string
+func (d *Droidstubs) mergeAnnoDirFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
+	ctx.VisitDirectDepsWithTag(metalavaMergeAnnotationsDirTag, func(m android.Module) {
+		if t, ok := m.(*ExportedDroiddocDir); ok {
+			cmd.FlagWithArg("--merge-qualifier-annotations ", t.dir.String()).Implicits(t.deps)
+		} else {
+			ctx.PropertyErrorf("merge_annotations_dirs",
+				"module %q is not a metalava merge-annotations dir", ctx.OtherModuleName(m))
+		}
+	})
+}
+
+func (d *Droidstubs) inclusionAnnotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
 	ctx.VisitDirectDepsWithTag(metalavaMergeInclusionAnnotationsDirTag, func(m android.Module) {
 		if t, ok := m.(*ExportedDroiddocDir); ok {
-			*implicits = append(*implicits, t.deps...)
-			flags += " --merge-inclusion-annotations " + t.dir.String()
+			cmd.FlagWithArg("--merge-inclusion-annotations ", t.dir.String()).Implicits(t.deps)
 		} else {
 			ctx.PropertyErrorf("merge_inclusion_annotations_dirs",
 				"module %q is not a metalava merge-annotations dir", ctx.OtherModuleName(m))
 		}
 	})
-
-	return flags
 }
 
-func (d *Droidstubs) collectAPILevelsAnnotationsFlags(ctx android.ModuleContext,
-	implicits *android.Paths, implicitOutputs *android.WritablePaths) string {
-	var flags string
+func (d *Droidstubs) apiLevelsAnnotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
 	if Bool(d.properties.Api_levels_annotations_enabled) {
 		d.apiVersionsXml = android.PathForModuleOut(ctx, "api-versions.xml")
-		*implicitOutputs = append(*implicitOutputs, d.apiVersionsXml)
 
 		if len(d.properties.Api_levels_annotations_dirs) == 0 {
 			ctx.PropertyErrorf("api_levels_annotations_dirs",
 				"has to be non-empty if api levels annotations was enabled!")
 		}
 
-		flags = " --generate-api-levels " + d.apiVersionsXml.String() + " --apply-api-levels " +
-			d.apiVersionsXml.String() + " --current-version " + ctx.Config().PlatformSdkVersion() +
-			" --current-codename " + ctx.Config().PlatformSdkCodename() + " "
+		cmd.FlagWithOutput("--generate-api-levels ", d.apiVersionsXml)
+		cmd.FlagWithInput("--apply-api-levels ", d.apiVersionsXml)
+		cmd.FlagWithArg("--current-version ", ctx.Config().PlatformSdkVersion())
+		cmd.FlagWithArg("--current-codename ", ctx.Config().PlatformSdkCodename())
 
 		ctx.VisitDirectDepsWithTag(metalavaAPILevelsAnnotationsDirTag, func(m android.Module) {
 			if t, ok := m.(*ExportedDroiddocDir); ok {
-				var androidJars android.Paths
 				for _, dep := range t.deps {
 					if strings.HasSuffix(dep.String(), "android.jar") {
-						androidJars = append(androidJars, dep)
+						cmd.Implicit(dep)
 					}
 				}
-				*implicits = append(*implicits, androidJars...)
-				flags += " --android-jar-pattern " + t.dir.String() + "/%/public/android.jar "
+				cmd.FlagWithArg("--android-jar-pattern ", t.dir.String()+"/%/public/android.jar")
 			} else {
 				ctx.PropertyErrorf("api_levels_annotations_dirs",
 					"module %q is not a metalava api-levels-annotations dir", ctx.OtherModuleName(m))
@@ -1509,112 +1432,57 @@
 		})
 
 	}
-
-	return flags
 }
 
-func (d *Droidstubs) collectApiToXmlFlags(ctx android.ModuleContext, implicits *android.Paths,
-	implicitOutputs *android.WritablePaths) string {
-	var flags string
+func (d *Droidstubs) apiToXmlFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
 	if Bool(d.properties.Jdiff_enabled) && !ctx.Config().IsPdkBuild() {
 		if d.apiFile.String() == "" {
 			ctx.ModuleErrorf("API signature file has to be specified in Metalava when jdiff is enabled.")
 		}
 
 		d.apiXmlFile = android.PathForModuleOut(ctx, ctx.ModuleName()+"_api.xml")
-		*implicitOutputs = append(*implicitOutputs, d.apiXmlFile)
-
-		flags = " --api-xml " + d.apiXmlFile.String()
+		cmd.FlagWithOutput("--api-xml ", d.apiXmlFile)
 
 		if String(d.properties.Check_api.Last_released.Api_file) == "" {
 			ctx.PropertyErrorf("check_api.last_released.api_file",
 				"has to be non-empty if jdiff was enabled!")
 		}
-		lastReleasedApi := ctx.ExpandSource(String(d.properties.Check_api.Last_released.Api_file),
-			"check_api.last_released.api_file")
-		*implicits = append(*implicits, lastReleasedApi)
 
+		lastReleasedApi := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Api_file))
 		d.lastReleasedApiXmlFile = android.PathForModuleOut(ctx, ctx.ModuleName()+"_last_released_api.xml")
-		*implicitOutputs = append(*implicitOutputs, d.lastReleasedApiXmlFile)
+		cmd.FlagWithInput("--convert-to-jdiff ", lastReleasedApi).Output(d.lastReleasedApiXmlFile)
+	}
+}
 
-		flags += " --convert-to-jdiff " + lastReleasedApi.String() + " " +
-			d.lastReleasedApiXmlFile.String()
+func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, javaVersion javaVersion, srcs android.Paths,
+	srcJarList android.Path, bootclasspath, classpath classpath, sourcepaths android.Paths) *android.RuleBuilderCommand {
+	cmd := rule.Command().BuiltTool(ctx, "metalava").
+		Flag(config.JavacVmFlags).
+		FlagWithArg("-encoding ", "UTF-8").
+		FlagWithArg("-source ", javaVersion.String()).
+		FlagWithRspFileInputList("@", srcs).
+		FlagWithInput("@", srcJarList)
+
+	if len(bootclasspath) > 0 {
+		cmd.FlagWithInputList("-bootclasspath ", bootclasspath.Paths(), ":")
 	}
 
-	return flags
-}
+	if len(classpath) > 0 {
+		cmd.FlagWithInputList("-classpath ", classpath.Paths(), ":")
+	}
 
-func (d *Droidstubs) transformMetalava(ctx android.ModuleContext, implicits android.Paths,
-	implicitOutputs android.WritablePaths, javaVersion,
-	bootclasspathArgs, classpathArgs, sourcepathArgs, opts string) {
+	if len(sourcepaths) > 0 {
+		cmd.FlagWithList("-sourcepath ", sourcepaths.Strings(), ":")
+	} else {
+		cmd.FlagWithArg("-sourcepath ", `""`)
+	}
 
-	ctx.Build(pctx, android.BuildParams{
-		Rule:            metalava,
-		Description:     "Metalava",
-		Output:          d.Javadoc.stubsSrcJar,
-		Inputs:          d.Javadoc.srcFiles,
-		Implicits:       implicits,
-		ImplicitOutputs: implicitOutputs,
-		Args: map[string]string{
-			"outDir":            android.PathForModuleOut(ctx, "out").String(),
-			"srcJarDir":         android.PathForModuleOut(ctx, "srcjars").String(),
-			"stubsDir":          android.PathForModuleOut(ctx, "stubsDir").String(),
-			"srcJars":           strings.Join(d.Javadoc.srcJars.Strings(), " "),
-			"javaVersion":       javaVersion,
-			"bootclasspathArgs": bootclasspathArgs,
-			"classpathArgs":     classpathArgs,
-			"sourcepathArgs":    sourcepathArgs,
-			"opts":              opts,
-		},
-	})
-}
+	cmd.Flag("--no-banner").
+		Flag("--color").
+		Flag("--quiet").
+		Flag("--format=v2")
 
-func (d *Droidstubs) transformCheckApi(ctx android.ModuleContext,
-	apiFile, removedApiFile android.Path, implicits android.Paths,
-	javaVersion, bootclasspathArgs, classpathArgs, sourcepathArgs, opts, subdir, msg string,
-	output android.WritablePath) {
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        metalavaApiCheck,
-		Description: "Metalava Check API",
-		Output:      output,
-		Inputs:      d.Javadoc.srcFiles,
-		Implicits: append(android.Paths{apiFile, removedApiFile, d.apiFile, d.removedApiFile},
-			implicits...),
-		Args: map[string]string{
-			"srcJarDir":         android.PathForModuleOut(ctx, subdir, "srcjars").String(),
-			"srcJars":           strings.Join(d.Javadoc.srcJars.Strings(), " "),
-			"javaVersion":       javaVersion,
-			"bootclasspathArgs": bootclasspathArgs,
-			"classpathArgs":     classpathArgs,
-			"sourcepathArgs":    sourcepathArgs,
-			"opts":              opts,
-			"msg":               msg,
-		},
-	})
-}
-
-func (d *Droidstubs) transformJdiff(ctx android.ModuleContext, implicits android.Paths,
-	implicitOutputs android.WritablePaths,
-	bootclasspathArgs, classpathArgs, sourcepathArgs, opts string) {
-	ctx.Build(pctx, android.BuildParams{
-		Rule:            javadoc,
-		Description:     "Jdiff",
-		Output:          d.jdiffStubsSrcJar,
-		Inputs:          d.Javadoc.srcFiles,
-		Implicits:       implicits,
-		ImplicitOutputs: implicitOutputs,
-		Args: map[string]string{
-			"outDir":            android.PathForModuleOut(ctx, "jdiff-out").String(),
-			"srcJarDir":         android.PathForModuleOut(ctx, "jdiff-srcjars").String(),
-			"stubsDir":          android.PathForModuleOut(ctx, "jdiff-stubsDir").String(),
-			"srcJars":           strings.Join(d.Javadoc.srcJars.Strings(), " "),
-			"opts":              opts,
-			"bootclasspathArgs": bootclasspathArgs,
-			"classpathArgs":     classpathArgs,
-			"sourcepathArgs":    sourcepathArgs,
-			"docZip":            d.jdiffDocZip.String(),
-		},
-	})
+	return cmd
 }
 
 func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -1622,29 +1490,29 @@
 
 	javaVersion := getJavaVersion(ctx, String(d.Javadoc.properties.Java_version), sdkContext(d))
 
-	var implicits android.Paths
-	implicits = append(implicits, d.Javadoc.srcJars...)
-	implicits = append(implicits, d.Javadoc.argFiles...)
+	// Create rule for metalava
 
-	var implicitOutputs android.WritablePaths
-	for _, o := range d.Javadoc.properties.Out {
-		implicitOutputs = append(implicitOutputs, android.PathForModuleGen(ctx, o))
-	}
+	d.Javadoc.stubsSrcJar = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"stubs.srcjar")
 
-	flags, err := d.initBuilderFlags(ctx, &implicits, deps)
-	metalavaCheckApiImplicits := implicits
-	jdiffImplicits := implicits
+	srcJarDir := android.PathForModuleOut(ctx, "srcjars")
+	stubsDir := android.PathForModuleOut(ctx, "stubsDir")
 
-	if err != nil {
-		return
-	}
+	rule := android.NewRuleBuilder()
 
-	flags.metalavaStubsFlags = d.collectStubsFlags(ctx, &implicitOutputs)
-	flags.metalavaAnnotationsFlags, flags.metalavaMergeAnnoDirFlags =
-		d.collectAnnotationsFlags(ctx, &implicits, &implicitOutputs)
-	flags.metalavaInclusionAnnotationsFlags = d.collectInclusionAnnotationsFlags(ctx, &implicits, &implicitOutputs)
-	flags.metalavaApiLevelsAnnotationsFlags = d.collectAPILevelsAnnotationsFlags(ctx, &implicits, &implicitOutputs)
-	flags.metalavaApiToXmlFlags = d.collectApiToXmlFlags(ctx, &implicits, &implicitOutputs)
+	rule.Command().Text("rm -rf").Text(stubsDir.String())
+	rule.Command().Text("mkdir -p").Text(stubsDir.String())
+
+	srcJarList := zipSyncCmd(ctx, rule, srcJarDir, d.Javadoc.srcJars)
+
+	cmd := metalavaCmd(ctx, rule, javaVersion, d.Javadoc.srcFiles, srcJarList,
+		deps.bootClasspath, deps.classpath, d.Javadoc.sourcepaths)
+
+	d.stubsFlags(ctx, cmd, stubsDir)
+
+	d.annotationsFlags(ctx, cmd)
+	d.inclusionAnnotationsFlags(ctx, cmd)
+	d.apiLevelsAnnotationsFlags(ctx, cmd)
+	d.apiToXmlFlags(ctx, cmd)
 
 	if strings.Contains(d.Javadoc.args, "--generate-documentation") {
 		// Currently Metalava have the ability to invoke Javadoc in a seperate process.
@@ -1652,61 +1520,229 @@
 		// "--generate-documentation" arg. This is not needed when Metalava removes this feature.
 		d.Javadoc.args = d.Javadoc.args + " -nodocs "
 	}
-	d.transformMetalava(ctx, implicits, implicitOutputs, javaVersion,
-		flags.bootClasspathArgs, flags.classpathArgs, flags.sourcepathArgs,
-		flags.metalavaStubsFlags+flags.metalavaAnnotationsFlags+flags.metalavaInclusionAnnotationsFlags+
-			flags.metalavaApiLevelsAnnotationsFlags+flags.metalavaApiToXmlFlags+" "+d.Javadoc.args)
 
-	if apiCheckEnabled(d.properties.Check_api.Current, "current") &&
-		!ctx.Config().IsPdkBuild() {
-		apiFile := ctx.ExpandSource(String(d.properties.Check_api.Current.Api_file),
-			"check_api.current.api_file")
-		removedApiFile := ctx.ExpandSource(String(d.properties.Check_api.Current.Removed_api_file),
-			"check_api.current_removed_api_file")
-
-		d.checkCurrentApiTimestamp = android.PathForModuleOut(ctx, "check_current_api.timestamp")
-		opts := " " + d.Javadoc.args + " --check-compatibility:api:current " + apiFile.String() +
-			" --check-compatibility:removed:current " + removedApiFile.String() +
-			flags.metalavaInclusionAnnotationsFlags + flags.metalavaMergeAnnoDirFlags + " "
-
-		d.transformCheckApi(ctx, apiFile, removedApiFile, metalavaCheckApiImplicits,
-			javaVersion, flags.bootClasspathArgs, flags.classpathArgs, flags.sourcepathArgs, opts, "current-apicheck",
-			fmt.Sprintf(`\n******************************\n`+
-				`You have tried to change the API from what has been previously approved.\n\n`+
-				`To make these errors go away, you have two choices:\n`+
-				`   1. You can add '@hide' javadoc comments to the methods, etc. listed in the\n`+
-				`      errors above.\n\n`+
-				`   2. You can update current.txt by executing the following command:\n`+
-				`         make %s-update-current-api\n\n`+
-				`      To submit the revised current.txt to the main Android repository,\n`+
-				`      you will need approval.\n`+
-				`******************************\n`, ctx.ModuleName()),
-			d.checkCurrentApiTimestamp)
-
-		d.updateCurrentApiTimestamp = android.PathForModuleOut(ctx, "update_current_api.timestamp")
-		transformUpdateApi(ctx, apiFile, removedApiFile, d.apiFile, d.removedApiFile,
-			d.updateCurrentApiTimestamp)
+	cmd.Flag(d.Javadoc.args).Implicits(d.Javadoc.argFiles)
+	for _, o := range d.Javadoc.properties.Out {
+		cmd.ImplicitOutput(android.PathForModuleGen(ctx, o))
 	}
 
-	if apiCheckEnabled(d.properties.Check_api.Last_released, "last_released") &&
+	rule.Command().
+		BuiltTool(ctx, "soong_zip").
+		Flag("-write_if_changed").
+		Flag("-jar").
+		FlagWithOutput("-o ", d.Javadoc.stubsSrcJar).
+		FlagWithArg("-C ", stubsDir.String()).
+		FlagWithArg("-D ", stubsDir.String())
+
+	if Bool(d.properties.Write_sdk_values) {
+		d.metadataZip = android.PathForModuleOut(ctx, ctx.ModuleName()+"-metadata.zip")
+		rule.Command().
+			BuiltTool(ctx, "soong_zip").
+			Flag("-write_if_changed").
+			Flag("-d").
+			FlagWithOutput("-o ", d.metadataZip).
+			FlagWithArg("-C ", d.metadataDir.String()).
+			FlagWithArg("-D ", d.metadataDir.String())
+	}
+
+	rule.Restat()
+
+	zipSyncCleanupCmd(rule, srcJarDir)
+
+	rule.Build(pctx, ctx, "metalava", "metalava")
+
+	// Create rule for apicheck
+
+	if BoolDefault(d.properties.Check_api.Api_lint.Enabled, false) && !ctx.Config().IsPdkBuild() {
+		rule := android.NewRuleBuilder()
+		rule.Command().Text("( true")
+
+		srcJarDir := android.PathForModuleOut(ctx, "api_lint", "srcjars")
+		srcJarList := zipSyncCmd(ctx, rule, srcJarDir, d.Javadoc.srcJars)
+
+		cmd := metalavaCmd(ctx, rule, javaVersion, d.Javadoc.srcFiles, srcJarList,
+			deps.bootClasspath, deps.classpath, d.Javadoc.sourcepaths)
+
+		cmd.Flag(d.Javadoc.args).Implicits(d.Javadoc.argFiles)
+
+		newSince := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Api_lint.New_since)
+		if newSince.Valid() {
+			cmd.FlagWithInput("--api-lint ", newSince.Path())
+		} else {
+			cmd.Flag("--api-lint")
+		}
+		d.apiLintReport = android.PathForModuleOut(ctx, "api_lint_report.txt")
+		cmd.FlagWithOutput("--report-even-if-suppressed ", d.apiLintReport)
+
+		d.inclusionAnnotationsFlags(ctx, cmd)
+		d.mergeAnnoDirFlags(ctx, cmd)
+
+		baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Api_lint.Baseline_file)
+		updatedBaselineOutput := android.PathForModuleOut(ctx, "api_lint_baseline.txt")
+		d.apiLintTimestamp = android.PathForModuleOut(ctx, "api_lint.timestamp")
+
+		if baselineFile.Valid() {
+			cmd.FlagWithInput("--baseline ", baselineFile.Path())
+			cmd.FlagWithOutput("--update-baseline ", updatedBaselineOutput)
+		}
+
+		zipSyncCleanupCmd(rule, srcJarDir)
+
+		msg := fmt.Sprintf(`\n******************************\n`+
+			`Your API changes are triggering API Lint warnings or errors.\n\n`+
+			`To make these errors go away, you have two choices:\n`+
+			`   1. You can suppress the errors with @SuppressLint(\"<id>\").\n\n`+
+			`   2. You can update the baseline by executing the following command:\n`+
+			`         cp \"$PWD/%s\" \"$PWD/%s\"\n\n`+
+			`******************************\n`, updatedBaselineOutput, baselineFile.Path())
+		rule.Command().
+			Text("touch").Output(d.apiLintTimestamp).
+			Text(") || (").
+			Text("echo").Flag("-e").Flag(`"` + msg + `"`).
+			Text("; exit 38").
+			Text(")")
+
+		rule.Build(pctx, ctx, "metalavaApiLint", "metalava API lint")
+
+	}
+
+	if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") &&
 		!ctx.Config().IsPdkBuild() {
-		apiFile := ctx.ExpandSource(String(d.properties.Check_api.Last_released.Api_file),
-			"check_api.last_released.api_file")
-		removedApiFile := ctx.ExpandSource(String(d.properties.Check_api.Last_released.Removed_api_file),
-			"check_api.last_released.removed_api_file")
+
+		if len(d.Javadoc.properties.Out) > 0 {
+			ctx.PropertyErrorf("out", "out property may not be combined with check_api")
+		}
+
+		apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Api_file))
+		removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Removed_api_file))
+		baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Current.Baseline_file)
+		updatedBaselineOutput := android.PathForModuleOut(ctx, "current_baseline.txt")
+
+		d.checkCurrentApiTimestamp = android.PathForModuleOut(ctx, "check_current_api.timestamp")
+
+		rule := android.NewRuleBuilder()
+
+		rule.Command().Text("( true")
+
+		srcJarDir := android.PathForModuleOut(ctx, "current-apicheck", "srcjars")
+		srcJarList := zipSyncCmd(ctx, rule, srcJarDir, d.Javadoc.srcJars)
+
+		cmd := metalavaCmd(ctx, rule, javaVersion, d.Javadoc.srcFiles, srcJarList,
+			deps.bootClasspath, deps.classpath, d.Javadoc.sourcepaths)
+
+		cmd.Flag(d.Javadoc.args).Implicits(d.Javadoc.argFiles).
+			FlagWithInput("--check-compatibility:api:current ", apiFile).
+			FlagWithInput("--check-compatibility:removed:current ", removedApiFile)
+
+		d.inclusionAnnotationsFlags(ctx, cmd)
+		d.mergeAnnoDirFlags(ctx, cmd)
+
+		if baselineFile.Valid() {
+			cmd.FlagWithInput("--baseline ", baselineFile.Path())
+			cmd.FlagWithOutput("--update-baseline ", updatedBaselineOutput)
+		}
+
+		zipSyncCleanupCmd(rule, srcJarDir)
+
+		msg := fmt.Sprintf(`\n******************************\n`+
+			`You have tried to change the API from what has been previously approved.\n\n`+
+			`To make these errors go away, you have two choices:\n`+
+			`   1. You can add '@hide' javadoc comments to the methods, etc. listed in the\n`+
+			`      errors above.\n\n`+
+			`   2. You can update current.txt by executing the following command:\n`+
+			`         make %s-update-current-api\n\n`+
+			`      To submit the revised current.txt to the main Android repository,\n`+
+			`      you will need approval.\n`+
+			`******************************\n`, ctx.ModuleName())
+
+		rule.Command().
+			Text("touch").Output(d.checkCurrentApiTimestamp).
+			Text(") || (").
+			Text("echo").Flag("-e").Flag(`"` + msg + `"`).
+			Text("; exit 38").
+			Text(")")
+
+		rule.Build(pctx, ctx, "metalavaCurrentApiCheck", "metalava check current API")
+
+		d.updateCurrentApiTimestamp = android.PathForModuleOut(ctx, "update_current_api.timestamp")
+
+		// update API rule
+		rule = android.NewRuleBuilder()
+
+		rule.Command().Text("( true")
+
+		rule.Command().
+			Text("cp").Flag("-f").
+			Input(d.apiFile).Flag(apiFile.String())
+
+		rule.Command().
+			Text("cp").Flag("-f").
+			Input(d.removedApiFile).Flag(removedApiFile.String())
+
+		msg = "failed to update public API"
+
+		rule.Command().
+			Text("touch").Output(d.updateCurrentApiTimestamp).
+			Text(") || (").
+			Text("echo").Flag("-e").Flag(`"` + msg + `"`).
+			Text("; exit 38").
+			Text(")")
+
+		rule.Build(pctx, ctx, "metalavaCurrentApiUpdate", "update current API")
+	}
+
+	if apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") &&
+		!ctx.Config().IsPdkBuild() {
+
+		if len(d.Javadoc.properties.Out) > 0 {
+			ctx.PropertyErrorf("out", "out property may not be combined with check_api")
+		}
+
+		apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Api_file))
+		removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Removed_api_file))
+		baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Last_released.Baseline_file)
+		updatedBaselineOutput := android.PathForModuleOut(ctx, "last_released_baseline.txt")
 
 		d.checkLastReleasedApiTimestamp = android.PathForModuleOut(ctx, "check_last_released_api.timestamp")
-		opts := " " + d.Javadoc.args + " --check-compatibility:api:released " + apiFile.String() +
-			flags.metalavaInclusionAnnotationsFlags + " --check-compatibility:removed:released " +
-			removedApiFile.String() + flags.metalavaMergeAnnoDirFlags + " "
 
-		d.transformCheckApi(ctx, apiFile, removedApiFile, metalavaCheckApiImplicits,
-			javaVersion, flags.bootClasspathArgs, flags.classpathArgs, flags.sourcepathArgs, opts, "last-apicheck",
-			`\n******************************\n`+
-				`You have tried to change the API from what has been previously released in\n`+
-				`an SDK.  Please fix the errors listed above.\n`+
-				`******************************\n`,
-			d.checkLastReleasedApiTimestamp)
+		rule := android.NewRuleBuilder()
+
+		rule.Command().Text("( true")
+
+		srcJarDir := android.PathForModuleOut(ctx, "last-apicheck", "srcjars")
+		srcJarList := zipSyncCmd(ctx, rule, srcJarDir, d.Javadoc.srcJars)
+
+		cmd := metalavaCmd(ctx, rule, javaVersion, d.Javadoc.srcFiles, srcJarList,
+			deps.bootClasspath, deps.classpath, d.Javadoc.sourcepaths)
+
+		cmd.Flag(d.Javadoc.args).Implicits(d.Javadoc.argFiles).
+			FlagWithInput("--check-compatibility:api:released ", apiFile)
+
+		d.inclusionAnnotationsFlags(ctx, cmd)
+
+		cmd.FlagWithInput("--check-compatibility:removed:released ", removedApiFile)
+
+		d.mergeAnnoDirFlags(ctx, cmd)
+
+		if baselineFile.Valid() {
+			cmd.FlagWithInput("--baseline ", baselineFile.Path())
+			cmd.FlagWithOutput("--update-baseline ", updatedBaselineOutput)
+		}
+
+		zipSyncCleanupCmd(rule, srcJarDir)
+
+		msg := `\n******************************\n` +
+			`You have tried to change the API from what has been previously released in\n` +
+			`an SDK.  Please fix the errors listed above.\n` +
+			`******************************\n`
+		rule.Command().
+			Text("touch").Output(d.checkLastReleasedApiTimestamp).
+			Text(") || (").
+			Text("echo").Flag("-e").Flag(`"` + msg + `"`).
+			Text("; exit 38").
+			Text(")")
+
+		rule.Build(pctx, ctx, "metalavaLastApiCheck", "metalava check last API")
 	}
 
 	if String(d.properties.Check_nullability_warnings) != "" {
@@ -1714,9 +1750,11 @@
 			ctx.PropertyErrorf("check_nullability_warnings",
 				"Cannot specify check_nullability_warnings unless validating nullability")
 		}
-		checkNullabilityWarnings := ctx.ExpandSource(String(d.properties.Check_nullability_warnings),
-			"check_nullability_warnings")
+
+		checkNullabilityWarnings := android.PathForModuleSrc(ctx, String(d.properties.Check_nullability_warnings))
+
 		d.checkNullabilityWarningsTimestamp = android.PathForModuleOut(ctx, "check_nullability_warnings.timestamp")
+
 		msg := fmt.Sprintf(`\n******************************\n`+
 			`The warnings encountered during nullability annotation validation did\n`+
 			`not match the checked in file of expected warnings. The diffs are shown\n`+
@@ -1726,20 +1764,32 @@
 			`         cp %s %s\n`+
 			`       and submitting the updated file as part of your change.`,
 			d.nullabilityWarningsFile, checkNullabilityWarnings)
-		ctx.Build(pctx, android.BuildParams{
-			Rule:        nullabilityWarningsCheck,
-			Description: "Nullability Warnings Check",
-			Output:      d.checkNullabilityWarningsTimestamp,
-			Implicits:   android.Paths{checkNullabilityWarnings, d.nullabilityWarningsFile},
-			Args: map[string]string{
-				"expected": checkNullabilityWarnings.String(),
-				"actual":   d.nullabilityWarningsFile.String(),
-				"msg":      msg,
-			},
-		})
+
+		rule := android.NewRuleBuilder()
+
+		rule.Command().
+			Text("(").
+			Text("diff").Input(checkNullabilityWarnings).Input(d.nullabilityWarningsFile).
+			Text("&&").
+			Text("touch").Output(d.checkNullabilityWarningsTimestamp).
+			Text(") || (").
+			Text("echo").Flag("-e").Flag(`"` + msg + `"`).
+			Text("; exit 38").
+			Text(")")
+
+		rule.Build(pctx, ctx, "nullabilityWarningsCheck", "nullability warnings check")
 	}
 
 	if Bool(d.properties.Jdiff_enabled) && !ctx.Config().IsPdkBuild() {
+		if len(d.Javadoc.properties.Out) > 0 {
+			ctx.PropertyErrorf("out", "out property may not be combined with jdiff")
+		}
+
+		outDir := android.PathForModuleOut(ctx, "jdiff-out")
+		srcJarDir := android.PathForModuleOut(ctx, "jdiff-srcjars")
+		stubsDir := android.PathForModuleOut(ctx, "jdiff-stubsDir")
+
+		rule := android.NewRuleBuilder()
 
 		// Please sync with android-api-council@ before making any changes for the name of jdiffDocZip below
 		// since there's cron job downstream that fetch this .zip file periodically.
@@ -1747,21 +1797,49 @@
 		d.jdiffDocZip = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"jdiff-docs.zip")
 		d.jdiffStubsSrcJar = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"jdiff-stubs.srcjar")
 
-		var jdiffImplicitOutputs android.WritablePaths
-		jdiffImplicitOutputs = append(jdiffImplicitOutputs, d.jdiffDocZip)
-
 		jdiff := android.PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "framework", "jdiff.jar")
-		jdiffImplicits = append(jdiffImplicits, android.Paths{jdiff, d.apiXmlFile, d.lastReleasedApiXmlFile}...)
 
-		opts := " -source 1.8 -J-Xmx1600m -XDignore.symbol.file " +
-			"-doclet jdiff.JDiff -docletpath " + jdiff.String() + " -quiet " +
-			"-newapi " + strings.TrimSuffix(d.apiXmlFile.Base(), d.apiXmlFile.Ext()) +
-			" -newapidir " + filepath.Dir(d.apiXmlFile.String()) +
-			" -oldapi " + strings.TrimSuffix(d.lastReleasedApiXmlFile.Base(), d.lastReleasedApiXmlFile.Ext()) +
-			" -oldapidir " + filepath.Dir(d.lastReleasedApiXmlFile.String())
+		rule.Command().Text("rm -rf").Text(outDir.String()).Text(stubsDir.String())
+		rule.Command().Text("mkdir -p").Text(outDir.String()).Text(stubsDir.String())
 
-		d.transformJdiff(ctx, jdiffImplicits, jdiffImplicitOutputs, flags.bootClasspathArgs, flags.classpathArgs,
-			flags.sourcepathArgs, opts)
+		srcJarList := zipSyncCmd(ctx, rule, srcJarDir, d.Javadoc.srcJars)
+
+		cmd := javadocBootclasspathCmd(ctx, rule, d.Javadoc.srcFiles, outDir, srcJarDir, srcJarList,
+			deps.bootClasspath, deps.classpath, d.sourcepaths)
+
+		cmd.Flag("-J-Xmx1600m").
+			Flag("-XDignore.symbol.file").
+			FlagWithArg("-doclet ", "jdiff.JDiff").
+			FlagWithInput("-docletpath ", jdiff).
+			Flag("-quiet").
+			FlagWithArg("-newapi ", strings.TrimSuffix(d.apiXmlFile.Base(), d.apiXmlFile.Ext())).
+			FlagWithArg("-newapidir ", filepath.Dir(d.apiXmlFile.String())).
+			Implicit(d.apiXmlFile).
+			FlagWithArg("-oldapi ", strings.TrimSuffix(d.lastReleasedApiXmlFile.Base(), d.lastReleasedApiXmlFile.Ext())).
+			FlagWithArg("-oldapidir ", filepath.Dir(d.lastReleasedApiXmlFile.String())).
+			Implicit(d.lastReleasedApiXmlFile)
+
+		rule.Command().
+			BuiltTool(ctx, "soong_zip").
+			Flag("-write_if_changed").
+			Flag("-d").
+			FlagWithOutput("-o ", d.jdiffDocZip).
+			FlagWithArg("-C ", outDir.String()).
+			FlagWithArg("-D ", outDir.String())
+
+		rule.Command().
+			BuiltTool(ctx, "soong_zip").
+			Flag("-write_if_changed").
+			Flag("-jar").
+			FlagWithOutput("-o ", d.jdiffStubsSrcJar).
+			FlagWithArg("-C ", stubsDir.String()).
+			FlagWithArg("-D ", stubsDir.String())
+
+		rule.Restat()
+
+		zipSyncCleanupCmd(rule, srcJarDir)
+
+		rule.Build(pctx, ctx, "jdiff", "jdiff")
 	}
 }
 
@@ -1787,6 +1865,7 @@
 	dir  android.Path
 }
 
+// droiddoc_exported_dir exports a directory of html templates or nullability annotations for use by doclava.
 func ExportedDroiddocDirFactory() android.Module {
 	module := &ExportedDroiddocDir{}
 	module.AddProperties(&module.properties)
@@ -1835,3 +1914,135 @@
 
 	return module
 }
+
+func zipSyncCmd(ctx android.ModuleContext, rule *android.RuleBuilder,
+	srcJarDir android.ModuleOutPath, srcJars android.Paths) android.OutputPath {
+
+	rule.Command().Text("rm -rf").Text(srcJarDir.String())
+	rule.Command().Text("mkdir -p").Text(srcJarDir.String())
+	srcJarList := srcJarDir.Join(ctx, "list")
+
+	rule.Temporary(srcJarList)
+
+	rule.Command().BuiltTool(ctx, "zipsync").
+		FlagWithArg("-d ", srcJarDir.String()).
+		FlagWithOutput("-l ", srcJarList).
+		FlagWithArg("-f ", `"*.java"`).
+		Inputs(srcJars)
+
+	return srcJarList
+}
+
+func zipSyncCleanupCmd(rule *android.RuleBuilder, srcJarDir android.ModuleOutPath) {
+	rule.Command().Text("rm -rf").Text(srcJarDir.String())
+}
+
+var _ android.PrebuiltInterface = (*PrebuiltStubsSources)(nil)
+
+type PrebuiltStubsSourcesProperties struct {
+	Srcs []string `android:"path"`
+}
+
+type PrebuiltStubsSources struct {
+	android.ModuleBase
+	android.DefaultableModuleBase
+	prebuilt android.Prebuilt
+	android.SdkBase
+
+	properties PrebuiltStubsSourcesProperties
+
+	// The source directories containing stubs source files.
+	srcDirs     android.Paths
+	stubsSrcJar android.ModuleOutPath
+}
+
+func (p *PrebuiltStubsSources) OutputFiles(tag string) (android.Paths, error) {
+	switch tag {
+	case "":
+		return android.Paths{p.stubsSrcJar}, nil
+	default:
+		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+	}
+}
+
+func (p *PrebuiltStubsSources) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	p.stubsSrcJar = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"stubs.srcjar")
+
+	p.srcDirs = android.PathsForModuleSrc(ctx, p.properties.Srcs)
+
+	rule := android.NewRuleBuilder()
+	command := rule.Command().
+		BuiltTool(ctx, "soong_zip").
+		Flag("-write_if_changed").
+		Flag("-jar").
+		FlagWithOutput("-o ", p.stubsSrcJar)
+
+	for _, d := range p.srcDirs {
+		dir := d.String()
+		command.
+			FlagWithArg("-C ", dir).
+			FlagWithInput("-D ", d)
+	}
+
+	rule.Restat()
+
+	rule.Build(pctx, ctx, "zip src", "Create srcjar from prebuilt source")
+}
+
+func (p *PrebuiltStubsSources) Prebuilt() *android.Prebuilt {
+	return &p.prebuilt
+}
+
+func (p *PrebuiltStubsSources) Name() string {
+	return p.prebuilt.Name(p.ModuleBase.Name())
+}
+
+// prebuilt_stubs_sources imports a set of java source files as if they were
+// generated by droidstubs.
+//
+// By default, a prebuilt_stubs_sources has a single variant that expects a
+// set of `.java` files generated by droidstubs.
+//
+// Specifying `host_supported: true` will produce two variants, one for use as a dependency of device modules and one
+// for host modules.
+//
+// Intended only for use by sdk snapshots.
+func PrebuiltStubsSourcesFactory() android.Module {
+	module := &PrebuiltStubsSources{}
+
+	module.AddProperties(&module.properties)
+
+	android.InitPrebuiltModule(module, &module.properties.Srcs)
+	android.InitSdkAwareModule(module)
+	InitDroiddocModule(module, android.HostAndDeviceSupported)
+	return module
+}
+
+type droidStubsSdkMemberType struct {
+	android.SdkMemberTypeBase
+}
+
+func (mt *droidStubsSdkMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
+	mctx.AddVariationDependencies(nil, dependencyTag, names...)
+}
+
+func (mt *droidStubsSdkMemberType) IsInstance(module android.Module) bool {
+	_, ok := module.(*Droidstubs)
+	return ok
+}
+
+func (mt *droidStubsSdkMemberType) BuildSnapshot(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder, member android.SdkMember) {
+	variants := member.Variants()
+	if len(variants) != 1 {
+		sdkModuleContext.ModuleErrorf("sdk contains %d variants of member %q but only one is allowed", len(variants), member.Name())
+	}
+	variant := variants[0]
+	d, _ := variant.(*Droidstubs)
+	stubsSrcJar := d.stubsSrcJar
+
+	snapshotRelativeDir := filepath.Join("java", d.Name()+"_stubs_sources")
+	builder.UnzipToSnapshot(stubsSrcJar, snapshotRelativeDir)
+
+	pbm := builder.AddPrebuiltModule(member, "prebuilt_stubs_sources")
+	pbm.AddProperty("srcs", []string{snapshotRelativeDir})
+}
diff --git a/java/gen.go b/java/gen.go
index b1c028d..d50a665 100644
--- a/java/gen.go
+++ b/java/gen.go
@@ -15,27 +15,22 @@
 package java
 
 import (
+	"strconv"
+	"strings"
+
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/pathtools"
 
 	"android/soong/android"
 )
 
 func init() {
-	pctx.HostBinToolVariable("aidlCmd", "aidl")
-	pctx.HostBinToolVariable("syspropCmd", "sysprop_java")
 	pctx.SourcePathVariable("logtagsCmd", "build/make/tools/java-event-log-tags.py")
 	pctx.SourcePathVariable("mergeLogtagsCmd", "build/make/tools/merge-event-log-tags.py")
 	pctx.SourcePathVariable("logtagsLib", "build/make/tools/event_log_tags.py")
 }
 
 var (
-	aidl = pctx.AndroidStaticRule("aidl",
-		blueprint.RuleParams{
-			Command:     "$aidlCmd -d$depFile $aidlFlags $in $out",
-			CommandDeps: []string{"$aidlCmd"},
-		},
-		"depFile", "aidlFlags")
-
 	logtags = pctx.AndroidStaticRule("logtags",
 		blueprint.RuleParams{
 			Command:     "$logtagsCmd -o $out $in",
@@ -47,36 +42,66 @@
 			Command:     "$mergeLogtagsCmd -o $out $in",
 			CommandDeps: []string{"$mergeLogtagsCmd", "$logtagsLib"},
 		})
-
-	sysprop = pctx.AndroidStaticRule("sysprop",
-		blueprint.RuleParams{
-			Command: `rm -rf $out.tmp && mkdir -p $out.tmp && ` +
-				`$syspropCmd --java-output-dir $out.tmp $in && ` +
-				`${config.SoongZipCmd} -jar -o $out -C $out.tmp -D $out.tmp && rm -rf $out.tmp`,
-			CommandDeps: []string{
-				"$syspropCmd",
-				"${config.SoongZipCmd}",
-			},
-		})
 )
 
-func genAidl(ctx android.ModuleContext, aidlFile android.Path, aidlFlags string, deps android.Paths) android.Path {
-	javaFile := android.GenPathWithExt(ctx, "aidl", aidlFile, "java")
-	depFile := javaFile.String() + ".d"
+func genAidl(ctx android.ModuleContext, aidlFiles android.Paths, aidlFlags string, deps android.Paths) android.Paths {
+	// Shard aidl files into groups of 50 to avoid having to recompile all of them if one changes and to avoid
+	// hitting command line length limits.
+	shards := android.ShardPaths(aidlFiles, 50)
 
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        aidl,
-		Description: "aidl " + aidlFile.Rel(),
-		Output:      javaFile,
-		Input:       aidlFile,
-		Implicits:   deps,
-		Args: map[string]string{
-			"depFile":   depFile,
-			"aidlFlags": aidlFlags,
-		},
-	})
+	srcJarFiles := make(android.Paths, 0, len(shards))
 
-	return javaFile
+	for i, shard := range shards {
+		srcJarFile := android.PathForModuleGen(ctx, "aidl", "aidl"+strconv.Itoa(i)+".srcjar")
+		srcJarFiles = append(srcJarFiles, srcJarFile)
+
+		outDir := srcJarFile.ReplaceExtension(ctx, "tmp")
+
+		rule := android.NewRuleBuilder()
+
+		rule.Command().Text("rm -rf").Flag(outDir.String())
+		rule.Command().Text("mkdir -p").Flag(outDir.String())
+		rule.Command().Text("FLAGS=' " + aidlFlags + "'")
+
+		for _, aidlFile := range shard {
+			depFile := srcJarFile.InSameDir(ctx, aidlFile.String()+".d")
+			javaFile := outDir.Join(ctx, pathtools.ReplaceExtension(aidlFile.String(), "java"))
+			rule.Command().
+				Tool(ctx.Config().HostToolPath(ctx, "aidl")).
+				FlagWithDepFile("-d", depFile).
+				Flag("$FLAGS").
+				Input(aidlFile).
+				Output(javaFile).
+				Implicits(deps)
+			rule.Temporary(javaFile)
+		}
+
+		rule.Command().
+			Tool(ctx.Config().HostToolPath(ctx, "soong_zip")).
+			// TODO(b/124333557): this can't use -srcjar for now, aidl on parcelables generates java files
+			//  without a package statement, which causes -srcjar to put them in the top level of the zip file.
+			//  Once aidl skips parcelables we can use -srcjar.
+			//Flag("-srcjar").
+			Flag("-write_if_changed").
+			FlagWithOutput("-o ", srcJarFile).
+			FlagWithArg("-C ", outDir.String()).
+			FlagWithArg("-D ", outDir.String())
+
+		rule.Command().Text("rm -rf").Flag(outDir.String())
+
+		rule.Restat()
+
+		ruleName := "aidl"
+		ruleDesc := "aidl"
+		if len(shards) > 1 {
+			ruleName += "_" + strconv.Itoa(i)
+			ruleDesc += " " + strconv.Itoa(i)
+		}
+
+		rule.Build(pctx, ctx, ruleName, ruleDesc)
+	}
+
+	return srcJarFiles
 }
 
 func genLogtags(ctx android.ModuleContext, logtagsFile android.Path) android.Path {
@@ -92,44 +117,55 @@
 	return javaFile
 }
 
-func genSysprop(ctx android.ModuleContext, syspropFile android.Path) android.Path {
-	srcJarFile := android.GenPathWithExt(ctx, "sysprop", syspropFile, "srcjar")
-
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        sysprop,
-		Description: "sysprop_java " + syspropFile.Rel(),
-		Output:      srcJarFile,
-		Input:       syspropFile,
-	})
-
-	return srcJarFile
+func genAidlIncludeFlags(srcFiles android.Paths) string {
+	var baseDirs []string
+	for _, srcFile := range srcFiles {
+		if srcFile.Ext() == ".aidl" {
+			baseDir := strings.TrimSuffix(srcFile.String(), srcFile.Rel())
+			if baseDir != "" && !android.InList(baseDir, baseDirs) {
+				baseDirs = append(baseDirs, baseDir)
+			}
+		}
+	}
+	return android.JoinWithPrefix(baseDirs, " -I")
 }
 
 func (j *Module) genSources(ctx android.ModuleContext, srcFiles android.Paths,
 	flags javaBuilderFlags) android.Paths {
 
 	outSrcFiles := make(android.Paths, 0, len(srcFiles))
+	var protoSrcs android.Paths
+	var aidlSrcs android.Paths
+
+	aidlIncludeFlags := genAidlIncludeFlags(srcFiles)
 
 	for _, srcFile := range srcFiles {
 		switch srcFile.Ext() {
 		case ".aidl":
-			javaFile := genAidl(ctx, srcFile, flags.aidlFlags, flags.aidlDeps)
-			outSrcFiles = append(outSrcFiles, javaFile)
+			aidlSrcs = append(aidlSrcs, srcFile)
 		case ".logtags":
 			j.logtagsSrcs = append(j.logtagsSrcs, srcFile)
 			javaFile := genLogtags(ctx, srcFile)
 			outSrcFiles = append(outSrcFiles, javaFile)
 		case ".proto":
-			srcJarFile := genProto(ctx, srcFile, flags.proto)
-			outSrcFiles = append(outSrcFiles, srcJarFile)
-		case ".sysprop":
-			srcJarFile := genSysprop(ctx, srcFile)
-			outSrcFiles = append(outSrcFiles, srcJarFile)
+			protoSrcs = append(protoSrcs, srcFile)
 		default:
 			outSrcFiles = append(outSrcFiles, srcFile)
 		}
 	}
 
+	// Process all proto files together to support sharding them into one or more rules that produce srcjars.
+	if len(protoSrcs) > 0 {
+		srcJarFiles := genProto(ctx, protoSrcs, flags.proto)
+		outSrcFiles = append(outSrcFiles, srcJarFiles...)
+	}
+
+	// Process all aidl files together to support sharding them into one or more rules that produce srcjars.
+	if len(aidlSrcs) > 0 {
+		srcJarFiles := genAidl(ctx, aidlSrcs, flags.aidlFlags+aidlIncludeFlags, flags.aidlDeps)
+		outSrcFiles = append(outSrcFiles, srcJarFiles...)
+	}
+
 	return outSrcFiles
 }
 
diff --git a/java/genrule.go b/java/genrule.go
index 25494ec..e0a9c8f 100644
--- a/java/genrule.go
+++ b/java/genrule.go
@@ -20,8 +20,12 @@
 )
 
 func init() {
-	android.RegisterModuleType("java_genrule", genRuleFactory)
-	android.RegisterModuleType("java_genrule_host", genRuleFactoryHost)
+	RegisterGenRuleBuildComponents(android.InitRegistrationContext)
+}
+
+func RegisterGenRuleBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("java_genrule", genRuleFactory)
+	ctx.RegisterModuleType("java_genrule_host", genRuleFactoryHost)
 }
 
 // java_genrule is a genrule that can depend on other java_* objects.
diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go
index cf9f492..ad84cde 100644
--- a/java/hiddenapi_singleton.go
+++ b/java/hiddenapi_singleton.go
@@ -64,7 +64,7 @@
 	stubFlagsRule(ctx)
 
 	// These rules depend on files located in frameworks/base, skip them if running in a tree that doesn't have them.
-	if ctx.Config().FrameworksBaseDirExists(ctx) && !ctx.Config().UnbundledBuild() {
+	if ctx.Config().FrameworksBaseDirExists(ctx) {
 		h.flags = flagsRule(ctx)
 		h.metadata = metadataRule(ctx)
 	} else {
@@ -97,7 +97,7 @@
 	// Add the android.test.base to the set of stubs only if the android.test.base module is on
 	// the boot jars list as the runtime will only enforce hiddenapi access against modules on
 	// that list.
-	if inList("android.test.base", ctx.Config().BootJars()) {
+	if inList("android.test.base", ctx.Config().BootJars()) && !ctx.Config().UnbundledBuildUsePrebuiltSdks() {
 		publicStubModules = append(publicStubModules, "android.test.base.stubs")
 	}
 
@@ -152,6 +152,14 @@
 		// Collect dex jar paths for modules that had hiddenapi encode called on them.
 		if h, ok := module.(hiddenAPIIntf); ok {
 			if jar := h.bootDexJar(); jar != nil {
+				// For a java lib included in an APEX, only take the one built for
+				// the platform variant, and skip the variants for APEXes.
+				// Otherwise, the hiddenapi tool will complain about duplicated classes
+				if a, ok := module.(android.ApexModule); ok {
+					if android.InAnyApex(module.Name()) && !a.IsForPlatform() {
+						return
+					}
+				}
 				bootDexJars = append(bootDexJars, jar)
 			}
 		}
@@ -182,7 +190,7 @@
 	rule.MissingDeps(missingDeps)
 
 	rule.Command().
-		Tool(pctx.HostBinToolPath(ctx, "hiddenapi")).
+		Tool(ctx.Config().HostToolPath(ctx, "hiddenapi")).
 		Text("list").
 		FlagForEachInput("--boot-dex=", bootDexJars).
 		FlagWithInputList("--public-stub-classpath=", publicStubPaths, ":").
@@ -233,6 +241,8 @@
 			android.PathForSource(ctx, "frameworks/base/config/hiddenapi-greylist.txt")).
 		FlagWithInput("--greylist-ignore-conflicts ",
 			greylistIgnoreConflicts).
+		FlagWithInput("--greylist-max-q ",
+			android.PathForSource(ctx, "frameworks/base/config/hiddenapi-greylist-max-q.txt")).
 		FlagWithInput("--greylist-max-p ",
 			android.PathForSource(ctx, "frameworks/base/config/hiddenapi-greylist-max-p.txt")).
 		FlagWithInput("--greylist-max-o-ignore-conflicts ",
diff --git a/java/java.go b/java/java.go
index a2e9ab0..4c6a5a5 100644
--- a/java/java.go
+++ b/java/java.go
@@ -25,6 +25,7 @@
 	"strings"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/pathtools"
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
@@ -33,23 +34,79 @@
 )
 
 func init() {
-	android.RegisterModuleType("java_defaults", defaultsFactory)
+	RegisterJavaBuildComponents(android.InitRegistrationContext)
 
-	android.RegisterModuleType("java_library", LibraryFactory)
-	android.RegisterModuleType("java_library_static", LibraryStaticFactory)
-	android.RegisterModuleType("java_library_host", LibraryHostFactory)
-	android.RegisterModuleType("java_binary", BinaryFactory)
-	android.RegisterModuleType("java_binary_host", BinaryHostFactory)
-	android.RegisterModuleType("java_test", TestFactory)
-	android.RegisterModuleType("java_test_helper_library", TestHelperLibraryFactory)
-	android.RegisterModuleType("java_test_host", TestHostFactory)
-	android.RegisterModuleType("java_import", ImportFactory)
-	android.RegisterModuleType("java_import_host", ImportFactoryHost)
-	android.RegisterModuleType("java_device_for_host", DeviceForHostFactory)
-	android.RegisterModuleType("java_host_for_device", HostForDeviceFactory)
-	android.RegisterModuleType("dex_import", DexImportFactory)
+	// Register sdk member types.
+	android.RegisterSdkMemberType(&headerLibrarySdkMemberType{
+		librarySdkMemberType{
+			android.SdkMemberTypeBase{
+				PropertyName: "java_header_libs",
+				SupportsSdk:  true,
+			},
+		},
+	})
 
-	android.RegisterSingletonType("logtags", LogtagsSingleton)
+	android.RegisterSdkMemberType(&implLibrarySdkMemberType{
+		librarySdkMemberType{
+			android.SdkMemberTypeBase{
+				PropertyName: "java_libs",
+			},
+		},
+	})
+
+	android.RegisterSdkMemberType(&testSdkMemberType{
+		SdkMemberTypeBase: android.SdkMemberTypeBase{
+			PropertyName: "java_tests",
+		},
+	})
+}
+
+func RegisterJavaBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("java_defaults", DefaultsFactory)
+
+	ctx.RegisterModuleType("java_library", LibraryFactory)
+	ctx.RegisterModuleType("java_library_static", LibraryStaticFactory)
+	ctx.RegisterModuleType("java_library_host", LibraryHostFactory)
+	ctx.RegisterModuleType("java_binary", BinaryFactory)
+	ctx.RegisterModuleType("java_binary_host", BinaryHostFactory)
+	ctx.RegisterModuleType("java_test", TestFactory)
+	ctx.RegisterModuleType("java_test_helper_library", TestHelperLibraryFactory)
+	ctx.RegisterModuleType("java_test_host", TestHostFactory)
+	ctx.RegisterModuleType("java_test_import", JavaTestImportFactory)
+	ctx.RegisterModuleType("java_import", ImportFactory)
+	ctx.RegisterModuleType("java_import_host", ImportFactoryHost)
+	ctx.RegisterModuleType("java_device_for_host", DeviceForHostFactory)
+	ctx.RegisterModuleType("java_host_for_device", HostForDeviceFactory)
+	ctx.RegisterModuleType("dex_import", DexImportFactory)
+
+	ctx.RegisterSingletonType("logtags", LogtagsSingleton)
+	ctx.RegisterSingletonType("kythe_java_extract", kytheExtractJavaFactory)
+}
+
+func (j *Module) checkSdkVersion(ctx android.ModuleContext) {
+	if j.SocSpecific() || j.DeviceSpecific() ||
+		(j.ProductSpecific() && ctx.Config().EnforceProductPartitionInterface()) {
+		if sc, ok := ctx.Module().(sdkContext); ok {
+			if sc.sdkVersion() == "" {
+				ctx.PropertyErrorf("sdk_version",
+					"sdk_version must have a value when the module is located at vendor or product(only if PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE is set).")
+			}
+		}
+	}
+}
+
+func (j *Module) checkPlatformAPI(ctx android.ModuleContext) {
+	if sc, ok := ctx.Module().(sdkContext); ok {
+		usePlatformAPI := proptools.Bool(j.deviceProperties.Platform_apis)
+		if usePlatformAPI != (sc.sdkVersion() == "") {
+			if usePlatformAPI {
+				ctx.PropertyErrorf("platform_apis", "platform_apis must be false when sdk_version is not empty.")
+			} else {
+				ctx.PropertyErrorf("platform_apis", "platform_apis must be true when sdk_version is empty.")
+			}
+		}
+
+	}
 }
 
 // TODO:
@@ -82,9 +139,6 @@
 	// list of files that should be excluded from java_resources and java_resource_dirs
 	Exclude_java_resources []string `android:"path,arch_variant"`
 
-	// don't build against the framework libraries (ext, and framework for device targets)
-	No_framework_libs *bool
-
 	// list of module-specific flags that will be used for javac compiles
 	Javacflags []string `android:"arch_variant"`
 
@@ -120,6 +174,9 @@
 	// List of modules to use as annotation processors
 	Plugins []string
 
+	// List of modules to export to libraries that directly depend on this library as annotation processors
+	Exported_plugins []string
+
 	// The number of Java source entries each Javac instance can process
 	Javac_shard_size *int64
 
@@ -127,10 +184,10 @@
 	Use_tools_jar *bool
 
 	Openjdk9 struct {
-		// List of source files that should only be used when passing -source 1.9
+		// List of source files that should only be used when passing -source 1.9 or higher
 		Srcs []string `android:"path"`
 
-		// List of javac flags that should only be used when passing -source 1.9
+		// List of javac flags that should only be used when passing -source 1.9 or higher
 		Javacflags []string
 	}
 
@@ -181,8 +238,8 @@
 	// list of module-specific flags that will be used for dex compiles
 	Dxflags []string `android:"arch_variant"`
 
-	// if not blank, set to the version of the sdk to compile against.  Defaults to compiling against the current
-	// sdk if platform_apis is not set.
+	// if not blank, set to the version of the sdk to compile against.
+	// Defaults to compiling against the current platform.
 	Sdk_version *string
 
 	// if not blank, set the minimum version of the sdk that the compiled artifacts will run against.
@@ -193,7 +250,9 @@
 	// Defaults to sdk_version if not set.
 	Target_sdk_version *string
 
-	// if true, compile against the platform APIs instead of an SDK.
+	// Whether to compile against the platform APIs instead of an SDK.
+	// If true, then sdk_version must be empty. The value of this field
+	// is ignored when module's type isn't android_app.
 	Platform_apis *bool
 
 	Aidl struct {
@@ -255,9 +314,13 @@
 		Proguard_flags_files []string `android:"path"`
 	}
 
-	// When targeting 1.9, override the modules to use with --system
+	// When targeting 1.9 and above, override the modules to use with --system,
+	// otherwise provides defaults libraries to add to the bootclasspath.
 	System_modules *string
 
+	// set the name of the output
+	Stem *string
+
 	UncompressDex bool `blueprint:"mutated"`
 	IsSDKLibrary  bool `blueprint:"mutated"`
 }
@@ -270,6 +333,8 @@
 type Module struct {
 	android.ModuleBase
 	android.DefaultableModuleBase
+	android.ApexModuleBase
+	android.SdkBase
 
 	properties       CompilerProperties
 	protoProperties  android.ProtoProperties
@@ -327,11 +392,17 @@
 	// manifest file to use instead of properties.Manifest
 	overrideManifest android.OptionalPath
 
-	// list of SDK lib names that this java moudule is exporting
+	// list of SDK lib names that this java module is exporting
 	exportedSdkLibs []string
 
-	// list of source files, collected from compiledJavaSrcs and compiledSrcJars
-	// filter out Exclude_srcs, will be used by android.IDEInfo struct
+	// list of plugins that this java module is exporting
+	exportedPluginJars android.Paths
+
+	// list of plugins that this java module is exporting
+	exportedPluginClasses []string
+
+	// list of source files, collected from srcFiles with unique java and all kt files,
+	// will be used by android.IDEInfo struct
 	expandIDEInfoCompiledSrcs []string
 
 	// expanded Jarjar_rules
@@ -345,6 +416,9 @@
 
 	hiddenAPI
 	dexpreopter
+
+	// list of the xref extraction files
+	kytheFiles android.Paths
 }
 
 func (j *Module) OutputFiles(tag string) (android.Paths, error) {
@@ -353,15 +427,13 @@
 		return append(android.Paths{j.outputFile}, j.extraOutputFiles...), nil
 	case ".jar":
 		return android.Paths{j.implementationAndResourcesJar}, nil
+	case ".proguard_map":
+		return android.Paths{j.proguardDictionary}, nil
 	default:
 		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
 	}
 }
 
-func (j *Module) DexJarFile() android.Path {
-	return j.dexJarFile
-}
-
 var _ android.OutputFileProducer = (*Module)(nil)
 
 type Dependency interface {
@@ -372,7 +444,10 @@
 	DexJar() android.Path
 	AidlIncludeDirs() android.Paths
 	ExportedSdkLibs() []string
+	ExportedPlugins() (android.Paths, []string)
 	SrcJarArgs() ([]string, android.Paths)
+	BaseModuleName() string
+	JacocoReportClassesFile() android.Path
 }
 
 type SdkLibraryDependency interface {
@@ -380,21 +455,14 @@
 	SdkImplementationJars(ctx android.BaseModuleContext, sdkVersion string) android.Paths
 }
 
-type SrcDependency interface {
-	CompiledSrcs() android.Paths
-	CompiledSrcJars() android.Paths
+type xref interface {
+	XrefJavaFiles() android.Paths
 }
 
-func (j *Module) CompiledSrcs() android.Paths {
-	return j.compiledJavaSrcs
+func (j *Module) XrefJavaFiles() android.Paths {
+	return j.kytheFiles
 }
 
-func (j *Module) CompiledSrcJars() android.Paths {
-	return j.compiledSrcJars
-}
-
-var _ SrcDependency = (*Module)(nil)
-
 func InitJavaModule(module android.DefaultableModule, hod android.HostOrDeviceSupported) {
 	android.InitAndroidArchModule(module, hod, android.MultilibCommon)
 	android.InitDefaultableModule(module)
@@ -407,13 +475,19 @@
 
 type jniDependencyTag struct {
 	blueprint.BaseDependencyTag
-	target android.Target
+}
+
+func IsJniDepTag(depTag blueprint.DependencyTag) bool {
+	_, ok := depTag.(*jniDependencyTag)
+	return ok
 }
 
 var (
 	staticLibTag          = dependencyTag{name: "staticlib"}
 	libTag                = dependencyTag{name: "javalib"}
+	java9LibTag           = dependencyTag{name: "java9lib"}
 	pluginTag             = dependencyTag{name: "plugin"}
+	exportedPluginTag     = dependencyTag{name: "exported-plugin"}
 	bootClasspathTag      = dependencyTag{name: "bootclasspath"}
 	systemModulesTag      = dependencyTag{name: "system modules"}
 	frameworkResTag       = dependencyTag{name: "framework-res"}
@@ -426,12 +500,27 @@
 	usesLibTag            = dependencyTag{name: "uses-library"}
 )
 
+func IsLibDepTag(depTag blueprint.DependencyTag) bool {
+	return depTag == libTag
+}
+
+func IsStaticLibDepTag(depTag blueprint.DependencyTag) bool {
+	return depTag == staticLibTag
+}
+
 type sdkDep struct {
 	useModule, useFiles, useDefaultLibs, invalidVersion bool
 
-	modules       []string
+	// The modules that will be added to the bootclasspath when targeting 1.8 or lower
+	bootclasspath []string
+
+	// The default system modules to use. Will be an empty string if no system
+	// modules are to be used.
 	systemModules string
 
+	// The modules that will be added ot the classpath when targeting 1.9 or higher
+	java9Classpath []string
+
 	frameworkResModule string
 
 	jars android.Paths
@@ -468,6 +557,10 @@
 	return String(j.deviceProperties.Sdk_version)
 }
 
+func (j *Module) systemModules() string {
+	return proptools.String(j.deviceProperties.System_modules)
+}
+
 func (j *Module) minSdkVersion() string {
 	if j.deviceProperties.Min_sdk_version != nil {
 		return *j.deviceProperties.Min_sdk_version
@@ -482,37 +575,35 @@
 	return j.sdkVersion()
 }
 
-func (j *Module) noFrameworkLibs() bool {
-	return Bool(j.properties.No_framework_libs)
+func (j *Module) AvailableFor(what string) bool {
+	if what == android.AvailableToPlatform && Bool(j.deviceProperties.Hostdex) {
+		// Exception: for hostdex: true libraries, the platform variant is created
+		// even if it's not marked as available to platform. In that case, the platform
+		// variant is used only for the hostdex and not installed to the device.
+		return true
+	}
+	return j.ApexModuleBase.AvailableFor(what)
 }
 
 func (j *Module) deps(ctx android.BottomUpMutatorContext) {
 	if ctx.Device() {
 		sdkDep := decodeSdkDep(ctx, sdkContext(j))
-		if sdkDep.hasStandardLibs() {
-			if sdkDep.useDefaultLibs {
-				ctx.AddVariationDependencies(nil, bootClasspathTag, config.DefaultBootclasspathLibraries...)
-				ctx.AddVariationDependencies(nil, systemModulesTag, config.DefaultSystemModules)
-				if sdkDep.hasFrameworkLibs() {
-					ctx.AddVariationDependencies(nil, libTag, config.DefaultLibraries...)
-				}
-			} else if sdkDep.useModule {
-				ctx.AddVariationDependencies(nil, systemModulesTag, sdkDep.systemModules)
-				ctx.AddVariationDependencies(nil, bootClasspathTag, sdkDep.modules...)
-				if j.deviceProperties.EffectiveOptimizeEnabled() {
-					ctx.AddVariationDependencies(nil, proguardRaiseTag, config.DefaultBootclasspathLibraries...)
-					ctx.AddVariationDependencies(nil, proguardRaiseTag, config.DefaultLibraries...)
-				}
+		if sdkDep.useDefaultLibs {
+			ctx.AddVariationDependencies(nil, bootClasspathTag, config.DefaultBootclasspathLibraries...)
+			ctx.AddVariationDependencies(nil, systemModulesTag, config.DefaultSystemModules)
+			if sdkDep.hasFrameworkLibs() {
+				ctx.AddVariationDependencies(nil, libTag, config.DefaultLibraries...)
 			}
-		} else if j.deviceProperties.System_modules == nil {
-			ctx.PropertyErrorf("sdk_version",
-				`system_modules is required to be set when sdk_version is "none", did you mean no_framework_libs?`)
-		} else if *j.deviceProperties.System_modules != "none" {
-			ctx.AddVariationDependencies(nil, systemModulesTag, *j.deviceProperties.System_modules)
+		} else if sdkDep.useModule {
+			ctx.AddVariationDependencies(nil, bootClasspathTag, sdkDep.bootclasspath...)
+			ctx.AddVariationDependencies(nil, systemModulesTag, sdkDep.systemModules)
+			ctx.AddVariationDependencies(nil, java9LibTag, sdkDep.java9Classpath...)
+			if j.deviceProperties.EffectiveOptimizeEnabled() && sdkDep.hasStandardLibs() {
+				ctx.AddVariationDependencies(nil, proguardRaiseTag, config.DefaultBootclasspathLibraries...)
+				ctx.AddVariationDependencies(nil, proguardRaiseTag, config.DefaultLibraries...)
+			}
 		}
-		if (ctx.ModuleName() == "framework") || (ctx.ModuleName() == "framework-annotation-proc") {
-			ctx.AddVariationDependencies(nil, frameworkResTag, "framework-res")
-		}
+
 		if ctx.ModuleName() == "android_stubs_current" ||
 			ctx.ModuleName() == "android_system_stubs_current" ||
 			ctx.ModuleName() == "android_test_stubs_current" {
@@ -520,12 +611,36 @@
 		}
 	}
 
-	ctx.AddVariationDependencies(nil, libTag, j.properties.Libs...)
-	ctx.AddVariationDependencies(nil, staticLibTag, j.properties.Static_libs...)
+	syspropPublicStubs := syspropPublicStubs(ctx.Config())
 
-	ctx.AddFarVariationDependencies([]blueprint.Variation{
-		{Mutator: "arch", Variation: ctx.Config().BuildOsCommonVariant},
-	}, pluginTag, j.properties.Plugins...)
+	// rewriteSyspropLibs validates if a java module can link against platform's sysprop_library,
+	// and redirects dependency to public stub depending on the link type.
+	rewriteSyspropLibs := func(libs []string, prop string) []string {
+		// make a copy
+		ret := android.CopyOf(libs)
+
+		for idx, lib := range libs {
+			stub, ok := syspropPublicStubs[lib]
+
+			if !ok {
+				continue
+			}
+
+			linkType, _ := j.getLinkType(ctx.ModuleName())
+			// only platform modules can use internal props
+			if linkType != javaPlatform {
+				ret[idx] = stub
+			}
+		}
+
+		return ret
+	}
+
+	ctx.AddVariationDependencies(nil, libTag, rewriteSyspropLibs(j.properties.Libs, "libs")...)
+	ctx.AddVariationDependencies(nil, staticLibTag, rewriteSyspropLibs(j.properties.Static_libs, "static_libs")...)
+
+	ctx.AddFarVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), pluginTag, j.properties.Plugins...)
+	ctx.AddFarVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), exportedPluginTag, j.properties.Exported_plugins...)
 
 	android.ProtoDeps(ctx, &j.protoProperties)
 	if j.hasSrcExt(".proto") {
@@ -542,7 +657,14 @@
 		}
 	}
 
-	if j.shouldInstrumentStatic(ctx) {
+	// Framework libraries need special handling in static coverage builds: they should not have
+	// static dependency on jacoco, otherwise there would be multiple conflicting definitions of
+	// the same jacoco classes coming from different bootclasspath jars.
+	if inList(ctx.ModuleName(), config.InstrumentFrameworkModules) {
+		if ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") {
+			j.properties.Instrument = true
+		}
+	} else if j.shouldInstrumentStatic(ctx) {
 		ctx.AddVariationDependencies(nil, staticLibTag, "jacocoagent")
 	}
 }
@@ -557,18 +679,6 @@
 	return false
 }
 
-func shardPaths(paths android.Paths, shardSize int) []android.Paths {
-	ret := make([]android.Paths, 0, (len(paths)+shardSize-1)/shardSize)
-	for len(paths) > shardSize {
-		ret = append(ret, paths[0:shardSize])
-		paths = paths[shardSize:]
-	}
-	if len(paths) > 0 {
-		ret = append(ret, paths)
-	}
-	return ret
-}
-
 func (j *Module) hasSrcExt(ext string) bool {
 	return hasSrcExt(j.properties.Srcs, ext)
 }
@@ -618,6 +728,7 @@
 
 type deps struct {
 	classpath          classpath
+	java9Classpath     classpath
 	bootClasspath      classpath
 	processorPath      classpath
 	processorClasses   []string
@@ -627,8 +738,7 @@
 	aidlIncludeDirs    android.Paths
 	srcs               android.Paths
 	srcJars            android.Paths
-	systemModules      android.Path
-	systemModulesDeps  android.Paths
+	systemModules      *systemModules
 	aidlPreprocess     android.OptionalPath
 	kotlinStdlib       android.Paths
 	kotlinAnnotations  android.Paths
@@ -654,7 +764,12 @@
 	javaPlatform
 )
 
-func getLinkType(m *Module, name string) (ret linkType, stubs bool) {
+type linkTypeContext interface {
+	android.Module
+	getLinkType(name string) (ret linkType, stubs bool)
+}
+
+func (m *Module) getLinkType(name string) (ret linkType, stubs bool) {
 	ver := m.sdkVersion()
 	switch {
 	case name == "core.current.stubs" || name == "core.platform.api.stubs" ||
@@ -685,16 +800,16 @@
 	}
 }
 
-func checkLinkType(ctx android.ModuleContext, from *Module, to *Library, tag dependencyTag) {
+func checkLinkType(ctx android.ModuleContext, from *Module, to linkTypeContext, tag dependencyTag) {
 	if ctx.Host() {
 		return
 	}
 
-	myLinkType, stubs := getLinkType(from, ctx.ModuleName())
+	myLinkType, stubs := from.getLinkType(ctx.ModuleName())
 	if stubs {
 		return
 	}
-	otherLinkType, _ := getLinkType(&to.Module, ctx.OtherModuleName(to))
+	otherLinkType, _ := to.getLinkType(ctx.OtherModuleName(to))
 	commonMessage := "Adjust sdk_version: property of the source or target module so that target module is built with the same or smaller API set than the source."
 
 	switch myLinkType {
@@ -728,7 +843,8 @@
 	if ctx.Device() {
 		sdkDep := decodeSdkDep(ctx, sdkContext(j))
 		if sdkDep.invalidVersion {
-			ctx.AddMissingDependencies(sdkDep.modules)
+			ctx.AddMissingDependencies(sdkDep.bootclasspath)
+			ctx.AddMissingDependencies(sdkDep.java9Classpath)
 		} else if sdkDep.useFiles {
 			// sdkDep.jar is actually equivalent to turbine header.jar.
 			deps.classpath = append(deps.classpath, sdkDep.jars...)
@@ -750,11 +866,13 @@
 			// Handled by AndroidApp.collectAppDeps
 			return
 		}
-
-		if to, ok := module.(*Library); ok {
-			switch tag {
-			case bootClasspathTag, libTag, staticLibTag:
-				checkLinkType(ctx, j, to, tag.(dependencyTag))
+		switch module.(type) {
+		case *Library, *AndroidLibrary:
+			if to, ok := module.(linkTypeContext); ok {
+				switch tag {
+				case bootClasspathTag, libTag, staticLibTag:
+					checkLinkType(ctx, j, to, tag.(dependencyTag))
+				}
 			}
 		}
 		switch dep := module.(type) {
@@ -776,6 +894,10 @@
 				// sdk lib names from dependencies are re-exported
 				j.exportedSdkLibs = append(j.exportedSdkLibs, dep.ExportedSdkLibs()...)
 				deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs()...)
+				pluginJars, pluginClasses := dep.ExportedPlugins()
+				addPlugins(&deps, pluginJars, pluginClasses...)
+			case java9LibTag:
+				deps.java9Classpath = append(deps.java9Classpath, dep.HeaderJars()...)
 			case staticLibTag:
 				deps.classpath = append(deps.classpath, dep.HeaderJars()...)
 				deps.staticJars = append(deps.staticJars, dep.ImplementationJars()...)
@@ -784,21 +906,30 @@
 				// sdk lib names from dependencies are re-exported
 				j.exportedSdkLibs = append(j.exportedSdkLibs, dep.ExportedSdkLibs()...)
 				deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs()...)
+				pluginJars, pluginClasses := dep.ExportedPlugins()
+				addPlugins(&deps, pluginJars, pluginClasses...)
 			case pluginTag:
 				if plugin, ok := dep.(*Plugin); ok {
-					deps.processorPath = append(deps.processorPath, dep.ImplementationAndResourcesJars()...)
 					if plugin.pluginProperties.Processor_class != nil {
-						deps.processorClasses = append(deps.processorClasses, *plugin.pluginProperties.Processor_class)
+						addPlugins(&deps, plugin.ImplementationAndResourcesJars(), *plugin.pluginProperties.Processor_class)
+					} else {
+						addPlugins(&deps, plugin.ImplementationAndResourcesJars())
 					}
 					deps.disableTurbine = deps.disableTurbine || Bool(plugin.pluginProperties.Generates_api)
 				} else {
 					ctx.PropertyErrorf("plugins", "%q is not a java_plugin module", otherName)
 				}
-			case frameworkResTag:
-				if (ctx.ModuleName() == "framework") || (ctx.ModuleName() == "framework-annotation-proc") {
-					// framework.jar has a one-off dependency on the R.java and Manifest.java files
-					// generated by framework-res.apk
-					deps.srcJars = append(deps.srcJars, dep.(*AndroidApp).aaptSrcJar)
+			case exportedPluginTag:
+				if plugin, ok := dep.(*Plugin); ok {
+					if plugin.pluginProperties.Generates_api != nil && *plugin.pluginProperties.Generates_api {
+						ctx.PropertyErrorf("exported_plugins", "Cannot export plugins with generates_api = true, found %v", otherName)
+					}
+					j.exportedPluginJars = append(j.exportedPluginJars, plugin.ImplementationAndResourcesJars()...)
+					if plugin.pluginProperties.Processor_class != nil {
+						j.exportedPluginClasses = append(j.exportedPluginClasses, *plugin.pluginProperties.Processor_class)
+					}
+				} else {
+					ctx.PropertyErrorf("exported_plugins", "%q is not a java_plugin module", otherName)
 				}
 			case frameworkApkTag:
 				if ctx.ModuleName() == "android_stubs_current" ||
@@ -831,6 +962,12 @@
 			}
 		default:
 			switch tag {
+			case bootClasspathTag:
+				// If a system modules dependency has been added to the bootclasspath
+				// then add its libs to the bootclasspath.
+				sm := module.(*SystemModules)
+				deps.bootClasspath = append(deps.bootClasspath, sm.headerJars...)
+
 			case systemModulesTag:
 				if deps.systemModules != nil {
 					panic("Found two system module dependencies")
@@ -839,8 +976,7 @@
 				if sm.outputDir == nil || len(sm.outputDeps) == 0 {
 					panic("Missing directory for system module dependency")
 				}
-				deps.systemModules = sm.outputDir
-				deps.systemModulesDeps = sm.outputDeps
+				deps.systemModules = &systemModules{sm.outputDir, sm.outputDeps}
 			}
 		}
 	})
@@ -850,8 +986,12 @@
 	return deps
 }
 
-func getJavaVersion(ctx android.ModuleContext, javaVersion string, sdkContext sdkContext) string {
-	var ret string
+func addPlugins(deps *deps, pluginJars android.Paths, pluginClasses ...string) {
+	deps.processorPath = append(deps.processorPath, pluginJars...)
+	deps.processorClasses = append(deps.processorClasses, pluginClasses...)
+}
+
+func getJavaVersion(ctx android.ModuleContext, javaVersion string, sdkContext sdkContext) javaVersion {
 	v := sdkContext.sdkVersion()
 	// For PDK builds, use the latest SDK version instead of "current"
 	if ctx.Config().IsPdkBuild() &&
@@ -869,23 +1009,66 @@
 		ctx.PropertyErrorf("sdk_version", "%s", err)
 	}
 	if javaVersion != "" {
-		ret = javaVersion
+		return normalizeJavaVersion(ctx, javaVersion)
 	} else if ctx.Device() && sdk <= 23 {
-		ret = "1.7"
-	} else if ctx.Device() && sdk <= 29 || !ctx.Config().TargetOpenJDK9() {
-		ret = "1.8"
-	} else if ctx.Device() &&
-		sdkContext.sdkVersion() != "" &&
-		sdkContext.sdkVersion() != "none" &&
-		sdkContext.sdkVersion() != "core_platform" &&
-		sdk == android.FutureApiLevel {
-		// TODO(ccross): once we generate stubs we should be able to use 1.9 for sdk_version: "current"
-		ret = "1.8"
+		return JAVA_VERSION_7
+	} else if ctx.Device() && sdk <= 29 {
+		return JAVA_VERSION_8
+	} else if ctx.Device() && ctx.Config().UnbundledBuildUsePrebuiltSdks() {
+		// TODO(b/142896162): once we have prebuilt system modules we can use 1.9 for unbundled builds
+		return JAVA_VERSION_8
 	} else {
-		ret = "1.9"
+		return JAVA_VERSION_9
 	}
+}
 
-	return ret
+type javaVersion int
+
+const (
+	JAVA_VERSION_UNSUPPORTED = 0
+	JAVA_VERSION_6           = 6
+	JAVA_VERSION_7           = 7
+	JAVA_VERSION_8           = 8
+	JAVA_VERSION_9           = 9
+)
+
+func (v javaVersion) String() string {
+	switch v {
+	case JAVA_VERSION_6:
+		return "1.6"
+	case JAVA_VERSION_7:
+		return "1.7"
+	case JAVA_VERSION_8:
+		return "1.8"
+	case JAVA_VERSION_9:
+		return "1.9"
+	default:
+		return "unsupported"
+	}
+}
+
+// Returns true if javac targeting this version uses system modules instead of a bootclasspath.
+func (v javaVersion) usesJavaModules() bool {
+	return v >= 9
+}
+
+func normalizeJavaVersion(ctx android.BaseModuleContext, javaVersion string) javaVersion {
+	switch javaVersion {
+	case "1.6", "6":
+		return JAVA_VERSION_6
+	case "1.7", "7":
+		return JAVA_VERSION_7
+	case "1.8", "8":
+		return JAVA_VERSION_8
+	case "1.9", "9":
+		return JAVA_VERSION_9
+	case "10", "11":
+		ctx.PropertyErrorf("java_version", "Java language levels above 9 are not supported")
+		return JAVA_VERSION_UNSUPPORTED
+	default:
+		ctx.PropertyErrorf("java_version", "Unrecognized Java language level")
+		return JAVA_VERSION_UNSUPPORTED
+	}
 }
 
 func (j *Module) collectBuilderFlags(ctx android.ModuleContext, deps deps) javaBuilderFlags {
@@ -897,7 +1080,7 @@
 
 	// javac flags.
 	javacFlags := j.properties.Javacflags
-	if flags.javaVersion == "1.9" {
+	if flags.javaVersion.usesJavaModules() {
 		javacFlags = append(javacFlags, j.properties.Openjdk9.Javacflags...)
 	}
 	if ctx.Config().MinimizeJavaDebugInfo() {
@@ -905,6 +1088,7 @@
 		// disk and memory usage.
 		javacFlags = append(javacFlags, "-g:source,lines")
 	}
+	javacFlags = append(javacFlags, "-Xlint:-dep-ann")
 
 	if ctx.Config().RunErrorProne() {
 		if config.ErrorProneClasspath == nil {
@@ -925,13 +1109,13 @@
 	// classpath
 	flags.bootClasspath = append(flags.bootClasspath, deps.bootClasspath...)
 	flags.classpath = append(flags.classpath, deps.classpath...)
+	flags.java9Classpath = append(flags.java9Classpath, deps.java9Classpath...)
 	flags.processorPath = append(flags.processorPath, deps.processorPath...)
 
 	flags.processor = strings.Join(deps.processorClasses, ",")
 
-	if len(flags.bootClasspath) == 0 && ctx.Host() && flags.javaVersion != "1.9" &&
-		decodeSdkDep(ctx, sdkContext(j)).hasStandardLibs() &&
-		inList(flags.javaVersion, []string{"1.6", "1.7", "1.8"}) {
+	if len(flags.bootClasspath) == 0 && ctx.Host() && !flags.javaVersion.usesJavaModules() &&
+		decodeSdkDep(ctx, sdkContext(j)).hasStandardLibs() {
 		// Give host-side tools a version of OpenJDK's standard libraries
 		// close to what they're targeting. As of Dec 2017, AOSP is only
 		// bundling OpenJDK 8 and 9, so nothing < 8 is available.
@@ -955,7 +1139,7 @@
 		}
 	}
 
-	if j.properties.Patch_module != nil && flags.javaVersion == "1.9" {
+	if j.properties.Patch_module != nil && flags.javaVersion.usesJavaModules() {
 		// Manually specify build directory in case it is not under the repo root.
 		// (javac doesn't seem to expand into symbolc links when searching for patch-module targets, so
 		// just adding a symlink under the root doesn't help.)
@@ -968,10 +1152,7 @@
 	}
 
 	// systemModules
-	if deps.systemModules != nil {
-		flags.systemModules = append(flags.systemModules, deps.systemModules)
-		flags.systemModulesDeps = append(flags.systemModulesDeps, deps.systemModulesDeps...)
-	}
+	flags.systemModules = deps.systemModules
 
 	// aidl flags.
 	flags.aidlFlags, flags.aidlDeps = j.aidlFlags(ctx, deps.aidlPreprocess, deps.aidlIncludeDirs)
@@ -986,13 +1167,12 @@
 }
 
 func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) {
-
 	j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.deviceProperties.Aidl.Export_include_dirs)
 
 	deps := j.collectDeps(ctx)
 	flags := j.collectBuilderFlags(ctx, deps)
 
-	if flags.javaVersion == "1.9" {
+	if flags.javaVersion.usesJavaModules() {
 		j.properties.Srcs = append(j.properties.Srcs, j.properties.Openjdk9.Srcs...)
 	}
 	srcFiles := android.PathsForModuleSrcExcludes(ctx, j.properties.Srcs, j.properties.Exclude_srcs)
@@ -1008,10 +1188,6 @@
 		srcJars = append(srcJars, aaptSrcJar)
 	}
 
-	// Collect source files from compiledJavaSrcs, compiledSrcJars and filter out Exclude_srcs
-	// that IDEInfo struct will use
-	j.expandIDEInfoCompiledSrcs = append(j.expandIDEInfoCompiledSrcs, srcFiles.Strings()...)
-
 	if j.properties.Jarjar_rules != nil {
 		j.expandJarjarRules = android.PathForModuleSrc(ctx, *j.properties.Jarjar_rules)
 	}
@@ -1028,6 +1204,9 @@
 		}
 	}
 
+	// Collect .java files for AIDEGen
+	j.expandIDEInfoCompiledSrcs = append(j.expandIDEInfoCompiledSrcs, uniqueSrcFiles.Strings()...)
+
 	var kotlinJars android.Paths
 
 	if srcFiles.HasExt(".kt") {
@@ -1052,6 +1231,9 @@
 		kotlinSrcFiles = append(kotlinSrcFiles, uniqueSrcFiles...)
 		kotlinSrcFiles = append(kotlinSrcFiles, srcFiles.FilterByExt(".kt")...)
 
+		// Collect .kt files for AIDEGen
+		j.expandIDEInfoCompiledSrcs = append(j.expandIDEInfoCompiledSrcs, srcFiles.FilterByExt(".kt").Strings()...)
+
 		flags.classpath = append(flags.classpath, deps.kotlinStdlib...)
 		flags.classpath = append(flags.classpath, deps.kotlinAnnotations...)
 
@@ -1121,21 +1303,20 @@
 			shardSize := int(*(j.properties.Javac_shard_size))
 			var shardSrcs []android.Paths
 			if len(uniqueSrcFiles) > 0 {
-				shardSrcs = shardPaths(uniqueSrcFiles, shardSize)
+				shardSrcs = android.ShardPaths(uniqueSrcFiles, shardSize)
 				for idx, shardSrc := range shardSrcs {
-					classes := android.PathForModuleOut(ctx, "javac", jarName+strconv.Itoa(idx))
-					TransformJavaToClasses(ctx, classes, idx, shardSrc, nil, flags, extraJarDeps)
+					classes := j.compileJavaClasses(ctx, jarName, idx, shardSrc,
+						nil, flags, extraJarDeps)
 					jars = append(jars, classes)
 				}
 			}
 			if len(srcJars) > 0 {
-				classes := android.PathForModuleOut(ctx, "javac", jarName+strconv.Itoa(len(shardSrcs)))
-				TransformJavaToClasses(ctx, classes, len(shardSrcs), nil, srcJars, flags, extraJarDeps)
+				classes := j.compileJavaClasses(ctx, jarName, len(shardSrcs),
+					nil, srcJars, flags, extraJarDeps)
 				jars = append(jars, classes)
 			}
 		} else {
-			classes := android.PathForModuleOut(ctx, "javac", jarName)
-			TransformJavaToClasses(ctx, classes, -1, uniqueSrcFiles, srcJars, flags, extraJarDeps)
+			classes := j.compileJavaClasses(ctx, jarName, -1, uniqueSrcFiles, srcJars, flags, extraJarDeps)
 			jars = append(jars, classes)
 		}
 		if ctx.Failed() {
@@ -1286,12 +1467,6 @@
 		j.headerJarFile = j.implementationJarFile
 	}
 
-	if ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") {
-		if inList(ctx.ModuleName(), config.InstrumentFrameworkModules) {
-			j.properties.Instrument = true
-		}
-	}
-
 	if j.shouldInstrument(ctx) {
 		outputFile = j.instrument(ctx, flags, outputFile, jarName)
 	}
@@ -1317,11 +1492,9 @@
 			return
 		}
 
-		if !ctx.Config().UnbundledBuild() {
-			// Hidden API CSV generation and dex encoding
-			dexOutputFile = j.hiddenAPI.hiddenAPI(ctx, dexOutputFile, j.implementationJarFile,
-				j.deviceProperties.UncompressDex)
-		}
+		// Hidden API CSV generation and dex encoding
+		dexOutputFile = j.hiddenAPI.hiddenAPI(ctx, dexOutputFile, j.implementationJarFile,
+			j.deviceProperties.UncompressDex)
 
 		// merge dex jar with resources if necessary
 		if j.resourceJar != nil {
@@ -1360,6 +1533,27 @@
 	j.outputFile = outputFile.WithoutRel()
 }
 
+func (j *Module) compileJavaClasses(ctx android.ModuleContext, jarName string, idx int,
+	srcFiles, srcJars android.Paths, flags javaBuilderFlags, extraJarDeps android.Paths) android.WritablePath {
+
+	kzipName := pathtools.ReplaceExtension(jarName, "kzip")
+	if idx >= 0 {
+		kzipName = strings.TrimSuffix(jarName, filepath.Ext(jarName)) + strconv.Itoa(idx) + ".kzip"
+		jarName += strconv.Itoa(idx)
+	}
+
+	classes := android.PathForModuleOut(ctx, "javac", jarName)
+	TransformJavaToClasses(ctx, classes, idx, srcFiles, srcJars, flags, extraJarDeps)
+
+	if ctx.Config().EmitXrefRules() {
+		extractionFile := android.PathForModuleOut(ctx, kzipName)
+		emitXrefRule(ctx, extractionFile, idx, srcFiles, srcJars, flags, extraJarDeps)
+		j.kytheFiles = append(j.kytheFiles, extractionFile)
+	}
+
+	return classes
+}
+
 // Check for invalid kotlinc flags. Only use this for flags explicitly passed by the user,
 // since some of these flags may be used internally.
 func CheckKotlincFlags(ctx android.ModuleContext, flags []string) {
@@ -1485,6 +1679,10 @@
 	return j.exportedSdkLibs
 }
 
+func (j *Module) ExportedPlugins() (android.Paths, []string) {
+	return j.exportedPluginJars, j.exportedPluginClasses
+}
+
 func (j *Module) SrcJarArgs() ([]string, android.Paths) {
 	return j.srcJarArgs, j.srcJarDeps
 }
@@ -1518,12 +1716,28 @@
 	return len(srcFiles) > 0 || len(ctx.GetDirectDepsWithTag(staticLibTag)) > 0
 }
 
+func (j *Module) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
+	depTag := ctx.OtherModuleDependencyTag(dep)
+	// dependencies other than the static linkage are all considered crossing APEX boundary
+	return depTag == staticLibTag
+}
+
+func (j *Module) Stem() string {
+	return proptools.StringDefault(j.deviceProperties.Stem, j.Name())
+}
+
+func (j *Module) JacocoReportClassesFile() android.Path {
+	return j.jacocoReportClassesFile
+}
+
 //
 // Java libraries (.jar file)
 //
 
 type Library struct {
 	Module
+
+	InstallMixin func(ctx android.ModuleContext, installPath android.Path) (extraInstallDeps android.Paths)
 }
 
 func shouldUncompressDex(ctx android.ModuleContext, dexpreopter *dexpreopter) bool {
@@ -1545,16 +1759,22 @@
 }
 
 func (j *Library) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	j.dexpreopter.installPath = android.PathForModuleInstall(ctx, "framework", ctx.ModuleName()+".jar")
+	j.checkSdkVersion(ctx)
+	j.dexpreopter.installPath = android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar")
 	j.dexpreopter.isSDKLibrary = j.deviceProperties.IsSDKLibrary
 	j.dexpreopter.isInstallable = Bool(j.properties.Installable)
 	j.dexpreopter.uncompressedDex = shouldUncompressDex(ctx, &j.dexpreopter)
 	j.deviceProperties.UncompressDex = j.dexpreopter.uncompressedDex
 	j.compile(ctx, nil)
 
-	if (Bool(j.properties.Installable) || ctx.Host()) && !android.DirectlyInAnyApex(ctx, ctx.ModuleName()) {
+	exclusivelyForApex := android.InAnyApex(ctx.ModuleName()) && !j.IsForPlatform()
+	if (Bool(j.properties.Installable) || ctx.Host()) && !exclusivelyForApex {
+		var extraInstallDeps android.Paths
+		if j.InstallMixin != nil {
+			extraInstallDeps = j.InstallMixin(ctx, j.outputFile)
+		}
 		j.installFile = ctx.InstallFile(android.PathForModuleInstall(ctx, "framework"),
-			ctx.ModuleName()+".jar", j.outputFile)
+			ctx.ModuleName()+".jar", j.outputFile, extraInstallDeps...)
 	}
 }
 
@@ -1562,6 +1782,97 @@
 	j.deps(ctx)
 }
 
+const (
+	aidlIncludeDir   = "aidl"
+	javaDir          = "java"
+	jarFileSuffix    = ".jar"
+	testConfigSuffix = "-AndroidTest.xml"
+)
+
+// path to the jar file of a java library. Relative to <sdk_root>/<api_dir>
+func sdkSnapshotFilePathForJar(member android.SdkMember) string {
+	return sdkSnapshotFilePathForMember(member, jarFileSuffix)
+}
+
+func sdkSnapshotFilePathForMember(member android.SdkMember, suffix string) string {
+	return filepath.Join(javaDir, member.Name()+suffix)
+}
+
+type librarySdkMemberType struct {
+	android.SdkMemberTypeBase
+}
+
+func (mt *librarySdkMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
+	mctx.AddVariationDependencies(nil, dependencyTag, names...)
+}
+
+func (mt *librarySdkMemberType) IsInstance(module android.Module) bool {
+	_, ok := module.(*Library)
+	return ok
+}
+
+func (mt *librarySdkMemberType) buildSnapshot(
+	sdkModuleContext android.ModuleContext,
+	builder android.SnapshotBuilder,
+	member android.SdkMember,
+	jarToExportGetter func(j *Library) android.Path) {
+
+	variants := member.Variants()
+	if len(variants) != 1 {
+		sdkModuleContext.ModuleErrorf("sdk contains %d variants of member %q but only one is allowed", len(variants), member.Name())
+		for _, variant := range variants {
+			sdkModuleContext.ModuleErrorf("    %q", variant)
+		}
+	}
+	variant := variants[0]
+	j := variant.(*Library)
+
+	exportedJar := jarToExportGetter(j)
+	snapshotRelativeJavaLibPath := sdkSnapshotFilePathForJar(member)
+	builder.CopyToSnapshot(exportedJar, snapshotRelativeJavaLibPath)
+
+	for _, dir := range j.AidlIncludeDirs() {
+		// TODO(jiyong): copy parcelable declarations only
+		aidlFiles, _ := sdkModuleContext.GlobWithDeps(dir.String()+"/**/*.aidl", nil)
+		for _, file := range aidlFiles {
+			builder.CopyToSnapshot(android.PathForSource(sdkModuleContext, file), filepath.Join(aidlIncludeDir, file))
+		}
+	}
+
+	module := builder.AddPrebuiltModule(member, "java_import")
+	module.AddProperty("jars", []string{snapshotRelativeJavaLibPath})
+}
+
+type headerLibrarySdkMemberType struct {
+	librarySdkMemberType
+}
+
+func (mt *headerLibrarySdkMemberType) BuildSnapshot(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder, member android.SdkMember) {
+	mt.librarySdkMemberType.buildSnapshot(sdkModuleContext, builder, member, func(j *Library) android.Path {
+		headerJars := j.HeaderJars()
+		if len(headerJars) != 1 {
+			panic(fmt.Errorf("there must be only one header jar from %q", j.Name()))
+		}
+
+		return headerJars[0]
+	})
+}
+
+type implLibrarySdkMemberType struct {
+	librarySdkMemberType
+}
+
+func (mt *implLibrarySdkMemberType) BuildSnapshot(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder, member android.SdkMember) {
+	mt.librarySdkMemberType.buildSnapshot(sdkModuleContext, builder, member, func(j *Library) android.Path {
+		implementationJars := j.ImplementationJars()
+		if len(implementationJars) != 1 {
+			panic(fmt.Errorf("there must be only one implementation jar from %q", j.Name()))
+		}
+
+		return implementationJars[0]
+	})
+}
+
 // java_library builds and links sources into a `.jar` file for the device, and possibly for the host as well.
 //
 // By default, a java_library has a single variant that produces a `.jar` file containing `.class` files that were
@@ -1582,6 +1893,8 @@
 		&module.Module.dexpreoptProperties,
 		&module.Module.protoProperties)
 
+	android.InitApexModule(module)
+	android.InitSdkAwareModule(module)
 	InitJavaModule(module, android.HostAndDeviceSupported)
 	return module
 }
@@ -1604,6 +1917,7 @@
 
 	module.Module.properties.Installable = proptools.BoolPtr(true)
 
+	android.InitApexModule(module)
 	InitJavaModule(module, android.HostSupported)
 	return module
 }
@@ -1628,6 +1942,11 @@
 	// list of files or filegroup modules that provide data that should be installed alongside
 	// the test
 	Data []string `android:"path"`
+
+	// 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 testHelperLibraryProperties struct {
@@ -1636,6 +1955,16 @@
 	Test_suites []string `android:"arch_variant"`
 }
 
+type prebuiltTestProperties 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"`
+}
+
 type Test struct {
 	Library
 
@@ -1651,8 +1980,17 @@
 	testHelperLibraryProperties testHelperLibraryProperties
 }
 
+type JavaTestImport struct {
+	Import
+
+	prebuiltTestProperties prebuiltTestProperties
+
+	testConfig android.Path
+}
+
 func (j *Test) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	j.testConfig = tradefed.AutoGenJavaTestConfig(ctx, j.testProperties.Test_config, j.testProperties.Test_config_template, j.testProperties.Test_suites)
+	j.testConfig = tradefed.AutoGenJavaTestConfig(ctx, j.testProperties.Test_config, j.testProperties.Test_config_template,
+		j.testProperties.Test_suites, j.testProperties.Auto_gen_config)
 	j.data = android.PathsForModuleSrc(ctx, j.testProperties.Data)
 
 	j.Library.GenerateAndroidBuildActions(ctx)
@@ -1662,6 +2000,53 @@
 	j.Library.GenerateAndroidBuildActions(ctx)
 }
 
+func (j *JavaTestImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	j.testConfig = tradefed.AutoGenJavaTestConfig(ctx, j.prebuiltTestProperties.Test_config, nil,
+		j.prebuiltTestProperties.Test_suites, nil)
+
+	j.Import.GenerateAndroidBuildActions(ctx)
+}
+
+type testSdkMemberType struct {
+	android.SdkMemberTypeBase
+}
+
+func (mt *testSdkMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
+	mctx.AddVariationDependencies(nil, dependencyTag, names...)
+}
+
+func (mt *testSdkMemberType) IsInstance(module android.Module) bool {
+	_, ok := module.(*Test)
+	return ok
+}
+
+func (mt *testSdkMemberType) BuildSnapshot(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder, member android.SdkMember) {
+	variants := member.Variants()
+	if len(variants) != 1 {
+		sdkModuleContext.ModuleErrorf("sdk contains %d variants of member %q but only one is allowed", len(variants), member.Name())
+		for _, variant := range variants {
+			sdkModuleContext.ModuleErrorf("    %q", variant)
+		}
+	}
+	variant := variants[0]
+	j := variant.(*Test)
+
+	implementationJars := j.ImplementationJars()
+	if len(implementationJars) != 1 {
+		panic(fmt.Errorf("there must be only one implementation jar from %q", j.Name()))
+	}
+
+	snapshotRelativeJavaLibPath := sdkSnapshotFilePathForJar(member)
+	builder.CopyToSnapshot(implementationJars[0], snapshotRelativeJavaLibPath)
+
+	snapshotRelativeTestConfigPath := sdkSnapshotFilePathForMember(member, testConfigSuffix)
+	builder.CopyToSnapshot(j.testConfig, snapshotRelativeTestConfigPath)
+
+	module := builder.AddPrebuiltModule(member, "java_test_import")
+	module.AddProperty("jars", []string{snapshotRelativeJavaLibPath})
+	module.AddProperty("test_config", snapshotRelativeTestConfigPath)
+}
+
 // java_test builds a and links sources into a `.jar` file for the device, and possibly for the host as well, and
 // creates an `AndroidTest.xml` file to allow running the test with `atest` or a `TEST_MAPPING` file.
 //
@@ -1705,6 +2090,30 @@
 	return module
 }
 
+// java_test_import imports one or more `.jar` files into the build graph as if they were built by a java_test module
+// and makes sure that it is added to the appropriate test suite.
+//
+// By default, a java_test_import has a single variant that expects a `.jar` file containing `.class` files that were
+// compiled against an Android classpath.
+//
+// Specifying `host_supported: true` will produce two variants, one for use as a dependency of device modules and one
+// for host modules.
+func JavaTestImportFactory() android.Module {
+	module := &JavaTestImport{}
+
+	module.AddProperties(
+		&module.Import.properties,
+		&module.prebuiltTestProperties)
+
+	module.Import.properties.Installable = proptools.BoolPtr(true)
+
+	android.InitPrebuiltModule(module, &module.properties.Jars)
+	android.InitApexModule(module)
+	android.InitSdkAwareModule(module)
+	InitJavaModule(module, android.HostAndDeviceSupported)
+	return module
+}
+
 // java_test_host builds a and links sources into a `.jar` file for the host, and creates an `AndroidTest.xml` file to
 // allow running the test with `atest` or a `TEST_MAPPING` file.
 //
@@ -1744,7 +2153,7 @@
 	isWrapperVariant bool
 
 	wrapperFile android.Path
-	binaryFile  android.OutputPath
+	binaryFile  android.InstallPath
 }
 
 func (j *Binary) HostToolPath() android.OptionalPath {
@@ -1855,12 +2264,17 @@
 
 	// if set to true, run Jetifier against .jar file. Defaults to false.
 	Jetifier *bool
+
+	// set the name of the output
+	Stem *string
 }
 
 type Import struct {
 	android.ModuleBase
 	android.DefaultableModuleBase
+	android.ApexModuleBase
 	prebuilt android.Prebuilt
+	android.SdkBase
 
 	properties ImportProperties
 
@@ -1888,6 +2302,14 @@
 	return j.prebuilt.Name(j.ModuleBase.Name())
 }
 
+func (j *Import) Stem() string {
+	return proptools.StringDefault(j.properties.Stem, j.ModuleBase.Name())
+}
+
+func (a *Import) JacocoReportClassesFile() android.Path {
+	return nil
+}
+
 func (j *Import) DepsMutator(ctx android.BottomUpMutatorContext) {
 	ctx.AddVariationDependencies(nil, libTag, j.properties.Libs...)
 }
@@ -1895,7 +2317,7 @@
 func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	jars := android.PathsForModuleSrc(ctx, j.properties.Jars)
 
-	jarName := ctx.ModuleName() + ".jar"
+	jarName := j.Stem() + ".jar"
 	outputFile := android.PathForModuleOut(ctx, "combined", jarName)
 	TransformJarsToJar(ctx, outputFile, "for prebuilts", jars, android.OptionalPath{},
 		false, j.properties.Exclude_files, j.properties.Exclude_dirs)
@@ -1929,7 +2351,7 @@
 	j.exportedSdkLibs = android.FirstUniqueStrings(j.exportedSdkLibs)
 	if Bool(j.properties.Installable) {
 		ctx.InstallFile(android.PathForModuleInstall(ctx, "framework"),
-			ctx.ModuleName()+".jar", outputFile)
+			jarName, outputFile)
 	}
 }
 
@@ -1972,6 +2394,10 @@
 	return j.exportedSdkLibs
 }
 
+func (j *Import) ExportedPlugins() (android.Paths, []string) {
+	return nil, nil
+}
+
 func (j *Import) SrcJarArgs() ([]string, android.Paths) {
 	return nil, nil
 }
@@ -2015,6 +2441,8 @@
 	module.AddProperties(&module.properties)
 
 	android.InitPrebuiltModule(module, &module.properties.Jars)
+	android.InitApexModule(module)
+	android.InitSdkAwareModule(module)
 	InitJavaModule(module, android.HostAndDeviceSupported)
 	return module
 }
@@ -2030,6 +2458,7 @@
 	module.AddProperties(&module.properties)
 
 	android.InitPrebuiltModule(module, &module.properties.Jars)
+	android.InitApexModule(module)
 	InitJavaModule(module, android.HostSupported)
 	return module
 }
@@ -2037,12 +2466,16 @@
 // dex_import module
 
 type DexImportProperties struct {
-	Jars []string
+	Jars []string `android:"path"`
+
+	// set the name of the output
+	Stem *string
 }
 
 type DexImport struct {
 	android.ModuleBase
 	android.DefaultableModuleBase
+	android.ApexModuleBase
 	prebuilt android.Prebuilt
 
 	properties DexImportProperties
@@ -2065,8 +2498,8 @@
 	return j.prebuilt.Name(j.ModuleBase.Name())
 }
 
-func (j *DexImport) DepsMutator(ctx android.BottomUpMutatorContext) {
-	android.ExtractSourcesDeps(ctx, j.properties.Jars)
+func (j *DexImport) Stem() string {
+	return proptools.StringDefault(j.properties.Stem, j.ModuleBase.Name())
 }
 
 func (j *DexImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -2074,7 +2507,7 @@
 		ctx.PropertyErrorf("jars", "exactly one jar must be provided")
 	}
 
-	j.dexpreopter.installPath = android.PathForModuleInstall(ctx, "framework", ctx.ModuleName()+".jar")
+	j.dexpreopter.installPath = android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar")
 	j.dexpreopter.isInstallable = true
 	j.dexpreopter.uncompressedDex = shouldUncompressDex(ctx, &j.dexpreopter)
 
@@ -2089,14 +2522,14 @@
 
 		// use zip2zip to uncompress classes*.dex files
 		rule.Command().
-			Tool(ctx.Config().HostToolPath(ctx, "zip2zip")).
+			BuiltTool(ctx, "zip2zip").
 			FlagWithInput("-i ", inputJar).
 			FlagWithOutput("-o ", temporary).
 			FlagWithArg("-0 ", "'classes*.dex'")
 
 		// use zipalign to align uncompressed classes*.dex files
 		rule.Command().
-			Tool(ctx.Config().HostToolPath(ctx, "zipalign")).
+			BuiltTool(ctx, "zipalign").
 			Flag("-f").
 			Text("4").
 			Input(temporary).
@@ -2137,6 +2570,7 @@
 	module.AddProperties(&module.properties)
 
 	android.InitPrebuiltModule(module, &module.properties.Jars)
+	android.InitApexModule(module)
 	InitJavaModule(module, android.DeviceSupported)
 	return module
 }
@@ -2147,6 +2581,7 @@
 type Defaults struct {
 	android.ModuleBase
 	android.DefaultsModuleBase
+	android.ApexModuleBase
 }
 
 // java_defaults provides a set of properties that can be inherited by other java or android modules.
@@ -2184,10 +2619,9 @@
 	return DefaultsFactory()
 }
 
-func DefaultsFactory(props ...interface{}) android.Module {
+func DefaultsFactory() android.Module {
 	module := &Defaults{}
 
-	module.AddProperties(props...)
 	module.AddProperties(
 		&CompilerProperties{},
 		&CompilerDeviceProperties{},
@@ -2202,13 +2636,37 @@
 		&AARImportProperties{},
 		&sdkLibraryProperties{},
 		&DexImportProperties{},
+		&android.ApexProperties{},
 	)
 
 	android.InitDefaultsModule(module)
-
 	return module
 }
 
+func kytheExtractJavaFactory() android.Singleton {
+	return &kytheExtractJavaSingleton{}
+}
+
+type kytheExtractJavaSingleton struct {
+}
+
+func (ks *kytheExtractJavaSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+	var xrefTargets android.Paths
+	ctx.VisitAllModules(func(module android.Module) {
+		if javaModule, ok := module.(xref); ok {
+			xrefTargets = append(xrefTargets, javaModule.XrefJavaFiles()...)
+		}
+	})
+	// TODO(asmundak): perhaps emit a rule to output a warning if there were no xrefTargets
+	if len(xrefTargets) > 0 {
+		ctx.Build(pctx, android.BuildParams{
+			Rule:   blueprint.Phony,
+			Output: android.PathForPhony(ctx, "xref_java"),
+			Inputs: xrefTargets,
+		})
+	}
+}
+
 var Bool = proptools.Bool
 var BoolDefault = proptools.BoolDefault
 var String = proptools.String
diff --git a/java/java_test.go b/java/java_test.go
index 22dec07..30a8ca6 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -18,10 +18,13 @@
 	"io/ioutil"
 	"os"
 	"path/filepath"
+	"reflect"
 	"strconv"
 	"strings"
 	"testing"
 
+	"github.com/google/blueprint/proptools"
+
 	"android/soong/android"
 	"android/soong/cc"
 	"android/soong/dexpreopt"
@@ -53,154 +56,35 @@
 	os.Exit(run())
 }
 
-func testConfig(env map[string]string) android.Config {
-	return TestConfig(buildDir, env)
+func testConfig(env map[string]string, bp string, fs map[string][]byte) android.Config {
+	return TestConfig(buildDir, env, bp, fs)
 }
 
-func testContext(config android.Config, bp string,
-	fs map[string][]byte) *android.TestContext {
+func testContext() *android.TestContext {
 
 	ctx := android.NewTestArchContext()
-	ctx.RegisterModuleType("android_app", android.ModuleFactoryAdaptor(AndroidAppFactory))
-	ctx.RegisterModuleType("android_app_certificate", android.ModuleFactoryAdaptor(AndroidAppCertificateFactory))
-	ctx.RegisterModuleType("android_app_import", android.ModuleFactoryAdaptor(AndroidAppImportFactory))
-	ctx.RegisterModuleType("android_library", android.ModuleFactoryAdaptor(AndroidLibraryFactory))
-	ctx.RegisterModuleType("android_test", android.ModuleFactoryAdaptor(AndroidTestFactory))
-	ctx.RegisterModuleType("android_test_helper_app", android.ModuleFactoryAdaptor(AndroidTestHelperAppFactory))
-	ctx.RegisterModuleType("java_binary", android.ModuleFactoryAdaptor(BinaryFactory))
-	ctx.RegisterModuleType("java_binary_host", android.ModuleFactoryAdaptor(BinaryHostFactory))
-	ctx.RegisterModuleType("java_device_for_host", android.ModuleFactoryAdaptor(DeviceForHostFactory))
-	ctx.RegisterModuleType("java_host_for_device", android.ModuleFactoryAdaptor(HostForDeviceFactory))
-	ctx.RegisterModuleType("java_library", android.ModuleFactoryAdaptor(LibraryFactory))
-	ctx.RegisterModuleType("java_library_host", android.ModuleFactoryAdaptor(LibraryHostFactory))
-	ctx.RegisterModuleType("java_test", android.ModuleFactoryAdaptor(TestFactory))
-	ctx.RegisterModuleType("java_import", android.ModuleFactoryAdaptor(ImportFactory))
-	ctx.RegisterModuleType("java_import_host", android.ModuleFactoryAdaptor(ImportFactoryHost))
-	ctx.RegisterModuleType("java_defaults", android.ModuleFactoryAdaptor(defaultsFactory))
-	ctx.RegisterModuleType("java_system_modules", android.ModuleFactoryAdaptor(SystemModulesFactory))
-	ctx.RegisterModuleType("java_genrule", android.ModuleFactoryAdaptor(genRuleFactory))
-	ctx.RegisterModuleType("java_plugin", android.ModuleFactoryAdaptor(PluginFactory))
-	ctx.RegisterModuleType("dex_import", android.ModuleFactoryAdaptor(DexImportFactory))
-	ctx.RegisterModuleType("filegroup", android.ModuleFactoryAdaptor(android.FileGroupFactory))
-	ctx.RegisterModuleType("genrule", android.ModuleFactoryAdaptor(genrule.GenRuleFactory))
-	ctx.RegisterModuleType("droiddoc", android.ModuleFactoryAdaptor(DroiddocFactory))
-	ctx.RegisterModuleType("droiddoc_host", android.ModuleFactoryAdaptor(DroiddocHostFactory))
-	ctx.RegisterModuleType("droiddoc_template", android.ModuleFactoryAdaptor(ExportedDroiddocDirFactory))
-	ctx.RegisterModuleType("java_sdk_library", android.ModuleFactoryAdaptor(SdkLibraryFactory))
-	ctx.RegisterModuleType("java_sdk_library_import", android.ModuleFactoryAdaptor(sdkLibraryImportFactory))
-	ctx.RegisterModuleType("override_android_app", android.ModuleFactoryAdaptor(OverrideAndroidAppModuleFactory))
-	ctx.RegisterModuleType("prebuilt_apis", android.ModuleFactoryAdaptor(PrebuiltApisFactory))
-	ctx.PreArchMutators(android.RegisterPrebuiltsPreArchMutators)
-	ctx.PreArchMutators(android.RegisterPrebuiltsPostDepsMutators)
+	RegisterJavaBuildComponents(ctx)
+	RegisterAppBuildComponents(ctx)
+	RegisterAARBuildComponents(ctx)
+	RegisterGenRuleBuildComponents(ctx)
+	RegisterSystemModulesBuildComponents(ctx)
+	ctx.RegisterModuleType("java_plugin", PluginFactory)
+	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
+	ctx.RegisterModuleType("genrule", genrule.GenRuleFactory)
+	RegisterDocsBuildComponents(ctx)
+	RegisterStubsBuildComponents(ctx)
+	RegisterSdkLibraryBuildComponents(ctx)
 	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
-	ctx.PreArchMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.TopDown("prebuilt_apis", PrebuiltApisMutator).Parallel()
-	})
+
+	RegisterPrebuiltApisBuildComponents(ctx)
+
 	ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators)
 	ctx.RegisterPreSingletonType("overlay", android.SingletonFactoryAdaptor(OverlaySingletonFactory))
 	ctx.RegisterPreSingletonType("sdk_versions", android.SingletonFactoryAdaptor(sdkPreSingletonFactory))
 
 	// Register module types and mutators from cc needed for JNI testing
-	ctx.RegisterModuleType("cc_library", android.ModuleFactoryAdaptor(cc.LibraryFactory))
-	ctx.RegisterModuleType("cc_object", android.ModuleFactoryAdaptor(cc.ObjectFactory))
-	ctx.RegisterModuleType("toolchain_library", android.ModuleFactoryAdaptor(cc.ToolchainLibraryFactory))
-	ctx.RegisterModuleType("llndk_library", android.ModuleFactoryAdaptor(cc.LlndkLibraryFactory))
-	ctx.RegisterModuleType("ndk_prebuilt_shared_stl", android.ModuleFactoryAdaptor(cc.NdkPrebuiltSharedStlFactory))
-	ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.BottomUp("link", cc.LinkageMutator).Parallel()
-		ctx.BottomUp("begin", cc.BeginMutator).Parallel()
-	})
-
-	bp += GatherRequiredDepsForTest()
-
-	mockFS := map[string][]byte{
-		"Android.bp":             []byte(bp),
-		"a.java":                 nil,
-		"b.java":                 nil,
-		"c.java":                 nil,
-		"b.kt":                   nil,
-		"a.jar":                  nil,
-		"b.jar":                  nil,
-		"java-res/a/a":           nil,
-		"java-res/b/b":           nil,
-		"java-res2/a":            nil,
-		"java-fg/a.java":         nil,
-		"java-fg/b.java":         nil,
-		"java-fg/c.java":         nil,
-		"api/current.txt":        nil,
-		"api/removed.txt":        nil,
-		"api/system-current.txt": nil,
-		"api/system-removed.txt": nil,
-		"api/test-current.txt":   nil,
-		"api/test-removed.txt":   nil,
-		"framework/aidl/a.aidl":  nil,
-
-		"prebuilts/ndk/current/sources/cxx-stl/llvm-libc++/libs/arm64-v8a/libc++_shared.so": nil,
-
-		"prebuilts/sdk/14/public/android.jar":         nil,
-		"prebuilts/sdk/14/public/framework.aidl":      nil,
-		"prebuilts/sdk/14/system/android.jar":         nil,
-		"prebuilts/sdk/17/public/android.jar":         nil,
-		"prebuilts/sdk/17/public/framework.aidl":      nil,
-		"prebuilts/sdk/17/system/android.jar":         nil,
-		"prebuilts/sdk/25/public/android.jar":         nil,
-		"prebuilts/sdk/25/public/framework.aidl":      nil,
-		"prebuilts/sdk/25/system/android.jar":         nil,
-		"prebuilts/sdk/current/core/android.jar":      nil,
-		"prebuilts/sdk/current/public/android.jar":    nil,
-		"prebuilts/sdk/current/public/framework.aidl": nil,
-		"prebuilts/sdk/current/public/core.jar":       nil,
-		"prebuilts/sdk/current/system/android.jar":    nil,
-		"prebuilts/sdk/current/test/android.jar":      nil,
-		"prebuilts/sdk/28/public/api/foo.txt":         nil,
-		"prebuilts/sdk/28/system/api/foo.txt":         nil,
-		"prebuilts/sdk/28/test/api/foo.txt":           nil,
-		"prebuilts/sdk/28/public/api/foo-removed.txt": nil,
-		"prebuilts/sdk/28/system/api/foo-removed.txt": nil,
-		"prebuilts/sdk/28/test/api/foo-removed.txt":   nil,
-		"prebuilts/sdk/28/public/api/bar.txt":         nil,
-		"prebuilts/sdk/28/system/api/bar.txt":         nil,
-		"prebuilts/sdk/28/test/api/bar.txt":           nil,
-		"prebuilts/sdk/28/public/api/bar-removed.txt": nil,
-		"prebuilts/sdk/28/system/api/bar-removed.txt": nil,
-		"prebuilts/sdk/28/test/api/bar-removed.txt":   nil,
-		"prebuilts/sdk/tools/core-lambda-stubs.jar":   nil,
-		"prebuilts/sdk/Android.bp":                    []byte(`prebuilt_apis { name: "sdk", api_dirs: ["14", "28", "current"],}`),
-
-		"prebuilts/apk/app.apk":        nil,
-		"prebuilts/apk/app_xhdpi.apk":  nil,
-		"prebuilts/apk/app_xxhdpi.apk": nil,
-
-		// For framework-res, which is an implicit dependency for framework
-		"AndroidManifest.xml":                        nil,
-		"build/make/target/product/security/testkey": nil,
-
-		"build/soong/scripts/jar-wrapper.sh": nil,
-
-		"build/make/core/verify_uses_libraries.sh": nil,
-
-		"build/make/core/proguard.flags":             nil,
-		"build/make/core/proguard_basic_keeps.flags": nil,
-
-		"jdk8/jre/lib/jce.jar": nil,
-		"jdk8/jre/lib/rt.jar":  nil,
-		"jdk8/lib/tools.jar":   nil,
-
-		"bar-doc/a.java":                 nil,
-		"bar-doc/b.java":                 nil,
-		"bar-doc/IFoo.aidl":              nil,
-		"bar-doc/known_oj_tags.txt":      nil,
-		"external/doclava/templates-sdk": nil,
-
-		"cert/new_cert.x509.pem": nil,
-		"cert/new_cert.pk8":      nil,
-	}
-
-	for k, v := range fs {
-		mockFS[k] = v
-	}
-
-	ctx.MockFileSystem(mockFS)
+	cc.RegisterRequiredBuildComponentsForTest(ctx)
+	ctx.RegisterModuleType("ndk_prebuilt_shared_stl", cc.NdkPrebuiltSharedStlFactory)
 
 	return ctx
 }
@@ -208,23 +92,56 @@
 func run(t *testing.T, ctx *android.TestContext, config android.Config) {
 	t.Helper()
 
-	pathCtx := android.PathContextForTesting(config, nil)
+	pathCtx := android.PathContextForTesting(config)
 	setDexpreoptTestGlobalConfig(config, dexpreopt.GlobalConfigForTests(pathCtx))
 
-	ctx.Register()
+	ctx.Register(config)
 	_, errs := ctx.ParseBlueprintsFiles("Android.bp")
 	android.FailIfErrored(t, errs)
 	_, errs = ctx.PrepareBuildActions(config)
 	android.FailIfErrored(t, errs)
 }
 
-func testJava(t *testing.T, bp string) *android.TestContext {
+func testJavaError(t *testing.T, pattern string, bp string) (*android.TestContext, android.Config) {
 	t.Helper()
-	config := testConfig(nil)
-	ctx := testContext(config, bp, nil)
+	return testJavaErrorWithConfig(t, pattern, testConfig(nil, bp, nil))
+}
+
+func testJavaErrorWithConfig(t *testing.T, pattern string, config android.Config) (*android.TestContext, android.Config) {
+	t.Helper()
+	ctx := testContext()
+
+	pathCtx := android.PathContextForTesting(config)
+	setDexpreoptTestGlobalConfig(config, dexpreopt.GlobalConfigForTests(pathCtx))
+
+	ctx.Register(config)
+	_, errs := ctx.ParseBlueprintsFiles("Android.bp")
+	if len(errs) > 0 {
+		android.FailIfNoMatchingErrors(t, pattern, errs)
+		return ctx, config
+	}
+	_, errs = ctx.PrepareBuildActions(config)
+	if len(errs) > 0 {
+		android.FailIfNoMatchingErrors(t, pattern, errs)
+		return ctx, config
+	}
+
+	t.Fatalf("missing expected error %q (0 errors are returned)", pattern)
+
+	return ctx, config
+}
+
+func testJava(t *testing.T, bp string) (*android.TestContext, android.Config) {
+	t.Helper()
+	return testJavaWithConfig(t, testConfig(nil, bp, nil))
+}
+
+func testJavaWithConfig(t *testing.T, config android.Config) (*android.TestContext, android.Config) {
+	t.Helper()
+	ctx := testContext()
 	run(t, ctx, config)
 
-	return ctx
+	return ctx, config
 }
 
 func moduleToPath(name string) string {
@@ -238,8 +155,96 @@
 	}
 }
 
+func TestJavaLinkType(t *testing.T) {
+	testJava(t, `
+		java_library {
+			name: "foo",
+			srcs: ["a.java"],
+			libs: ["bar"],
+			static_libs: ["baz"],
+		}
+
+		java_library {
+			name: "bar",
+			sdk_version: "current",
+			srcs: ["b.java"],
+		}
+
+		java_library {
+			name: "baz",
+			sdk_version: "system_current",
+			srcs: ["c.java"],
+		}
+	`)
+
+	testJavaError(t, "Adjust sdk_version: property of the source or target module so that target module is built with the same or smaller API set than the source.", `
+		java_library {
+			name: "foo",
+			srcs: ["a.java"],
+			libs: ["bar"],
+			sdk_version: "current",
+			static_libs: ["baz"],
+		}
+
+		java_library {
+			name: "bar",
+			sdk_version: "current",
+			srcs: ["b.java"],
+		}
+
+		java_library {
+			name: "baz",
+			sdk_version: "system_current",
+			srcs: ["c.java"],
+		}
+	`)
+
+	testJava(t, `
+		java_library {
+			name: "foo",
+			srcs: ["a.java"],
+			libs: ["bar"],
+			sdk_version: "system_current",
+			static_libs: ["baz"],
+		}
+
+		java_library {
+			name: "bar",
+			sdk_version: "current",
+			srcs: ["b.java"],
+		}
+
+		java_library {
+			name: "baz",
+			sdk_version: "system_current",
+			srcs: ["c.java"],
+		}
+	`)
+
+	testJavaError(t, "Adjust sdk_version: property of the source or target module so that target module is built with the same or smaller API set than the source.", `
+		java_library {
+			name: "foo",
+			srcs: ["a.java"],
+			libs: ["bar"],
+			sdk_version: "system_current",
+			static_libs: ["baz"],
+		}
+
+		java_library {
+			name: "bar",
+			sdk_version: "current",
+			srcs: ["b.java"],
+		}
+
+		java_library {
+			name: "baz",
+			srcs: ["c.java"],
+		}
+	`)
+}
+
 func TestSimple(t *testing.T) {
-	ctx := testJava(t, `
+	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
 			srcs: ["a.java"],
@@ -282,8 +287,126 @@
 	}
 }
 
+func TestExportedPlugins(t *testing.T) {
+	type Result struct {
+		library    string
+		processors string
+	}
+	var tests = []struct {
+		name    string
+		extra   string
+		results []Result
+	}{
+		{
+			name:    "Exported plugin is not a direct plugin",
+			extra:   `java_library { name: "exports", srcs: ["a.java"], exported_plugins: ["plugin"] }`,
+			results: []Result{{library: "exports", processors: "-proc:none"}},
+		},
+		{
+			name: "Exports plugin to dependee",
+			extra: `
+				java_library{name: "exports", exported_plugins: ["plugin"]}
+				java_library{name: "foo", srcs: ["a.java"], libs: ["exports"]}
+				java_library{name: "bar", srcs: ["a.java"], static_libs: ["exports"]}
+			`,
+			results: []Result{
+				{library: "foo", processors: "-processor com.android.TestPlugin"},
+				{library: "bar", processors: "-processor com.android.TestPlugin"},
+			},
+		},
+		{
+			name: "Exports plugin to android_library",
+			extra: `
+				java_library{name: "exports", exported_plugins: ["plugin"]}
+				android_library{name: "foo", srcs: ["a.java"],  libs: ["exports"]}
+				android_library{name: "bar", srcs: ["a.java"], static_libs: ["exports"]}
+			`,
+			results: []Result{
+				{library: "foo", processors: "-processor com.android.TestPlugin"},
+				{library: "bar", processors: "-processor com.android.TestPlugin"},
+			},
+		},
+		{
+			name: "Exports plugin is not propagated via transitive deps",
+			extra: `
+				java_library{name: "exports", exported_plugins: ["plugin"]}
+				java_library{name: "foo", srcs: ["a.java"], libs: ["exports"]}
+				java_library{name: "bar", srcs: ["a.java"], static_libs: ["foo"]}
+			`,
+			results: []Result{
+				{library: "foo", processors: "-processor com.android.TestPlugin"},
+				{library: "bar", processors: "-proc:none"},
+			},
+		},
+		{
+			name: "Exports plugin appends to plugins",
+			extra: `
+                java_plugin{name: "plugin2", processor_class: "com.android.TestPlugin2"}
+				java_library{name: "exports", exported_plugins: ["plugin"]}
+				java_library{name: "foo", srcs: ["a.java"], libs: ["exports"], plugins: ["plugin2"]}
+			`,
+			results: []Result{
+				{library: "foo", processors: "-processor com.android.TestPlugin,com.android.TestPlugin2"},
+			},
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			ctx, _ := testJava(t, `
+				java_plugin {
+					name: "plugin",
+					processor_class: "com.android.TestPlugin",
+				}
+			`+test.extra)
+
+			for _, want := range test.results {
+				javac := ctx.ModuleForTests(want.library, "android_common").Rule("javac")
+				if javac.Args["processor"] != want.processors {
+					t.Errorf("For library %v, expected %v, found %v", want.library, want.processors, javac.Args["processor"])
+				}
+			}
+		})
+	}
+}
+
+func TestSdkVersionByPartition(t *testing.T) {
+	testJavaError(t, "sdk_version must have a value when the module is located at vendor or product", `
+		java_library {
+			name: "foo",
+			srcs: ["a.java"],
+			vendor: true,
+		}
+	`)
+
+	testJava(t, `
+		java_library {
+			name: "bar",
+			srcs: ["b.java"],
+		}
+	`)
+
+	for _, enforce := range []bool{true, false} {
+		bp := `
+			java_library {
+				name: "foo",
+				srcs: ["a.java"],
+				product_specific: true,
+			}
+		`
+
+		config := testConfig(nil, bp, nil)
+		config.TestProductVariables.EnforceProductPartitionInterface = proptools.BoolPtr(enforce)
+		if enforce {
+			testJavaErrorWithConfig(t, "sdk_version must have a value when the module is located at vendor or product", config)
+		} else {
+			testJavaWithConfig(t, config)
+		}
+	}
+}
+
 func TestArchSpecific(t *testing.T) {
-	ctx := testJava(t, `
+	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
 			srcs: ["a.java"],
@@ -302,7 +425,7 @@
 }
 
 func TestBinary(t *testing.T) {
-	ctx := testJava(t, `
+	ctx, _ := testJava(t, `
 		java_library_host {
 			name: "foo",
 			srcs: ["a.java"],
@@ -331,10 +454,10 @@
 }
 
 func TestPrebuilts(t *testing.T) {
-	ctx := testJava(t, `
+	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
-			srcs: ["a.java"],
+			srcs: ["a.java", ":stubs-source"],
 			libs: ["bar", "sdklib"],
 			static_libs: ["baz"],
 		}
@@ -358,14 +481,35 @@
 			name: "sdklib",
 			jars: ["b.jar"],
 		}
+
+		prebuilt_stubs_sources {
+			name: "stubs-source",
+			srcs: ["stubs/sources"],
+		}
+
+		java_test_import {
+			name: "test",
+			jars: ["a.jar"],
+			test_suites: ["cts"],
+			test_config: "AndroidTest.xml",
+		}
 		`)
 
-	javac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
+	fooModule := ctx.ModuleForTests("foo", "android_common")
+	javac := fooModule.Rule("javac")
 	combineJar := ctx.ModuleForTests("foo", "android_common").Description("for javac")
 	barJar := ctx.ModuleForTests("bar", "android_common").Rule("combineJar").Output
 	bazJar := ctx.ModuleForTests("baz", "android_common").Rule("combineJar").Output
 	sdklibStubsJar := ctx.ModuleForTests("sdklib.stubs", "android_common").Rule("combineJar").Output
 
+	fooLibrary := fooModule.Module().(*Library)
+	assertDeepEquals(t, "foo java sources incorrect",
+		[]string{"a.java"}, fooLibrary.compiledJavaSrcs.Strings())
+
+	assertDeepEquals(t, "foo java source jars incorrect",
+		[]string{".intermediates/stubs-source/android_common/stubs-source-stubs.srcjar"},
+		android.NormalizePathsForTesting(fooLibrary.compiledSrcJars))
+
 	if !strings.Contains(javac.Args["classpath"], barJar.String()) {
 		t.Errorf("foo classpath %v does not contain %q", javac.Args["classpath"], barJar.String())
 	}
@@ -381,8 +525,14 @@
 	ctx.ModuleForTests("qux", "android_common").Rule("Cp")
 }
 
+func assertDeepEquals(t *testing.T, message string, expected interface{}, actual interface{}) {
+	if !reflect.DeepEqual(expected, actual) {
+		t.Errorf("%s: expected %q, found %q", message, expected, actual)
+	}
+}
+
 func TestDefaults(t *testing.T) {
-	ctx := testJava(t, `
+	ctx, _ := testJava(t, `
 		java_defaults {
 			name: "defaults",
 			srcs: ["a.java"],
@@ -528,7 +678,7 @@
 
 	for _, test := range table {
 		t.Run(test.name, func(t *testing.T) {
-			ctx := testJava(t, `
+			ctx, _ := testJava(t, `
 				java_library {
 					name: "foo",
 					srcs: [
@@ -557,7 +707,7 @@
 }
 
 func TestIncludeSrcs(t *testing.T) {
-	ctx := testJava(t, `
+	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
 			srcs: [
@@ -620,7 +770,7 @@
 }
 
 func TestGeneratedSources(t *testing.T) {
-	ctx := testJava(t, `
+	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
 			srcs: [
@@ -653,7 +803,7 @@
 }
 
 func TestTurbine(t *testing.T) {
-	ctx := testJava(t, `
+	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
 			srcs: ["a.java"],
@@ -702,7 +852,7 @@
 }
 
 func TestSharding(t *testing.T) {
-	ctx := testJava(t, `
+	ctx, _ := testJava(t, `
 		java_library {
 			name: "bar",
 			srcs: ["a.java","b.java","c.java"],
@@ -720,16 +870,22 @@
 }
 
 func TestDroiddoc(t *testing.T) {
-	ctx := testJava(t, `
-		droiddoc_template {
+	ctx, _ := testJava(t, `
+		droiddoc_exported_dir {
 		    name: "droiddoc-templates-sdk",
 		    path: ".",
 		}
+		filegroup {
+		    name: "bar-doc-aidl-srcs",
+		    srcs: ["bar-doc/IBar.aidl"],
+		    path: "bar-doc",
+		}
 		droiddoc {
 		    name: "bar-doc",
 		    srcs: [
 		        "bar-doc/*.java",
 		        "bar-doc/IFoo.aidl",
+		        ":bar-doc-aidl-srcs",
 		    ],
 		    exclude_srcs: [
 		        "bar-doc/b.java"
@@ -747,23 +903,27 @@
 		}
 		`)
 
-	stubsJar := filepath.Join(buildDir, ".intermediates", "bar-doc", "android_common", "bar-doc-stubs.srcjar")
-	barDoc := ctx.ModuleForTests("bar-doc", "android_common").Output("bar-doc-stubs.srcjar")
-	if stubsJar != barDoc.Output.String() {
-		t.Errorf("expected stubs Jar [%q], got %q", stubsJar, barDoc.Output.String())
-	}
-	inputs := ctx.ModuleForTests("bar-doc", "android_common").Rule("javadoc").Inputs
+	barDoc := ctx.ModuleForTests("bar-doc", "android_common").Rule("javadoc")
 	var javaSrcs []string
-	for _, i := range inputs {
+	for _, i := range barDoc.Inputs {
 		javaSrcs = append(javaSrcs, i.Base())
 	}
-	if len(javaSrcs) != 2 || javaSrcs[0] != "a.java" || javaSrcs[1] != "IFoo.java" {
-		t.Errorf("inputs of bar-doc must be []string{\"a.java\", \"IFoo.java\", but was %#v.", javaSrcs)
+	if len(javaSrcs) != 1 || javaSrcs[0] != "a.java" {
+		t.Errorf("inputs of bar-doc must be []string{\"a.java\"}, but was %#v.", javaSrcs)
+	}
+
+	aidl := ctx.ModuleForTests("bar-doc", "android_common").Rule("aidl")
+	if g, w := barDoc.Implicits.Strings(), aidl.Output.String(); !inList(w, g) {
+		t.Errorf("implicits of bar-doc must contain %q, but was %q.", w, g)
+	}
+
+	if g, w := aidl.Implicits.Strings(), []string{"bar-doc/IBar.aidl", "bar-doc/IFoo.aidl"}; !reflect.DeepEqual(w, g) {
+		t.Errorf("aidl inputs must be %q, but was %q", w, g)
 	}
 }
 
 func TestJarGenrules(t *testing.T) {
-	ctx := testJava(t, `
+	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
 			srcs: ["a.java"],
@@ -817,7 +977,7 @@
 }
 
 func TestExcludeFileGroupInSrcs(t *testing.T) {
-	ctx := testJava(t, `
+	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
 			srcs: ["a.java", ":foo-srcs"],
@@ -843,8 +1003,7 @@
 }
 
 func TestJavaLibrary(t *testing.T) {
-	config := testConfig(nil)
-	ctx := testContext(config, "", map[string][]byte{
+	config := testConfig(nil, "", map[string][]byte{
 		"libcore/Android.bp": []byte(`
 				java_library {
 						name: "core",
@@ -852,12 +1011,13 @@
 						system_modules: "none",
 				}`),
 	})
+	ctx := testContext()
 	run(t, ctx, config)
 }
 
 func TestJavaSdkLibrary(t *testing.T) {
-	ctx := testJava(t, `
-		droiddoc_template {
+	ctx, _ := testJava(t, `
+		droiddoc_exported_dir {
 			name: "droiddoc-templates-sdk",
 			path: ".",
 		}
@@ -1004,30 +1164,32 @@
 }
 
 func TestPatchModule(t *testing.T) {
-	bp := `
-		java_library {
-			name: "foo",
-			srcs: ["a.java"],
-		}
-
-		java_library {
-			name: "bar",
-			srcs: ["b.java"],
-			sdk_version: "none",
-			system_modules: "none",
-			patch_module: "java.base",
-		}
-
-		java_library {
-			name: "baz",
-			srcs: ["c.java"],
-			patch_module: "java.base",
-		}
-	`
-
 	t.Run("Java language level 8", func(t *testing.T) {
-		// Test default javac -source 1.8 -target 1.8
-		ctx := testJava(t, bp)
+		// Test with legacy javac -source 1.8 -target 1.8
+		bp := `
+			java_library {
+				name: "foo",
+				srcs: ["a.java"],
+				java_version: "1.8",
+			}
+
+			java_library {
+				name: "bar",
+				srcs: ["b.java"],
+				sdk_version: "none",
+				system_modules: "none",
+				patch_module: "java.base",
+				java_version: "1.8",
+			}
+
+			java_library {
+				name: "baz",
+				srcs: ["c.java"],
+				patch_module: "java.base",
+				java_version: "1.8",
+			}
+		`
+		ctx, _ := testJava(t, bp)
 
 		checkPatchModuleFlag(t, ctx, "foo", "")
 		checkPatchModuleFlag(t, ctx, "bar", "")
@@ -1035,10 +1197,28 @@
 	})
 
 	t.Run("Java language level 9", func(t *testing.T) {
-		// Test again with javac -source 9 -target 9
-		config := testConfig(map[string]string{"EXPERIMENTAL_JAVA_LANGUAGE_LEVEL_9": "true"})
-		ctx := testContext(config, bp, nil)
-		run(t, ctx, config)
+		// Test with default javac -source 9 -target 9
+		bp := `
+			java_library {
+				name: "foo",
+				srcs: ["a.java"],
+			}
+
+			java_library {
+				name: "bar",
+				srcs: ["b.java"],
+				sdk_version: "none",
+				system_modules: "none",
+				patch_module: "java.base",
+			}
+
+			java_library {
+				name: "baz",
+				srcs: ["c.java"],
+				patch_module: "java.base",
+			}
+		`
+		ctx, _ := testJava(t, bp)
 
 		checkPatchModuleFlag(t, ctx, "foo", "")
 		expected := "java.base=.:" + buildDir
diff --git a/java/jdeps.go b/java/jdeps.go
index fccc40f..49e3de3 100644
--- a/java/jdeps.go
+++ b/java/jdeps.go
@@ -17,7 +17,6 @@
 import (
 	"encoding/json"
 	"fmt"
-	"os"
 
 	"android/soong/android"
 )
@@ -92,23 +91,21 @@
 		moduleInfos[name] = dpInfo
 	})
 
-	jfpath := android.PathForOutput(ctx, jdepsJsonFileName).String()
+	jfpath := android.PathForOutput(ctx, jdepsJsonFileName)
 	err := createJsonFile(moduleInfos, jfpath)
 	if err != nil {
 		ctx.Errorf(err.Error())
 	}
 }
 
-func createJsonFile(moduleInfos map[string]android.IdeInfo, jfpath string) error {
-	file, err := os.Create(jfpath)
-	if err != nil {
-		return fmt.Errorf("Failed to create file: %s, relative: %v", jdepsJsonFileName, err)
-	}
-	defer file.Close()
+func createJsonFile(moduleInfos map[string]android.IdeInfo, jfpath android.WritablePath) error {
 	buf, err := json.MarshalIndent(moduleInfos, "", "\t")
 	if err != nil {
-		return fmt.Errorf("Write file failed: %s, relative: %v", jdepsJsonFileName, err)
+		return fmt.Errorf("JSON marshal of java deps failed: %s", err)
 	}
-	fmt.Fprintf(file, string(buf))
+	err = android.WriteFileToOutputDir(jfpath, buf, 0666)
+	if err != nil {
+		return fmt.Errorf("Writing java deps to %s failed: %s", jfpath.String(), err)
+	}
 	return nil
 }
diff --git a/java/kotlin.go b/java/kotlin.go
index 8306907..5319a4f 100644
--- a/java/kotlin.go
+++ b/java/kotlin.go
@@ -26,7 +26,7 @@
 	"github.com/google/blueprint"
 )
 
-var kotlinc = pctx.AndroidGomaStaticRule("kotlinc",
+var kotlinc = pctx.AndroidRemoteStaticRule("kotlinc", android.SUPPORTS_GOMA,
 	blueprint.RuleParams{
 		Command: `rm -rf "$classesDir" "$srcJarDir" "$kotlinBuildFile" "$emptyDir" && ` +
 			`mkdir -p "$classesDir" "$srcJarDir" "$emptyDir" && ` +
@@ -88,7 +88,7 @@
 	})
 }
 
-var kapt = pctx.AndroidGomaStaticRule("kapt",
+var kapt = pctx.AndroidRemoteStaticRule("kapt", android.SUPPORTS_GOMA,
 	blueprint.RuleParams{
 		Command: `rm -rf "$srcJarDir" "$kotlinBuildFile" "$kaptDir" && mkdir -p "$srcJarDir" "$kaptDir" && ` +
 			`${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
@@ -133,7 +133,7 @@
 	deps = append(deps, srcJars...)
 	deps = append(deps, flags.processorPath...)
 
-	kaptProcessorPath := flags.processorPath.FormTurbineClasspath("-P plugin:org.jetbrains.kotlin.kapt3:apclasspath=")
+	kaptProcessorPath := flags.processorPath.FormRepeatedClassPath("-P plugin:org.jetbrains.kotlin.kapt3:apclasspath=")
 
 	kaptProcessor := ""
 	if flags.processor != "" {
@@ -141,8 +141,8 @@
 	}
 
 	encodedJavacFlags := kaptEncodeFlags([][2]string{
-		{"-source", flags.javaVersion},
-		{"-target", flags.javaVersion},
+		{"-source", flags.javaVersion.String()},
+		{"-target", flags.javaVersion.String()},
 	})
 
 	kotlinName := filepath.Join(ctx.ModuleDir(), ctx.ModuleSubDir(), ctx.ModuleName())
diff --git a/java/kotlin_test.go b/java/kotlin_test.go
index e0eb0c0..5c6d45f 100644
--- a/java/kotlin_test.go
+++ b/java/kotlin_test.go
@@ -22,7 +22,7 @@
 )
 
 func TestKotlin(t *testing.T) {
-	ctx := testJava(t, `
+	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
 			srcs: ["a.java", "b.kt"],
@@ -84,7 +84,7 @@
 }
 
 func TestKapt(t *testing.T) {
-	ctx := testJava(t, `
+	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
 			srcs: ["a.java", "b.kt"],
diff --git a/java/platform_compat_config.go b/java/platform_compat_config.go
new file mode 100644
index 0000000..d5c7579
--- /dev/null
+++ b/java/platform_compat_config.go
@@ -0,0 +1,132 @@
+// 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 java
+
+import (
+	"android/soong/android"
+)
+
+func init() {
+	android.RegisterSingletonType("platform_compat_config_singleton", platformCompatConfigSingletonFactory)
+	android.RegisterModuleType("platform_compat_config", platformCompatConfigFactory)
+}
+
+type platformCompatConfigSingleton struct {
+	metadata android.Path
+}
+
+type platformCompatConfigProperties struct {
+	Src *string `android:"path"`
+}
+
+type platformCompatConfig struct {
+	android.ModuleBase
+
+	properties     platformCompatConfigProperties
+	installDirPath android.InstallPath
+	configFile     android.OutputPath
+	metadataFile   android.OutputPath
+}
+
+func (p *platformCompatConfig) compatConfigMetadata() android.OutputPath {
+	return p.metadataFile
+}
+
+type platformCompatConfigIntf interface {
+	compatConfigMetadata() android.OutputPath
+}
+
+var _ platformCompatConfigIntf = (*platformCompatConfig)(nil)
+
+// compat singleton rules
+func (p *platformCompatConfigSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+
+	var compatConfigMetadata android.Paths
+
+	ctx.VisitAllModules(func(module android.Module) {
+		if c, ok := module.(platformCompatConfigIntf); ok {
+			metadata := c.compatConfigMetadata()
+			compatConfigMetadata = append(compatConfigMetadata, metadata)
+		}
+	})
+
+	if compatConfigMetadata == nil {
+		// nothing to do.
+		return
+	}
+
+	rule := android.NewRuleBuilder()
+	outputPath := android.PathForOutput(ctx, "compat_config", "merged_compat_config.xml")
+
+	rule.Command().
+		BuiltTool(ctx, "process-compat-config").
+		FlagForEachInput("--xml ", compatConfigMetadata).
+		FlagWithOutput("--merged-config ", outputPath)
+
+	rule.Build(pctx, ctx, "merged-compat-config", "Merge compat config")
+
+	p.metadata = outputPath
+}
+
+func (p *platformCompatConfigSingleton) MakeVars(ctx android.MakeVarsContext) {
+	if p.metadata != nil {
+		ctx.Strict("INTERNAL_PLATFORM_MERGED_COMPAT_CONFIG", p.metadata.String())
+	}
+}
+
+func (p *platformCompatConfig) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	rule := android.NewRuleBuilder()
+
+	configFileName := p.Name() + ".xml"
+	metadataFileName := p.Name() + "_meta.xml"
+	p.configFile = android.PathForModuleOut(ctx, configFileName).OutputPath
+	p.metadataFile = android.PathForModuleOut(ctx, metadataFileName).OutputPath
+	path := android.PathForModuleSrc(ctx, String(p.properties.Src))
+
+	rule.Command().
+		BuiltTool(ctx, "process-compat-config").
+		FlagWithInput("--jar ", path).
+		FlagWithOutput("--device-config ", p.configFile).
+		FlagWithOutput("--merged-config ", p.metadataFile)
+
+	p.installDirPath = android.PathForModuleInstall(ctx, "etc", "compatconfig")
+	rule.Build(pctx, ctx, configFileName, "Extract compat/compat_config.xml and install it")
+
+}
+
+func (p *platformCompatConfig) AndroidMkEntries() []android.AndroidMkEntries {
+	return []android.AndroidMkEntries{android.AndroidMkEntries{
+		Class:      "ETC",
+		OutputFile: android.OptionalPathForPath(p.configFile),
+		Include:    "$(BUILD_PREBUILT)",
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(entries *android.AndroidMkEntries) {
+				entries.SetString("LOCAL_MODULE_PATH", p.installDirPath.ToMakePath().String())
+				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.configFile.Base())
+			},
+		},
+	}}
+}
+
+func platformCompatConfigSingletonFactory() android.Singleton {
+	return &platformCompatConfigSingleton{}
+}
+
+func platformCompatConfigFactory() android.Module {
+	module := &platformCompatConfig{}
+	module.AddProperties(&module.properties)
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+	return module
+}
diff --git a/java/plugin_test.go b/java/plugin_test.go
index d1aef2c..c7913d3 100644
--- a/java/plugin_test.go
+++ b/java/plugin_test.go
@@ -20,7 +20,7 @@
 )
 
 func TestNoPlugin(t *testing.T) {
-	ctx := testJava(t, `
+	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
 			srcs: ["a.java"],
@@ -44,7 +44,7 @@
 }
 
 func TestPlugin(t *testing.T) {
-	ctx := testJava(t, `
+	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
 			srcs: ["a.java"],
@@ -83,7 +83,7 @@
 }
 
 func TestPluginGeneratesApi(t *testing.T) {
-	ctx := testJava(t, `
+	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
 			srcs: ["a.java"],
diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go
index c370811..cb17fee 100644
--- a/java/prebuilt_apis.go
+++ b/java/prebuilt_apis.go
@@ -23,9 +23,13 @@
 )
 
 func init() {
-	android.RegisterModuleType("prebuilt_apis", PrebuiltApisFactory)
+	RegisterPrebuiltApisBuildComponents(android.InitRegistrationContext)
+}
 
-	android.PreArchMutators(func(ctx android.RegisterMutatorsContext) {
+func RegisterPrebuiltApisBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("prebuilt_apis", PrebuiltApisFactory)
+
+	ctx.PreArchMutators(func(ctx android.RegisterMutatorsContext) {
 		ctx.TopDown("prebuilt_apis", PrebuiltApisMutator).Parallel()
 	})
 }
@@ -82,7 +86,7 @@
 	props.Sdk_version = proptools.StringPtr("current")
 	props.Installable = proptools.BoolPtr(false)
 
-	mctx.CreateModule(android.ModuleFactoryAdaptor(ImportFactory), &props)
+	mctx.CreateModule(ImportFactory, &props)
 }
 
 func createFilegroup(mctx android.TopDownMutatorContext, module string, scope string, apiver string, path string) {
@@ -93,7 +97,7 @@
 	}{}
 	filegroupProps.Name = proptools.StringPtr(fgName)
 	filegroupProps.Srcs = []string{path}
-	mctx.CreateModule(android.ModuleFactoryAdaptor(android.FileGroupFactory), &filegroupProps)
+	mctx.CreateModule(android.FileGroupFactory, &filegroupProps)
 }
 
 func getPrebuiltFiles(mctx android.TopDownMutatorContext, name string) []string {
diff --git a/java/proto.go b/java/proto.go
index 37de1d2..4d735eb 100644
--- a/java/proto.go
+++ b/java/proto.go
@@ -15,36 +15,61 @@
 package java
 
 import (
+	"path/filepath"
+	"strconv"
+
 	"android/soong/android"
 )
 
-func genProto(ctx android.ModuleContext, protoFile android.Path, flags android.ProtoFlags) android.Path {
-	srcJarFile := android.GenPathWithExt(ctx, "proto", protoFile, "srcjar")
+func genProto(ctx android.ModuleContext, protoFiles android.Paths, flags android.ProtoFlags) android.Paths {
+	// Shard proto files into groups of 100 to avoid having to recompile all of them if one changes and to avoid
+	// hitting command line length limits.
+	shards := android.ShardPaths(protoFiles, 100)
 
-	outDir := srcJarFile.ReplaceExtension(ctx, "tmp")
-	depFile := srcJarFile.ReplaceExtension(ctx, "srcjar.d")
+	srcJarFiles := make(android.Paths, 0, len(shards))
 
-	rule := android.NewRuleBuilder()
+	for i, shard := range shards {
+		srcJarFile := android.PathForModuleGen(ctx, "proto", "proto"+strconv.Itoa(i)+".srcjar")
+		srcJarFiles = append(srcJarFiles, srcJarFile)
 
-	rule.Command().Text("rm -rf").Flag(outDir.String())
-	rule.Command().Text("mkdir -p").Flag(outDir.String())
+		outDir := srcJarFile.ReplaceExtension(ctx, "tmp")
 
-	android.ProtoRule(ctx, rule, protoFile, flags, flags.Deps, outDir, depFile, nil)
+		rule := android.NewRuleBuilder()
 
-	// Proto generated java files have an unknown package name in the path, so package the entire output directory
-	// into a srcjar.
-	rule.Command().
-		Tool(ctx.Config().HostToolPath(ctx, "soong_zip")).
-		Flag("-jar").
-		FlagWithOutput("-o ", srcJarFile).
-		FlagWithArg("-C ", outDir.String()).
-		FlagWithArg("-D ", outDir.String())
+		rule.Command().Text("rm -rf").Flag(outDir.String())
+		rule.Command().Text("mkdir -p").Flag(outDir.String())
 
-	rule.Command().Text("rm -rf").Flag(outDir.String())
+		for _, protoFile := range shard {
+			depFile := srcJarFile.InSameDir(ctx, protoFile.String()+".d")
+			rule.Command().Text("mkdir -p").Flag(filepath.Dir(depFile.String()))
+			android.ProtoRule(ctx, rule, protoFile, flags, flags.Deps, outDir, depFile, nil)
+		}
 
-	rule.Build(pctx, ctx, "protoc_"+protoFile.Rel(), "protoc "+protoFile.Rel())
+		// Proto generated java files have an unknown package name in the path, so package the entire output directory
+		// into a srcjar.
+		rule.Command().
+			BuiltTool(ctx, "soong_zip").
+			Flag("-jar").
+			Flag("-write_if_changed").
+			FlagWithOutput("-o ", srcJarFile).
+			FlagWithArg("-C ", outDir.String()).
+			FlagWithArg("-D ", outDir.String())
 
-	return srcJarFile
+		rule.Command().Text("rm -rf").Flag(outDir.String())
+
+		rule.Restat()
+
+		ruleName := "protoc"
+		ruleDesc := "protoc"
+		if len(shards) > 1 {
+			ruleName += "_" + strconv.Itoa(i)
+			ruleDesc += " " + strconv.Itoa(i)
+		}
+
+		rule.Build(pctx, ctx, ruleName, ruleDesc)
+	}
+
+	return srcJarFiles
 }
 
 func protoDeps(ctx android.BottomUpMutatorContext, p *android.ProtoProperties) {
@@ -75,20 +100,29 @@
 	flags.proto = android.GetProtoFlags(ctx, p)
 
 	if String(p.Proto.Plugin) == "" {
+		var typeToPlugin string
 		switch String(p.Proto.Type) {
 		case "micro":
 			flags.proto.OutTypeFlag = "--javamicro_out"
+			typeToPlugin = "javamicro"
 		case "nano":
 			flags.proto.OutTypeFlag = "--javanano_out"
-		case "lite":
+			typeToPlugin = "javanano"
+		case "lite", "":
 			flags.proto.OutTypeFlag = "--java_out"
 			flags.proto.OutParams = append(flags.proto.OutParams, "lite")
-		case "full", "":
+		case "full":
 			flags.proto.OutTypeFlag = "--java_out"
 		default:
 			ctx.PropertyErrorf("proto.type", "unknown proto type %q",
 				String(p.Proto.Type))
 		}
+
+		if typeToPlugin != "" {
+			hostTool := ctx.Config().HostToolPath(ctx, "protoc-gen-"+typeToPlugin)
+			flags.proto.Deps = append(flags.proto.Deps, hostTool)
+			flags.proto.Flags = append(flags.proto.Flags, "--plugin=protoc-gen-"+typeToPlugin+"="+hostTool.String())
+		}
 	}
 
 	flags.proto.OutParams = append(flags.proto.OutParams, j.Proto.Output_params...)
diff --git a/java/robolectric.go b/java/robolectric.go
index 1de56a5..3195615 100644
--- a/java/robolectric.go
+++ b/java/robolectric.go
@@ -34,10 +34,17 @@
 	"truth-prebuilt",
 }
 
+var (
+	roboCoverageLibsTag = dependencyTag{name: "roboSrcs"}
+)
+
 type robolectricProperties struct {
 	// The name of the android_app module that the tests will run against.
 	Instrumentation_for *string
 
+	// Additional libraries for which coverage data should be generated
+	Coverage_libs []string
+
 	Test_options struct {
 		// Timeout in seconds when running the tests.
 		Timeout *int64
@@ -54,6 +61,8 @@
 
 	libs  []string
 	tests []string
+
+	roboSrcJar android.Path
 }
 
 func (r *robolectricTest) DepsMutator(ctx android.BottomUpMutatorContext) {
@@ -66,13 +75,37 @@
 	}
 
 	ctx.AddVariationDependencies(nil, libTag, robolectricDefaultLibs...)
+
+	ctx.AddVariationDependencies(nil, roboCoverageLibsTag, r.robolectricProperties.Coverage_libs...)
 }
 
 func (r *robolectricTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	roboTestConfig := android.PathForModuleGen(ctx, "robolectric").
+		Join(ctx, "com/android/tools/test_config.properties")
+
+	// TODO: this inserts paths to built files into the test, it should really be inserting the contents.
+	instrumented := ctx.GetDirectDepsWithTag(instrumentationForTag)
+
+	if len(instrumented) != 1 {
+		panic(fmt.Errorf("expected exactly 1 instrumented dependency, got %d", len(instrumented)))
+	}
+
+	instrumentedApp, ok := instrumented[0].(*AndroidApp)
+	if !ok {
+		ctx.PropertyErrorf("instrumentation_for", "dependency must be an android_app")
+	}
+
+	generateRoboTestConfig(ctx, roboTestConfig, instrumentedApp)
+	r.extraResources = android.Paths{roboTestConfig}
+
 	r.Library.GenerateAndroidBuildActions(ctx)
 
+	roboSrcJar := android.PathForModuleGen(ctx, "robolectric", ctx.ModuleName()+".srcjar")
+	r.generateRoboSrcJar(ctx, roboSrcJar, instrumentedApp)
+	r.roboSrcJar = roboSrcJar
+
 	for _, dep := range ctx.GetDirectDepsWithTag(libTag) {
-		r.libs = append(r.libs, ctx.OtherModuleName(dep))
+		r.libs = append(r.libs, dep.(Dependency).BaseModuleName())
 	}
 
 	// TODO: this could all be removed if tradefed was used as the test runner, it will find everything
@@ -90,51 +123,70 @@
 	}
 }
 
-func shardTests(paths []string, shards int) [][]string {
-	if shards > len(paths) {
-		shards = len(paths)
-	}
-	if shards == 0 {
-		return nil
-	}
-	ret := make([][]string, 0, shards)
-	shardSize := (len(paths) + shards - 1) / shards
-	for len(paths) > shardSize {
-		ret = append(ret, paths[0:shardSize])
-		paths = paths[shardSize:]
-	}
-	if len(paths) > 0 {
-		ret = append(ret, paths)
-	}
-	return ret
+func generateRoboTestConfig(ctx android.ModuleContext, outputFile android.WritablePath, instrumentedApp *AndroidApp) {
+	manifest := instrumentedApp.mergedManifestFile
+	resourceApk := instrumentedApp.outputFile
+
+	rule := android.NewRuleBuilder()
+
+	rule.Command().Text("rm -f").Output(outputFile)
+	rule.Command().
+		Textf(`echo "android_merged_manifest=%s" >>`, manifest.String()).Output(outputFile).Text("&&").
+		Textf(`echo "android_resource_apk=%s" >>`, resourceApk.String()).Output(outputFile).
+		// Make it depend on the files to which it points so the test file's timestamp is updated whenever the
+		// contents change
+		Implicit(manifest).
+		Implicit(resourceApk)
+
+	rule.Build(pctx, ctx, "generate_test_config", "generate test_config.properties")
 }
 
-func (r *robolectricTest) AndroidMk() android.AndroidMkData {
-	data := r.Library.AndroidMk()
+func (r *robolectricTest) generateRoboSrcJar(ctx android.ModuleContext, outputFile android.WritablePath,
+	instrumentedApp *AndroidApp) {
 
-	data.Custom = func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
-		android.WriteAndroidMkData(w, data)
+	srcJarArgs := copyOf(instrumentedApp.srcJarArgs)
+	srcJarDeps := append(android.Paths(nil), instrumentedApp.srcJarDeps...)
 
-		if s := r.robolectricProperties.Test_options.Shards; s != nil && *s > 1 {
-			shards := shardTests(r.tests, int(*s))
-			for i, shard := range shards {
-				r.writeTestRunner(w, name, "Run"+name+strconv.Itoa(i), shard)
-			}
-
-			// TODO: add rules to dist the outputs of the individual tests, or combine them together?
-			fmt.Fprintln(w, "")
-			fmt.Fprintln(w, ".PHONY:", "Run"+name)
-			fmt.Fprintln(w, "Run"+name, ": \\")
-			for i := range shards {
-				fmt.Fprintln(w, "   ", "Run"+name+strconv.Itoa(i), "\\")
-			}
-			fmt.Fprintln(w, "")
-		} else {
-			r.writeTestRunner(w, name, "Run"+name, r.tests)
+	for _, m := range ctx.GetDirectDepsWithTag(roboCoverageLibsTag) {
+		if dep, ok := m.(Dependency); ok {
+			depSrcJarArgs, depSrcJarDeps := dep.SrcJarArgs()
+			srcJarArgs = append(srcJarArgs, depSrcJarArgs...)
+			srcJarDeps = append(srcJarDeps, depSrcJarDeps...)
 		}
 	}
 
-	return data
+	TransformResourcesToJar(ctx, outputFile, srcJarArgs, srcJarDeps)
+}
+
+func (r *robolectricTest) AndroidMkEntries() []android.AndroidMkEntries {
+	entriesList := r.Library.AndroidMkEntries()
+	entries := &entriesList[0]
+
+	entries.ExtraFooters = []android.AndroidMkExtraFootersFunc{
+		func(w io.Writer, name, prefix, moduleDir string, entries *android.AndroidMkEntries) {
+			if s := r.robolectricProperties.Test_options.Shards; s != nil && *s > 1 {
+				numShards := int(*s)
+				shardSize := (len(r.tests) + numShards - 1) / numShards
+				shards := android.ShardStrings(r.tests, shardSize)
+				for i, shard := range shards {
+					r.writeTestRunner(w, name, "Run"+name+strconv.Itoa(i), shard)
+				}
+
+				// TODO: add rules to dist the outputs of the individual tests, or combine them together?
+				fmt.Fprintln(w, "")
+				fmt.Fprintln(w, ".PHONY:", "Run"+name)
+				fmt.Fprintln(w, "Run"+name, ": \\")
+				for i := range shards {
+					fmt.Fprintln(w, "   ", "Run"+name+strconv.Itoa(i), "\\")
+				}
+				fmt.Fprintln(w, "")
+			} else {
+				r.writeTestRunner(w, name, "Run"+name, r.tests)
+			}
+		},
+	}
+
+	return entriesList
 }
 
 func (r *robolectricTest) writeTestRunner(w io.Writer, module, name string, tests []string) {
@@ -144,6 +196,7 @@
 	fmt.Fprintln(w, "LOCAL_JAVA_LIBRARIES :=", module)
 	fmt.Fprintln(w, "LOCAL_JAVA_LIBRARIES += ", strings.Join(r.libs, " "))
 	fmt.Fprintln(w, "LOCAL_TEST_PACKAGE :=", String(r.robolectricProperties.Instrumentation_for))
+	fmt.Fprintln(w, "LOCAL_INSTRUMENT_SRCJARS :=", r.roboSrcJar.String())
 	fmt.Fprintln(w, "LOCAL_ROBOTEST_FILES :=", strings.Join(tests, " "))
 	if t := r.robolectricProperties.Test_options.Timeout; t != nil {
 		fmt.Fprintln(w, "LOCAL_ROBOTEST_TIMEOUT :=", *t)
@@ -164,6 +217,7 @@
 
 	module.AddProperties(
 		&module.Module.properties,
+		&module.Module.deviceProperties,
 		&module.Module.protoProperties,
 		&module.robolectricProperties)
 
diff --git a/java/robolectric_test.go b/java/robolectric_test.go
deleted file mode 100644
index e89c6e7..0000000
--- a/java/robolectric_test.go
+++ /dev/null
@@ -1,88 +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 java
-
-import (
-	"reflect"
-	"testing"
-)
-
-func Test_shardTests(t *testing.T) {
-	type args struct {
-		paths  []string
-		shards int
-	}
-	tests := []struct {
-		name string
-		args args
-		want [][]string
-	}{
-		{
-			name: "empty",
-			args: args{
-				paths:  nil,
-				shards: 1,
-			},
-			want: [][]string(nil),
-		},
-		{
-			name: "too many shards",
-			args: args{
-				paths:  []string{"a", "b"},
-				shards: 3,
-			},
-			want: [][]string{{"a"}, {"b"}},
-		},
-		{
-			name: "single shard",
-			args: args{
-				paths:  []string{"a", "b"},
-				shards: 1,
-			},
-			want: [][]string{{"a", "b"}},
-		},
-		{
-			name: "shard per input",
-			args: args{
-				paths:  []string{"a", "b", "c"},
-				shards: 3,
-			},
-			want: [][]string{{"a"}, {"b"}, {"c"}},
-		},
-		{
-			name: "balanced shards",
-			args: args{
-				paths:  []string{"a", "b", "c", "d"},
-				shards: 2,
-			},
-			want: [][]string{{"a", "b"}, {"c", "d"}},
-		},
-		{
-			name: "unbalanced shards",
-			args: args{
-				paths:  []string{"a", "b", "c"},
-				shards: 2,
-			},
-			want: [][]string{{"a", "b"}, {"c"}},
-		},
-	}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			if got := shardTests(tt.args.paths, tt.args.shards); !reflect.DeepEqual(got, tt.want) {
-				t.Errorf("shardTests() = %v, want %v", got, tt.want)
-			}
-		})
-	}
-}
diff --git a/java/sdk.go b/java/sdk.go
index 6ffe399..66eb284 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -19,7 +19,6 @@
 	"android/soong/java/config"
 	"fmt"
 	"path/filepath"
-	"runtime"
 	"sort"
 	"strconv"
 	"strings"
@@ -40,13 +39,12 @@
 type sdkContext interface {
 	// sdkVersion returns the sdk_version property of the current module, or an empty string if it is not set.
 	sdkVersion() string
+	// systemModules returns the system_modules property of the current module, or an empty string if it is not set.
+	systemModules() string
 	// minSdkVersion returns the min_sdk_version property of the current module, or sdkVersion() if it is not set.
 	minSdkVersion() string
 	// targetSdkVersion returns the target_sdk_version property of the current module, or sdkVersion() if it is not set.
 	targetSdkVersion() string
-
-	// Temporarily provide access to the no_frameworks_libs property (where present).
-	noFrameworkLibs() bool
 }
 
 func sdkVersionOrDefault(ctx android.BaseModuleContext, v string) string {
@@ -60,7 +58,7 @@
 
 // Returns a sdk version as a number.  For modules targeting an unreleased SDK (meaning it does not yet have a number)
 // it returns android.FutureApiLevel (10000).
-func sdkVersionToNumber(ctx android.BaseModuleContext, v string) (int, error) {
+func sdkVersionToNumber(ctx android.EarlyModuleContext, v string) (int, error) {
 	switch v {
 	case "", "none", "current", "test_current", "system_current", "core_current", "core_platform":
 		return ctx.Config().DefaultAppTargetSdkInt(), nil
@@ -74,7 +72,7 @@
 	}
 }
 
-func sdkVersionToNumberAsString(ctx android.BaseModuleContext, v string) (string, error) {
+func sdkVersionToNumberAsString(ctx android.EarlyModuleContext, v string) (string, error) {
 	n, err := sdkVersionToNumber(ctx, v)
 	if err != nil {
 		return "", err
@@ -82,8 +80,9 @@
 	return strconv.Itoa(n), nil
 }
 
-func decodeSdkDep(ctx android.BaseModuleContext, sdkContext sdkContext) sdkDep {
+func decodeSdkDep(ctx android.EarlyModuleContext, sdkContext sdkContext) sdkDep {
 	v := sdkContext.sdkVersion()
+
 	// For PDK builds, use the latest SDK version instead of "current"
 	if ctx.Config().IsPdkBuild() && (v == "" || v == "current") {
 		sdkVersions := ctx.Config().Get(sdkVersionsKey).([]int)
@@ -123,17 +122,17 @@
 		if (!jarPath.Valid() || !aidlPath.Valid()) && ctx.Config().AllowMissingDependencies() {
 			return sdkDep{
 				invalidVersion: true,
-				modules:        []string{fmt.Sprintf("sdk_%s_%s_android", api, v)},
+				bootclasspath:  []string{fmt.Sprintf("sdk_%s_%s_android", api, v)},
 			}
 		}
 
 		if !jarPath.Valid() {
-			ctx.PropertyErrorf("sdk_version", "invalid sdk version %q, %q does not exist", v, jar)
+			ctx.PropertyErrorf("sdk_version", "invalid sdk version %q, %q does not exist", sdk, jar)
 			return sdkDep{}
 		}
 
 		if !aidlPath.Valid() {
-			ctx.PropertyErrorf("sdk_version", "invalid sdk version %q, %q does not exist", v, aidl)
+			ctx.PropertyErrorf("sdk_version", "invalid sdk version %q, %q does not exist", sdk, aidl)
 			return sdkDep{}
 		}
 
@@ -141,30 +140,18 @@
 			useFiles: true,
 			jars:     android.Paths{jarPath.Path(), lambdaStubsPath},
 			aidl:     android.OptionalPathForPath(aidlPath.Path()),
-
-			// Pass value straight through for now to match previous behavior.
-			noFrameworksLibs: sdkContext.noFrameworkLibs(),
 		}
 	}
 
 	toModule := func(m, r string, aidl android.Path) sdkDep {
-		ret := sdkDep{
+		return sdkDep{
 			useModule:          true,
-			modules:            []string{m, config.DefaultLambdaStubsLibrary},
-			systemModules:      m + "_system_modules",
+			bootclasspath:      []string{m, config.DefaultLambdaStubsLibrary},
+			systemModules:      "core-current-stubs-system-modules",
+			java9Classpath:     []string{m},
 			frameworkResModule: r,
 			aidl:               android.OptionalPathForPath(aidl),
-
-			// Pass value straight through for now to match previous behavior.
-			noFrameworksLibs: sdkContext.noFrameworkLibs(),
 		}
-
-		if m == "core.current.stubs" {
-			ret.systemModules = "core-current-stubs-system-modules"
-		} else if m == "core.platform.api.stubs" {
-			ret.systemModules = "core-platform-api-stubs-system-modules"
-		}
-		return ret
 	}
 
 	// Ensures that the specificed system SDK version is one of BOARD_SYSTEMSDK_VERSIONS (for vendor apks)
@@ -192,13 +179,23 @@
 		return sdkDep{
 			useDefaultLibs:     true,
 			frameworkResModule: "framework-res",
-
-			// Pass value straight through for now to match previous behavior.
-			noFrameworksLibs: sdkContext.noFrameworkLibs(),
 		}
 	case "none":
+		systemModules := sdkContext.systemModules()
+		if systemModules == "" {
+			ctx.PropertyErrorf("sdk_version",
+				`system_modules is required to be set to a non-empty value when sdk_version is "none", did you mean sdk_version: "core_platform"?`)
+		} else if systemModules == "none" {
+			return sdkDep{
+				noStandardLibs: true,
+			}
+		}
+
 		return sdkDep{
+			useModule:      true,
 			noStandardLibs: true,
+			systemModules:  systemModules,
+			bootclasspath:  []string{systemModules},
 		}
 	case "core_platform":
 		return sdkDep{
@@ -307,7 +304,7 @@
 			rule.Command().
 				Text("rm -f").Output(aidl)
 			rule.Command().
-				Tool(ctx.Config().HostToolPath(ctx, "sdkparcelables")).
+				BuiltTool(ctx, "sdkparcelables").
 				Input(jar).
 				Output(aidl)
 
@@ -360,15 +357,7 @@
 
 		cmd.Text("cat").
 			Inputs(android.PathsForSource(ctx, in)).
-			Text("|")
-
-		if runtime.GOOS == "darwin" {
-			cmd.Text("md5")
-		} else {
-			cmd.Text("md5sum")
-		}
-
-		cmd.Text("| cut -d' ' -f1 >").
+			Text("| md5sum | cut -d' ' -f1 >").
 			Output(out)
 	} else {
 		// Unbundled build
diff --git a/java/sdk_library.go b/java/sdk_library.go
index b4a3f29..9b30e2c 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -16,7 +16,6 @@
 
 import (
 	"android/soong/android"
-	"android/soong/genrule"
 	"fmt"
 	"io"
 	"path"
@@ -25,23 +24,35 @@
 	"strings"
 	"sync"
 
-	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
 
-var (
+const (
 	sdkStubsLibrarySuffix = ".stubs"
 	sdkSystemApiSuffix    = ".system"
 	sdkTestApiSuffix      = ".test"
 	sdkDocsSuffix         = ".docs"
 	sdkXmlFileSuffix      = ".xml"
+	permissionsTemplate   = `<?xml version="1.0" encoding="utf-8"?>\n` +
+		`<!-- Copyright (C) 2018 The Android Open Source Project\n` +
+		`\n` +
+		`    Licensed under the Apache License, Version 2.0 (the "License");\n` +
+		`    you may not use this file except in compliance with the License.\n` +
+		`    You may obtain a copy of the License at\n` +
+		`\n` +
+		`        http://www.apache.org/licenses/LICENSE-2.0\n` +
+		`\n` +
+		`    Unless required by applicable law or agreed to in writing, software\n` +
+		`    distributed under the License is distributed on an "AS IS" BASIS,\n` +
+		`    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n` +
+		`    See the License for the specific language governing permissions and\n` +
+		`    limitations under the License.\n` +
+		`-->\n` +
+		`<permissions>\n` +
+		`    <library name="%s" file="%s"/>\n` +
+		`</permissions>\n`
 )
 
-type stubsLibraryDependencyTag struct {
-	blueprint.BaseDependencyTag
-	name string
-}
-
 var (
 	publicApiStubsTag = dependencyTag{name: "public"}
 	systemApiStubsTag = dependencyTag{name: "system"}
@@ -63,19 +74,12 @@
 	javaSdkLibrariesLock sync.Mutex
 )
 
-// java_sdk_library is to make a Java library that implements optional platform APIs to apps.
-// It is actually a wrapper of several modules: 1) stubs library that clients are linked against
-// to, 2) droiddoc module that internally generates API stubs source files, 3) the real runtime
-// shared library that implements the APIs, and 4) XML file for adding the runtime lib to the
-// classpath at runtime if requested via <uses-library>.
-//
 // TODO: these are big features that are currently missing
 // 1) disallowing linking to the runtime shared lib
 // 2) HTML generation
 
 func init() {
-	android.RegisterModuleType("java_sdk_library", SdkLibraryFactory)
-	android.RegisterModuleType("java_sdk_library_import", sdkLibraryImportFactory)
+	RegisterSdkLibraryBuildComponents(android.InitRegistrationContext)
 
 	android.RegisterMakeVarsProvider(pctx, func(ctx android.MakeVarsContext) {
 		javaSdkLibraries := javaSdkLibraries(ctx.Config())
@@ -84,19 +88,31 @@
 	})
 }
 
-type sdkLibraryProperties struct {
-	// list of optional source files that are part of API but not part of runtime library.
-	Api_srcs []string `android:"arch_variant"`
+func RegisterSdkLibraryBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("java_sdk_library", SdkLibraryFactory)
+	ctx.RegisterModuleType("java_sdk_library_import", sdkLibraryImportFactory)
+}
 
+type sdkLibraryProperties struct {
 	// List of Java libraries that will be in the classpath when building stubs
 	Stub_only_libs []string `android:"arch_variant"`
 
-	// list of package names that will be documented and publicized as API
+	// list of package names that will be documented and publicized as API.
+	// This allows the API to be restricted to a subset of the source files provided.
+	// If this is unspecified then all the source files will be treated as being part
+	// of the API.
 	Api_packages []string
 
 	// list of package names that must be hidden from the API
 	Hidden_api_packages []string
 
+	// the relative path to the directory containing the api specification files.
+	// Defaults to "api".
+	Api_dir *string
+
+	// If set to true there is no runtime library.
+	Api_only *bool
+
 	// local files that are used within user customized droiddoc options.
 	Droiddoc_option_files []string
 
@@ -106,16 +122,6 @@
 	//  $(location <label>): the path to the droiddoc_option_files with name <label>
 	Droiddoc_options []string
 
-	// the java library (in classpath) for documentation that provides java srcs and srcjars.
-	Srcs_lib *string
-
-	// the base dirs under srcs_lib will be scanned for java srcs.
-	Srcs_lib_whitelist_dirs []string
-
-	// the sub dirs under srcs_lib_whitelist_dirs will be scanned for java srcs.
-	// Defaults to "android.annotation".
-	Srcs_lib_whitelist_pkgs []string
-
 	// a list of top-level directories containing files to merge qualifier annotations
 	// (i.e. those intended to be included in the stubs written) from.
 	Merge_annotations_dirs []string
@@ -129,6 +135,9 @@
 	// don't create dist rules.
 	No_dist *bool `blueprint:"mutated"`
 
+	// indicates whether system and test apis should be managed.
+	Has_system_and_test_apis bool `blueprint:"mutated"`
+
 	// TODO: determines whether to create HTML doc or not
 	//Html_doc *bool
 }
@@ -149,29 +158,40 @@
 	publicApiFilePath android.Path
 	systemApiFilePath android.Path
 	testApiFilePath   android.Path
+
+	permissionsFile android.Path
 }
 
 var _ Dependency = (*SdkLibrary)(nil)
 var _ SdkLibraryDependency = (*SdkLibrary)(nil)
 
 func (module *SdkLibrary) DepsMutator(ctx android.BottomUpMutatorContext) {
+	useBuiltStubs := !ctx.Config().UnbundledBuildUsePrebuiltSdks()
 	// Add dependencies to the stubs library
-	ctx.AddVariationDependencies(nil, publicApiStubsTag, module.stubsName(apiScopePublic))
+	if useBuiltStubs {
+		ctx.AddVariationDependencies(nil, publicApiStubsTag, module.stubsName(apiScopePublic))
+	}
 	ctx.AddVariationDependencies(nil, publicApiFileTag, module.docsName(apiScopePublic))
 
-	sdkDep := decodeSdkDep(ctx, sdkContext(&module.Library))
-	if sdkDep.hasStandardLibs() {
-		ctx.AddVariationDependencies(nil, systemApiStubsTag, module.stubsName(apiScopeSystem))
+	if module.sdkLibraryProperties.Has_system_and_test_apis {
+		if useBuiltStubs {
+			ctx.AddVariationDependencies(nil, systemApiStubsTag, module.stubsName(apiScopeSystem))
+			ctx.AddVariationDependencies(nil, testApiStubsTag, module.stubsName(apiScopeTest))
+		}
 		ctx.AddVariationDependencies(nil, systemApiFileTag, module.docsName(apiScopeSystem))
 		ctx.AddVariationDependencies(nil, testApiFileTag, module.docsName(apiScopeTest))
-		ctx.AddVariationDependencies(nil, testApiStubsTag, module.stubsName(apiScopeTest))
 	}
 
 	module.Library.deps(ctx)
 }
 
 func (module *SdkLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	module.Library.GenerateAndroidBuildActions(ctx)
+	// Don't build an implementation library if this is api only.
+	if !proptools.Bool(module.sdkLibraryProperties.Api_only) {
+		module.Library.GenerateAndroidBuildActions(ctx)
+	}
+
+	module.buildPermissionsFile(ctx)
 
 	// Record the paths to the header jars of the library (stubs and impl).
 	// When this java_sdk_library is dependened from others via "libs" property,
@@ -208,65 +228,92 @@
 	})
 }
 
-func (module *SdkLibrary) AndroidMk() android.AndroidMkData {
-	data := module.Library.AndroidMk()
-	data.Required = append(data.Required, module.xmlFileName())
+func (module *SdkLibrary) buildPermissionsFile(ctx android.ModuleContext) {
+	xmlContent := fmt.Sprintf(permissionsTemplate, module.BaseModuleName(), module.implPath())
+	permissionsFile := android.PathForModuleOut(ctx, module.xmlFileName())
 
-	data.Custom = func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
-		android.WriteAndroidMkData(w, data)
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        android.WriteFile,
+		Output:      permissionsFile,
+		Description: "Generating " + module.BaseModuleName() + " permissions",
+		Args: map[string]string{
+			"content": xmlContent,
+		},
+	})
 
-		module.Library.AndroidMkHostDex(w, name, data)
-		if !Bool(module.sdkLibraryProperties.No_dist) {
-			// Create a phony module that installs the impl library, for the case when this lib is
-			// in PRODUCT_PACKAGES.
-			owner := module.ModuleBase.Owner()
-			if owner == "" {
-				if Bool(module.sdkLibraryProperties.Core_lib) {
-					owner = "core"
-				} else {
-					owner = "android"
+	module.permissionsFile = permissionsFile
+}
+
+func (module *SdkLibrary) OutputFiles(tag string) (android.Paths, error) {
+	switch tag {
+	case ".xml":
+		return android.Paths{module.permissionsFile}, nil
+	}
+	return module.Library.OutputFiles(tag)
+}
+
+func (module *SdkLibrary) AndroidMkEntries() []android.AndroidMkEntries {
+	if proptools.Bool(module.sdkLibraryProperties.Api_only) {
+		return nil
+	}
+	entriesList := module.Library.AndroidMkEntries()
+	entries := &entriesList[0]
+	entries.Required = append(entries.Required, module.xmlFileName())
+
+	entries.ExtraFooters = []android.AndroidMkExtraFootersFunc{
+		func(w io.Writer, name, prefix, moduleDir string, entries *android.AndroidMkEntries) {
+			if !Bool(module.sdkLibraryProperties.No_dist) {
+				// Create a phony module that installs the impl library, for the case when this lib is
+				// in PRODUCT_PACKAGES.
+				owner := module.ModuleBase.Owner()
+				if owner == "" {
+					if Bool(module.sdkLibraryProperties.Core_lib) {
+						owner = "core"
+					} else {
+						owner = "android"
+					}
+				}
+				// Create dist rules to install the stubs libs to the dist dir
+				if len(module.publicApiStubsPath) == 1 {
+					fmt.Fprintln(w, "$(call dist-for-goals,sdk win_sdk,"+
+						module.publicApiStubsImplPath.Strings()[0]+
+						":"+path.Join("apistubs", owner, "public",
+						module.BaseModuleName()+".jar")+")")
+				}
+				if len(module.systemApiStubsPath) == 1 {
+					fmt.Fprintln(w, "$(call dist-for-goals,sdk win_sdk,"+
+						module.systemApiStubsImplPath.Strings()[0]+
+						":"+path.Join("apistubs", owner, "system",
+						module.BaseModuleName()+".jar")+")")
+				}
+				if len(module.testApiStubsPath) == 1 {
+					fmt.Fprintln(w, "$(call dist-for-goals,sdk win_sdk,"+
+						module.testApiStubsImplPath.Strings()[0]+
+						":"+path.Join("apistubs", owner, "test",
+						module.BaseModuleName()+".jar")+")")
+				}
+				if module.publicApiFilePath != nil {
+					fmt.Fprintln(w, "$(call dist-for-goals,sdk win_sdk,"+
+						module.publicApiFilePath.String()+
+						":"+path.Join("apistubs", owner, "public", "api",
+						module.BaseModuleName()+".txt")+")")
+				}
+				if module.systemApiFilePath != nil {
+					fmt.Fprintln(w, "$(call dist-for-goals,sdk win_sdk,"+
+						module.systemApiFilePath.String()+
+						":"+path.Join("apistubs", owner, "system", "api",
+						module.BaseModuleName()+".txt")+")")
+				}
+				if module.testApiFilePath != nil {
+					fmt.Fprintln(w, "$(call dist-for-goals,sdk win_sdk,"+
+						module.testApiFilePath.String()+
+						":"+path.Join("apistubs", owner, "test", "api",
+						module.BaseModuleName()+".txt")+")")
 				}
 			}
-			// Create dist rules to install the stubs libs to the dist dir
-			if len(module.publicApiStubsPath) == 1 {
-				fmt.Fprintln(w, "$(call dist-for-goals,sdk win_sdk,"+
-					module.publicApiStubsImplPath.Strings()[0]+
-					":"+path.Join("apistubs", owner, "public",
-					module.BaseModuleName()+".jar")+")")
-			}
-			if len(module.systemApiStubsPath) == 1 {
-				fmt.Fprintln(w, "$(call dist-for-goals,sdk win_sdk,"+
-					module.systemApiStubsImplPath.Strings()[0]+
-					":"+path.Join("apistubs", owner, "system",
-					module.BaseModuleName()+".jar")+")")
-			}
-			if len(module.testApiStubsPath) == 1 {
-				fmt.Fprintln(w, "$(call dist-for-goals,sdk win_sdk,"+
-					module.testApiStubsImplPath.Strings()[0]+
-					":"+path.Join("apistubs", owner, "test",
-					module.BaseModuleName()+".jar")+")")
-			}
-			if module.publicApiFilePath != nil {
-				fmt.Fprintln(w, "$(call dist-for-goals,sdk win_sdk,"+
-					module.publicApiFilePath.String()+
-					":"+path.Join("apistubs", owner, "public", "api",
-					module.BaseModuleName()+".txt")+")")
-			}
-			if module.systemApiFilePath != nil {
-				fmt.Fprintln(w, "$(call dist-for-goals,sdk win_sdk,"+
-					module.systemApiFilePath.String()+
-					":"+path.Join("apistubs", owner, "system", "api",
-					module.BaseModuleName()+".txt")+")")
-			}
-			if module.testApiFilePath != nil {
-				fmt.Fprintln(w, "$(call dist-for-goals,sdk win_sdk,"+
-					module.testApiFilePath.String()+
-					":"+path.Join("apistubs", owner, "test", "api",
-					module.BaseModuleName()+".txt")+")")
-			}
-		}
+		},
 	}
-	return data
+	return entriesList
 }
 
 // Module name of the stubs library
@@ -300,6 +347,12 @@
 
 // File path to the runtime implementation library
 func (module *SdkLibrary) implPath() string {
+	if apexName := module.ApexName(); apexName != "" {
+		// TODO(b/146468504): ApexName() is only a soong module name, not apex name.
+		// In most cases, this works fine. But when apex_name is set or override_apex is used
+		// this can be wrong.
+		return fmt.Sprintf("/apex/%s/javalib/%s.jar", apexName, module.implName())
+	}
 	partition := "system"
 	if module.SocSpecific() {
 		partition = "vendor"
@@ -307,6 +360,8 @@
 		partition = "odm"
 	} else if module.ProductSpecific() {
 		partition = "product"
+	} else if module.SystemExtSpecific() {
+		partition = "system_ext"
 	}
 	return "/" + partition + "/framework/" + module.implName() + ".jar"
 }
@@ -319,7 +374,7 @@
 // SDK version that the stubs library is built against. Note that this is always
 // *current. Older stubs library built with a numberd SDK version is created from
 // the prebuilt jar.
-func (module *SdkLibrary) sdkVersion(apiScope apiScope) string {
+func (module *SdkLibrary) sdkVersionForScope(apiScope apiScope) string {
 	switch apiScope {
 	case apiScopePublic:
 		return "current"
@@ -332,6 +387,18 @@
 	}
 }
 
+// Get the sdk version for use when compiling the stubs library.
+func (module *SdkLibrary) sdkVersionForStubsLibrary(mctx android.LoadHookContext, apiScope apiScope) string {
+	sdkDep := decodeSdkDep(mctx, sdkContext(&module.Library))
+	if sdkDep.hasStandardLibs() {
+		// If building against a standard sdk then use the sdk version appropriate for the scope.
+		return module.sdkVersionForScope(apiScope)
+	} else {
+		// Otherwise, use no system module.
+		return "none"
+	}
+}
+
 // $(INTERNAL_PLATFORM_<apiTagName>_API_FILE) points to the generated
 // api file for the current source
 // TODO: remove this when apicheck is done in soong
@@ -377,17 +444,19 @@
 // Creates a static java library that has API stubs
 func (module *SdkLibrary) createStubsLibrary(mctx android.LoadHookContext, apiScope apiScope) {
 	props := struct {
-		Name              *string
-		Srcs              []string
-		Sdk_version       *string
-		Libs              []string
-		Soc_specific      *bool
-		Device_specific   *bool
-		Product_specific  *bool
-		Compile_dex       *bool
-		System_modules    *string
-		Java_version      *string
-		Product_variables struct {
+		Name                *string
+		Srcs                []string
+		Installable         *bool
+		Sdk_version         *string
+		System_modules      *string
+		Libs                []string
+		Soc_specific        *bool
+		Device_specific     *bool
+		Product_specific    *bool
+		System_ext_specific *bool
+		Compile_dex         *bool
+		Java_version        *string
+		Product_variables   struct {
 			Unbundled_build struct {
 				Enabled *bool
 			}
@@ -401,23 +470,19 @@
 		}
 	}{}
 
-	sdkVersion := module.sdkVersion(apiScope)
-	sdkDep := decodeSdkDep(mctx, sdkContext(&module.Library))
-	if !sdkDep.hasStandardLibs() {
-		sdkVersion = "none"
-	}
-
 	props.Name = proptools.StringPtr(module.stubsName(apiScope))
 	// sources are generated from the droiddoc
 	props.Srcs = []string{":" + module.docsName(apiScope)}
+	sdkVersion := module.sdkVersionForStubsLibrary(mctx, apiScope)
 	props.Sdk_version = proptools.StringPtr(sdkVersion)
+	props.System_modules = module.Library.Module.deviceProperties.System_modules
+	props.Installable = proptools.BoolPtr(false)
 	props.Libs = module.sdkLibraryProperties.Stub_only_libs
 	// Unbundled apps will use the prebult one from /prebuilts/sdk
 	if mctx.Config().UnbundledBuildUsePrebuiltSdks() {
 		props.Product_variables.Unbundled_build.Enabled = proptools.BoolPtr(false)
 	}
 	props.Product_variables.Pdk.Enabled = proptools.BoolPtr(false)
-	props.System_modules = module.Library.Module.deviceProperties.System_modules
 	props.Openjdk9.Srcs = module.Library.Module.properties.Openjdk9.Srcs
 	props.Openjdk9.Javacflags = module.Library.Module.properties.Openjdk9.Javacflags
 	props.Java_version = module.Library.Module.properties.Java_version
@@ -431,22 +496,22 @@
 		props.Device_specific = proptools.BoolPtr(true)
 	} else if module.ProductSpecific() {
 		props.Product_specific = proptools.BoolPtr(true)
+	} else if module.SystemExtSpecific() {
+		props.System_ext_specific = proptools.BoolPtr(true)
 	}
 
-	mctx.CreateModule(android.ModuleFactoryAdaptor(LibraryFactory), &props)
+	mctx.CreateModule(LibraryFactory, &props)
 }
 
 // Creates a droiddoc module that creates stubs source files from the given full source
 // files
-func (module *SdkLibrary) createDocs(mctx android.LoadHookContext, apiScope apiScope) {
+func (module *SdkLibrary) createStubsSources(mctx android.LoadHookContext, apiScope apiScope) {
 	props := struct {
 		Name                             *string
 		Srcs                             []string
 		Installable                      *bool
-		Srcs_lib                         *string
-		Srcs_lib_whitelist_dirs          []string
-		Srcs_lib_whitelist_pkgs          []string
 		Sdk_version                      *string
+		System_modules                   *string
 		Libs                             []string
 		Arg_files                        []string
 		Args                             *string
@@ -468,6 +533,8 @@
 	}{}
 
 	sdkDep := decodeSdkDep(mctx, sdkContext(&module.Library))
+	// Use the platform API if standard libraries were requested, otherwise use
+	// no default libraries.
 	sdkVersion := ""
 	if !sdkDep.hasStandardLibs() {
 		sdkVersion = "none"
@@ -475,8 +542,8 @@
 
 	props.Name = proptools.StringPtr(module.docsName(apiScope))
 	props.Srcs = append(props.Srcs, module.Library.Module.properties.Srcs...)
-	props.Srcs = append(props.Srcs, module.sdkLibraryProperties.Api_srcs...)
 	props.Sdk_version = proptools.StringPtr(sdkVersion)
+	props.System_modules = module.Library.Module.deviceProperties.System_modules
 	props.Installable = proptools.BoolPtr(false)
 	// A droiddoc module has only one Libs property and doesn't distinguish between
 	// shared libs and static libs. So we need to add both of these libs to Libs property.
@@ -489,21 +556,36 @@
 	props.Merge_annotations_dirs = module.sdkLibraryProperties.Merge_annotations_dirs
 	props.Merge_inclusion_annotations_dirs = module.sdkLibraryProperties.Merge_inclusion_annotations_dirs
 
-	droiddocArgs := " --stub-packages " + strings.Join(module.sdkLibraryProperties.Api_packages, ":") +
-		" " + android.JoinWithPrefix(module.sdkLibraryProperties.Hidden_api_packages, " --hide-package ") +
-		" " + android.JoinWithPrefix(module.sdkLibraryProperties.Droiddoc_options, " ") +
-		" --hide MissingPermission --hide BroadcastBehavior " +
-		"--hide HiddenSuperclass --hide DeprecationMismatch --hide UnavailableSymbol " +
-		"--hide SdkConstant --hide HiddenTypeParameter --hide Todo --hide Typo"
+	droiddocArgs := []string{}
+	if len(module.sdkLibraryProperties.Api_packages) != 0 {
+		droiddocArgs = append(droiddocArgs, "--stub-packages "+strings.Join(module.sdkLibraryProperties.Api_packages, ":"))
+	}
+	if len(module.sdkLibraryProperties.Hidden_api_packages) != 0 {
+		droiddocArgs = append(droiddocArgs,
+			android.JoinWithPrefix(module.sdkLibraryProperties.Hidden_api_packages, " --hide-package "))
+	}
+	droiddocArgs = append(droiddocArgs, module.sdkLibraryProperties.Droiddoc_options...)
+	disabledWarnings := []string{
+		"MissingPermission",
+		"BroadcastBehavior",
+		"HiddenSuperclass",
+		"DeprecationMismatch",
+		"UnavailableSymbol",
+		"SdkConstant",
+		"HiddenTypeParameter",
+		"Todo",
+		"Typo",
+	}
+	droiddocArgs = append(droiddocArgs, android.JoinWithPrefix(disabledWarnings, "--hide "))
 
 	switch apiScope {
 	case apiScopeSystem:
-		droiddocArgs = droiddocArgs + " -showAnnotation android.annotation.SystemApi"
+		droiddocArgs = append(droiddocArgs, "-showAnnotation android.annotation.SystemApi")
 	case apiScopeTest:
-		droiddocArgs = droiddocArgs + " -showAnnotation android.annotation.TestApi"
+		droiddocArgs = append(droiddocArgs, " -showAnnotation android.annotation.TestApi")
 	}
 	props.Arg_files = module.sdkLibraryProperties.Droiddoc_option_files
-	props.Args = proptools.StringPtr(droiddocArgs)
+	props.Args = proptools.StringPtr(strings.Join(droiddocArgs, " "))
 
 	// List of APIs identified from the provided source files are created. They are later
 	// compared against to the not-yet-released (a.k.a current) list of APIs and to the
@@ -518,8 +600,9 @@
 		currentApiFileName = "test-" + currentApiFileName
 		removedApiFileName = "test-" + removedApiFileName
 	}
-	currentApiFileName = path.Join("api", currentApiFileName)
-	removedApiFileName = path.Join("api", removedApiFileName)
+	apiDir := module.getApiDir()
+	currentApiFileName = path.Join(apiDir, currentApiFileName)
+	removedApiFileName = path.Join(apiDir, removedApiFileName)
 	// TODO(jiyong): remove these three props
 	props.Api_tag_name = proptools.StringPtr(module.apiTagName(apiScope))
 	props.Api_filename = proptools.StringPtr(currentApiFileName)
@@ -535,62 +618,25 @@
 	props.Check_api.Last_released.Removed_api_file = proptools.StringPtr(
 		module.latestRemovedApiFilegroupName(apiScope))
 	props.Check_api.Ignore_missing_latest_api = proptools.BoolPtr(true)
-	props.Srcs_lib = module.sdkLibraryProperties.Srcs_lib
-	props.Srcs_lib_whitelist_dirs = module.sdkLibraryProperties.Srcs_lib_whitelist_dirs
-	props.Srcs_lib_whitelist_pkgs = module.sdkLibraryProperties.Srcs_lib_whitelist_pkgs
 
-	mctx.CreateModule(android.ModuleFactoryAdaptor(DroidstubsFactory), &props)
+	mctx.CreateModule(DroidstubsFactory, &props)
 }
 
 // Creates the xml file that publicizes the runtime library
 func (module *SdkLibrary) createXmlFile(mctx android.LoadHookContext) {
-	template := `
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-  
-          http://www.apache.org/licenses/LICENSE-2.0
-  
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<permissions>
-    <library name="%s" file="%s"/>
-</permissions>
-`
-	// genrule to generate the xml file content from the template above
-	// TODO: preserve newlines in the generate xml file. Newlines are being squashed
-	// in the ninja file. Do we need to have an external tool for this?
-	xmlContent := fmt.Sprintf(template, module.BaseModuleName(), module.implPath())
-	genruleProps := struct {
-		Name *string
-		Cmd  *string
-		Out  []string
-	}{}
-	genruleProps.Name = proptools.StringPtr(module.xmlFileName() + "-gen")
-	genruleProps.Cmd = proptools.StringPtr("echo '" + xmlContent + "' > $(out)")
-	genruleProps.Out = []string{module.xmlFileName()}
-	mctx.CreateModule(android.ModuleFactoryAdaptor(genrule.GenRuleFactory), &genruleProps)
-
 	// creates a prebuilt_etc module to actually place the xml file under
 	// <partition>/etc/permissions
 	etcProps := struct {
-		Name             *string
-		Src              *string
-		Sub_dir          *string
-		Soc_specific     *bool
-		Device_specific  *bool
-		Product_specific *bool
+		Name                *string
+		Src                 *string
+		Sub_dir             *string
+		Soc_specific        *bool
+		Device_specific     *bool
+		Product_specific    *bool
+		System_ext_specific *bool
 	}{}
 	etcProps.Name = proptools.StringPtr(module.xmlFileName())
-	etcProps.Src = proptools.StringPtr(":" + module.xmlFileName() + "-gen")
+	etcProps.Src = proptools.StringPtr(":" + module.BaseModuleName() + "{.xml}")
 	etcProps.Sub_dir = proptools.StringPtr("permissions")
 	if module.SocSpecific() {
 		etcProps.Soc_specific = proptools.BoolPtr(true)
@@ -598,8 +644,10 @@
 		etcProps.Device_specific = proptools.BoolPtr(true)
 	} else if module.ProductSpecific() {
 		etcProps.Product_specific = proptools.BoolPtr(true)
+	} else if module.SystemExtSpecific() {
+		etcProps.System_ext_specific = proptools.BoolPtr(true)
 	}
-	mctx.CreateModule(android.ModuleFactoryAdaptor(android.PrebuiltEtcFactory), &etcProps)
+	mctx.CreateModule(android.PrebuiltEtcFactory, &etcProps)
 }
 
 func (module *SdkLibrary) PrebuiltJars(ctx android.BaseModuleContext, sdkVersion string) android.Paths {
@@ -619,7 +667,11 @@
 	jar := filepath.Join(dir, module.BaseModuleName()+".jar")
 	jarPath := android.ExistentPathForSource(ctx, jar)
 	if !jarPath.Valid() {
-		ctx.PropertyErrorf("sdk_library", "invalid sdk version %q, %q does not exist", v, jar)
+		if ctx.Config().AllowMissingDependencies() {
+			return android.Paths{android.PathForSource(ctx, jar)}
+		} else {
+			ctx.PropertyErrorf("sdk_library", "invalid sdk version %q, %q does not exist", sdkVersion, jar)
+		}
 		return nil
 	}
 	return android.Paths{jarPath.Path()}
@@ -669,23 +721,38 @@
 	}).(*[]string)
 }
 
+func (module *SdkLibrary) getApiDir() string {
+	return proptools.StringDefault(module.sdkLibraryProperties.Api_dir, "api")
+}
+
 // For a java_sdk_library module, create internal modules for stubs, docs,
 // runtime libs and xml file. If requested, the stubs and docs are created twice
 // once for public API level and once for system API level
 func (module *SdkLibrary) CreateInternalModules(mctx android.LoadHookContext) {
 	if len(module.Library.Module.properties.Srcs) == 0 {
 		mctx.PropertyErrorf("srcs", "java_sdk_library must specify srcs")
+		return
 	}
 
-	if len(module.sdkLibraryProperties.Api_packages) == 0 {
-		mctx.PropertyErrorf("api_packages", "java_sdk_library must specify api_packages")
+	// If this builds against standard libraries (i.e. is not part of the core libraries)
+	// then assume it provides both system and test apis. Otherwise, assume it does not and
+	// also assume it does not contribute to the dist build.
+	sdkDep := decodeSdkDep(mctx, sdkContext(&module.Library))
+	hasSystemAndTestApis := sdkDep.hasStandardLibs()
+	module.sdkLibraryProperties.Has_system_and_test_apis = hasSystemAndTestApis
+	module.sdkLibraryProperties.No_dist = proptools.BoolPtr(!hasSystemAndTestApis)
+
+	scopes := []string{""}
+	if hasSystemAndTestApis {
+		scopes = append(scopes, "system-", "test-")
 	}
 
 	missing_current_api := false
 
-	for _, scope := range []string{"", "system-", "test-"} {
+	apiDir := module.getApiDir()
+	for _, scope := range scopes {
 		for _, api := range []string{"current.txt", "removed.txt"} {
-			path := path.Join(mctx.ModuleDir(), "api", scope+api)
+			path := path.Join(mctx.ModuleDir(), apiDir, scope+api)
 			p := android.ExistentPathForSource(mctx, path)
 			if !p.Valid() {
 				mctx.ModuleErrorf("Current api file %#v doesn't exist", path)
@@ -704,33 +771,35 @@
 
 		mctx.ModuleErrorf("One or more current api files are missing. "+
 			"You can update them by:\n"+
-			"%s %q && m update-api", script, mctx.ModuleDir())
+			"%s %q %s && m update-api",
+			script, filepath.Join(mctx.ModuleDir(), apiDir), strings.Join(scopes, " "))
 		return
 	}
 
 	// for public API stubs
 	module.createStubsLibrary(mctx, apiScopePublic)
-	module.createDocs(mctx, apiScopePublic)
+	module.createStubsSources(mctx, apiScopePublic)
 
-	sdkDep := decodeSdkDep(mctx, sdkContext(&module.Library))
-	if sdkDep.hasStandardLibs() {
+	if hasSystemAndTestApis {
 		// for system API stubs
 		module.createStubsLibrary(mctx, apiScopeSystem)
-		module.createDocs(mctx, apiScopeSystem)
+		module.createStubsSources(mctx, apiScopeSystem)
 
 		// for test API stubs
 		module.createStubsLibrary(mctx, apiScopeTest)
-		module.createDocs(mctx, apiScopeTest)
-
-		// for runtime
-		module.createXmlFile(mctx)
+		module.createStubsSources(mctx, apiScopeTest)
 	}
 
-	// record java_sdk_library modules so that they are exported to make
-	javaSdkLibraries := javaSdkLibraries(mctx.Config())
-	javaSdkLibrariesLock.Lock()
-	defer javaSdkLibrariesLock.Unlock()
-	*javaSdkLibraries = append(*javaSdkLibraries, module.BaseModuleName())
+	if !proptools.Bool(module.sdkLibraryProperties.Api_only) {
+		// for runtime
+		module.createXmlFile(mctx)
+
+		// record java_sdk_library modules so that they are exported to make
+		javaSdkLibraries := javaSdkLibraries(mctx.Config())
+		javaSdkLibrariesLock.Lock()
+		defer javaSdkLibrariesLock.Unlock()
+		*javaSdkLibraries = append(*javaSdkLibraries, module.BaseModuleName())
+	}
 }
 
 func (module *SdkLibrary) InitSdkLibraryProperties() {
@@ -746,9 +815,15 @@
 	module.Library.Module.deviceProperties.IsSDKLibrary = true
 }
 
+// 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,
+// 3) the real runtime shared library that implements the APIs, and 4) XML file for adding
+// the runtime lib to the classpath at runtime if requested via <uses-library>.
 func SdkLibraryFactory() android.Module {
 	module := &SdkLibrary{}
 	module.InitSdkLibraryProperties()
+	android.InitApexModule(module)
 	InitJavaModule(module, android.HostAndDeviceSupported)
 	android.AddLoadHook(module, func(ctx android.LoadHookContext) { module.CreateInternalModules(ctx) })
 	return module
@@ -787,6 +862,7 @@
 
 var _ SdkLibraryDependency = (*sdkLibraryImport)(nil)
 
+// java_sdk_library_import imports a prebuilt java_sdk_library.
 func sdkLibraryImportFactory() android.Module {
 	module := &sdkLibraryImport{}
 
@@ -810,10 +886,11 @@
 func (module *sdkLibraryImport) createInternalModules(mctx android.LoadHookContext) {
 	// Creates a java import for the jar with ".stubs" suffix
 	props := struct {
-		Name             *string
-		Soc_specific     *bool
-		Device_specific  *bool
-		Product_specific *bool
+		Name                *string
+		Soc_specific        *bool
+		Device_specific     *bool
+		Product_specific    *bool
+		System_ext_specific *bool
 	}{}
 
 	props.Name = proptools.StringPtr(module.BaseModuleName() + sdkStubsLibrarySuffix)
@@ -824,9 +901,11 @@
 		props.Device_specific = proptools.BoolPtr(true)
 	} else if module.ProductSpecific() {
 		props.Product_specific = proptools.BoolPtr(true)
+	} else if module.SystemExtSpecific() {
+		props.System_ext_specific = proptools.BoolPtr(true)
 	}
 
-	mctx.CreateModule(android.ModuleFactoryAdaptor(ImportFactory), &props, &module.properties)
+	mctx.CreateModule(ImportFactory, &props, &module.properties)
 
 	javaSdkLibraries := javaSdkLibraries(mctx.Config())
 	javaSdkLibrariesLock.Lock()
diff --git a/java/sdk_test.go b/java/sdk_test.go
index 953c372..9cabd77 100644
--- a/java/sdk_test.go
+++ b/java/sdk_test.go
@@ -28,182 +28,188 @@
 
 func TestClasspath(t *testing.T) {
 	var classpathTestcases = []struct {
-		name          string
-		unbundled     bool
-		pdk           bool
-		moduleType    string
-		host          android.OsClass
-		properties    string
-		bootclasspath []string
-		system        string
-		classpath     []string
-		aidl          string
+		name       string
+		unbundled  bool
+		pdk        bool
+		moduleType string
+		host       android.OsClass
+		properties string
+
+		// for java 8
+		bootclasspath  []string
+		java8classpath []string
+
+		// for java 9
+		system         string
+		java9classpath []string
+
+		forces8 bool // if set, javac will always be called with java 8 arguments
+
+		aidl string
 	}{
 		{
-			name:          "default",
-			bootclasspath: config.DefaultBootclasspathLibraries,
-			system:        config.DefaultSystemModules,
-			classpath:     config.DefaultLibraries,
-			aidl:          "-Iframework/aidl",
+			name:           "default",
+			bootclasspath:  config.DefaultBootclasspathLibraries,
+			system:         config.DefaultSystemModules,
+			java8classpath: config.DefaultLibraries,
+			java9classpath: config.DefaultLibraries,
+			aidl:           "-Iframework/aidl",
 		},
 		{
-			name:          "no_framework_libs:true",
-			properties:    `no_framework_libs:true`,
-			bootclasspath: config.DefaultBootclasspathLibraries,
-			system:        config.DefaultSystemModules,
-			classpath:     []string{},
-			aidl:          "",
+			name:           `sdk_version:"core_platform"`,
+			properties:     `sdk_version:"core_platform"`,
+			bootclasspath:  config.DefaultBootclasspathLibraries,
+			system:         config.DefaultSystemModules,
+			java8classpath: []string{},
+			aidl:           "",
 		},
 		{
-			name:          `sdk_version:"core_platform"`,
-			properties:    `sdk_version:"core_platform"`,
-			bootclasspath: config.DefaultBootclasspathLibraries,
-			system:        config.DefaultSystemModules,
-			classpath:     []string{},
-			aidl:          "",
-		},
-		{
-			name:          "blank sdk version",
-			properties:    `sdk_version: "",`,
-			bootclasspath: config.DefaultBootclasspathLibraries,
-			system:        config.DefaultSystemModules,
-			classpath:     config.DefaultLibraries,
-			aidl:          "-Iframework/aidl",
+			name:           "blank sdk version",
+			properties:     `sdk_version: "",`,
+			bootclasspath:  config.DefaultBootclasspathLibraries,
+			system:         config.DefaultSystemModules,
+			java8classpath: config.DefaultLibraries,
+			java9classpath: config.DefaultLibraries,
+			aidl:           "-Iframework/aidl",
 		},
 		{
 
-			name:          "sdk v25",
-			properties:    `sdk_version: "25",`,
-			bootclasspath: []string{`""`},
-			system:        "bootclasspath", // special value to tell 1.9 test to expect bootclasspath
-			classpath:     []string{"prebuilts/sdk/25/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
-			aidl:          "-pprebuilts/sdk/25/public/framework.aidl",
+			name:           "sdk v29",
+			properties:     `sdk_version: "29",`,
+			bootclasspath:  []string{`""`},
+			forces8:        true,
+			java8classpath: []string{"prebuilts/sdk/29/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+			aidl:           "-pprebuilts/sdk/29/public/framework.aidl",
 		},
 		{
 
-			name:          "current",
-			properties:    `sdk_version: "current",`,
-			bootclasspath: []string{"android_stubs_current", "core-lambda-stubs"},
-			system:        "bootclasspath", // special value to tell 1.9 test to expect bootclasspath
-			aidl:          "-p" + buildDir + "/framework.aidl",
+			name:           "current",
+			properties:     `sdk_version: "current",`,
+			bootclasspath:  []string{"android_stubs_current", "core-lambda-stubs"},
+			system:         "core-current-stubs-system-modules",
+			java9classpath: []string{"android_stubs_current"},
+			aidl:           "-p" + buildDir + "/framework.aidl",
 		},
 		{
 
-			name:          "system_current",
-			properties:    `sdk_version: "system_current",`,
-			bootclasspath: []string{"android_system_stubs_current", "core-lambda-stubs"},
-			system:        "bootclasspath", // special value to tell 1.9 test to expect bootclasspath
-			aidl:          "-p" + buildDir + "/framework.aidl",
+			name:           "system_current",
+			properties:     `sdk_version: "system_current",`,
+			bootclasspath:  []string{"android_system_stubs_current", "core-lambda-stubs"},
+			system:         "core-current-stubs-system-modules",
+			java9classpath: []string{"android_system_stubs_current"},
+			aidl:           "-p" + buildDir + "/framework.aidl",
 		},
 		{
 
-			name:          "system_25",
-			properties:    `sdk_version: "system_25",`,
-			bootclasspath: []string{`""`},
-			system:        "bootclasspath", // special value to tell 1.9 test to expect bootclasspath
-			classpath:     []string{"prebuilts/sdk/25/system/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
-			aidl:          "-pprebuilts/sdk/25/public/framework.aidl",
+			name:           "system_29",
+			properties:     `sdk_version: "system_29",`,
+			bootclasspath:  []string{`""`},
+			forces8:        true,
+			java8classpath: []string{"prebuilts/sdk/29/system/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+			aidl:           "-pprebuilts/sdk/29/public/framework.aidl",
 		},
 		{
 
-			name:          "test_current",
-			properties:    `sdk_version: "test_current",`,
-			bootclasspath: []string{"android_test_stubs_current", "core-lambda-stubs"},
-			system:        "bootclasspath", // special value to tell 1.9 test to expect bootclasspath
-			aidl:          "-p" + buildDir + "/framework.aidl",
+			name:           "test_current",
+			properties:     `sdk_version: "test_current",`,
+			bootclasspath:  []string{"android_test_stubs_current", "core-lambda-stubs"},
+			system:         "core-current-stubs-system-modules",
+			java9classpath: []string{"android_test_stubs_current"},
+			aidl:           "-p" + buildDir + "/framework.aidl",
 		},
 		{
 
-			name:          "core_current",
-			properties:    `sdk_version: "core_current",`,
-			bootclasspath: []string{"core.current.stubs", "core-lambda-stubs"},
-			system:        "bootclasspath", // special value to tell 1.9 test to expect bootclasspath
+			name:           "core_current",
+			properties:     `sdk_version: "core_current",`,
+			bootclasspath:  []string{"core.current.stubs", "core-lambda-stubs"},
+			system:         "core-current-stubs-system-modules",
+			java9classpath: []string{"core.current.stubs"},
 		},
 		{
 
-			name:          "nostdlib",
-			properties:    `sdk_version: "none", system_modules: "none"`,
-			system:        "none",
-			bootclasspath: []string{`""`},
-			classpath:     []string{},
+			name:           "nostdlib",
+			properties:     `sdk_version: "none", system_modules: "none"`,
+			system:         "none",
+			bootclasspath:  []string{`""`},
+			java8classpath: []string{},
 		},
 		{
 
-			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{`""`},
-			classpath:     []string{},
+			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"},
+			java8classpath: []string{},
 		},
 		{
 
-			name:          "host default",
-			moduleType:    "java_library_host",
-			properties:    ``,
-			host:          android.Host,
-			bootclasspath: []string{"jdk8/jre/lib/jce.jar", "jdk8/jre/lib/rt.jar"},
-			classpath:     []string{},
+			name:           "host default",
+			moduleType:     "java_library_host",
+			properties:     ``,
+			host:           android.Host,
+			bootclasspath:  []string{"jdk8/jre/lib/jce.jar", "jdk8/jre/lib/rt.jar"},
+			java8classpath: []string{},
 		},
 		{
 
-			name:          "host supported default",
-			host:          android.Host,
-			properties:    `host_supported: true,`,
-			classpath:     []string{},
-			bootclasspath: []string{"jdk8/jre/lib/jce.jar", "jdk8/jre/lib/rt.jar"},
+			name:           "host supported default",
+			host:           android.Host,
+			properties:     `host_supported: true,`,
+			java8classpath: []string{},
+			bootclasspath:  []string{"jdk8/jre/lib/jce.jar", "jdk8/jre/lib/rt.jar"},
 		},
 		{
-			name:       "host supported nostdlib",
-			host:       android.Host,
-			properties: `host_supported: true, sdk_version: "none", system_modules: "none"`,
-			classpath:  []string{},
+			name:           "host supported nostdlib",
+			host:           android.Host,
+			properties:     `host_supported: true, sdk_version: "none", system_modules: "none"`,
+			java8classpath: []string{},
 		},
 		{
 
-			name:          "unbundled sdk v25",
-			unbundled:     true,
-			properties:    `sdk_version: "25",`,
-			bootclasspath: []string{`""`},
-			system:        "bootclasspath", // special value to tell 1.9 test to expect bootclasspath
-			classpath:     []string{"prebuilts/sdk/25/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
-			aidl:          "-pprebuilts/sdk/25/public/framework.aidl",
+			name:           "unbundled sdk v29",
+			unbundled:      true,
+			properties:     `sdk_version: "29",`,
+			bootclasspath:  []string{`""`},
+			forces8:        true,
+			java8classpath: []string{"prebuilts/sdk/29/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+			aidl:           "-pprebuilts/sdk/29/public/framework.aidl",
 		},
 		{
 
-			name:          "unbundled current",
-			unbundled:     true,
-			properties:    `sdk_version: "current",`,
-			bootclasspath: []string{`""`},
-			system:        "bootclasspath", // special value to tell 1.9 test to expect bootclasspath
-			classpath:     []string{"prebuilts/sdk/current/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
-			aidl:          "-pprebuilts/sdk/current/public/framework.aidl",
+			name:           "unbundled current",
+			unbundled:      true,
+			properties:     `sdk_version: "current",`,
+			bootclasspath:  []string{`""`},
+			forces8:        true,
+			java8classpath: []string{"prebuilts/sdk/current/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+			aidl:           "-pprebuilts/sdk/current/public/framework.aidl",
 		},
 
 		{
-			name:          "pdk default",
-			pdk:           true,
-			bootclasspath: []string{`""`},
-			system:        "bootclasspath", // special value to tell 1.9 test to expect bootclasspath
-			classpath:     []string{"prebuilts/sdk/25/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
-			aidl:          "-pprebuilts/sdk/25/public/framework.aidl",
+			name:           "pdk default",
+			pdk:            true,
+			bootclasspath:  []string{`""`},
+			forces8:        true,
+			java8classpath: []string{"prebuilts/sdk/29/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+			aidl:           "-pprebuilts/sdk/29/public/framework.aidl",
 		},
 		{
-			name:          "pdk current",
-			pdk:           true,
-			properties:    `sdk_version: "current",`,
-			bootclasspath: []string{`""`},
-			system:        "bootclasspath", // special value to tell 1.9 test to expect bootclasspath
-			classpath:     []string{"prebuilts/sdk/25/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
-			aidl:          "-pprebuilts/sdk/25/public/framework.aidl",
+			name:           "pdk current",
+			pdk:            true,
+			properties:     `sdk_version: "current",`,
+			bootclasspath:  []string{`""`},
+			forces8:        true,
+			java8classpath: []string{"prebuilts/sdk/29/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+			aidl:           "-pprebuilts/sdk/29/public/framework.aidl",
 		},
 		{
-			name:          "pdk 25",
-			pdk:           true,
-			properties:    `sdk_version: "25",`,
-			bootclasspath: []string{`""`},
-			system:        "bootclasspath", // special value to tell 1.9 test to expect bootclasspath
-			classpath:     []string{"prebuilts/sdk/25/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
-			aidl:          "-pprebuilts/sdk/25/public/framework.aidl",
+			name:           "pdk 29",
+			pdk:            true,
+			properties:     `sdk_version: "29",`,
+			bootclasspath:  []string{`""`},
+			forces8:        true,
+			java8classpath: []string{"prebuilts/sdk/29/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+			aidl:           "-pprebuilts/sdk/29/public/framework.aidl",
 		},
 	}
 
@@ -214,7 +220,7 @@
 				moduleType = testcase.moduleType
 			}
 
-			bp := moduleType + ` {
+			props := `
 				name: "foo",
 				srcs: ["a.java"],
 				target: {
@@ -222,6 +228,10 @@
 						srcs: ["bar-doc/IFoo.aidl"],
 					},
 				},
+				`
+			bp := moduleType + " {" + props + testcase.properties + `
+			}`
+			bpJava8 := moduleType + " {" + props + `java_version: "1.8",
 				` + testcase.properties + `
 			}`
 
@@ -239,101 +249,131 @@
 			}
 
 			bootclasspath := convertModulesToPaths(testcase.bootclasspath)
-			classpath := convertModulesToPaths(testcase.classpath)
+			java8classpath := convertModulesToPaths(testcase.java8classpath)
+			java9classpath := convertModulesToPaths(testcase.java9classpath)
 
-			bc := strings.Join(bootclasspath, ":")
-			if bc != "" {
-				bc = "-bootclasspath " + bc
+			bc := ""
+			var bcDeps []string
+			if len(bootclasspath) > 0 {
+				bc = "-bootclasspath " + strings.Join(bootclasspath, ":")
+				if bootclasspath[0] != `""` {
+					bcDeps = bootclasspath
+				}
 			}
 
-			c := strings.Join(classpath, ":")
-			if c != "" {
-				c = "-classpath " + c
+			j8c := ""
+			if len(java8classpath) > 0 {
+				j8c = "-classpath " + strings.Join(java8classpath, ":")
 			}
+
+			j9c := ""
+			if len(java9classpath) > 0 {
+				j9c = "-classpath " + strings.Join(java9classpath, ":")
+			}
+
 			system := ""
+			var systemDeps []string
 			if testcase.system == "none" {
 				system = "--system=none"
 			} else if testcase.system != "" {
 				system = "--system=" + filepath.Join(buildDir, ".intermediates", testcase.system, "android_common", "system")
+				// The module-relative parts of these paths are hardcoded in system_modules.go:
+				systemDeps = []string{
+					filepath.Join(buildDir, ".intermediates", testcase.system, "android_common", "system", "lib", "modules"),
+					filepath.Join(buildDir, ".intermediates", testcase.system, "android_common", "system", "lib", "jrt-fs.jar"),
+					filepath.Join(buildDir, ".intermediates", testcase.system, "android_common", "system", "release"),
+				}
 			}
 
-			checkClasspath := func(t *testing.T, ctx *android.TestContext) {
-				javac := ctx.ModuleForTests("foo", variant).Rule("javac")
+			checkClasspath := func(t *testing.T, ctx *android.TestContext, isJava8 bool) {
+				foo := ctx.ModuleForTests("foo", variant)
+				javac := foo.Rule("javac")
+				var deps []string
+
+				aidl := foo.MaybeRule("aidl")
+				if aidl.Rule != nil {
+					deps = append(deps, aidl.Output.String())
+				}
 
 				got := javac.Args["bootClasspath"]
-				if got != bc {
-					t.Errorf("bootclasspath expected %q != got %q", bc, got)
+				expected := ""
+				if isJava8 || testcase.forces8 {
+					expected = bc
+					deps = append(deps, bcDeps...)
+				} else {
+					expected = system
+					deps = append(deps, systemDeps...)
+				}
+				if got != expected {
+					t.Errorf("bootclasspath expected %q != got %q", expected, got)
 				}
 
+				if isJava8 || testcase.forces8 {
+					expected = j8c
+					deps = append(deps, java8classpath...)
+				} else {
+					expected = j9c
+					deps = append(deps, java9classpath...)
+				}
 				got = javac.Args["classpath"]
-				if got != c {
-					t.Errorf("classpath expected %q != got %q", c, got)
+				if got != expected {
+					t.Errorf("classpath expected %q != got %q", expected, got)
 				}
 
-				var deps []string
-				if len(bootclasspath) > 0 && bootclasspath[0] != `""` {
-					deps = append(deps, bootclasspath...)
-				}
-				deps = append(deps, classpath...)
-
 				if !reflect.DeepEqual(javac.Implicits.Strings(), deps) {
 					t.Errorf("implicits expected %q != got %q", deps, javac.Implicits.Strings())
 				}
 			}
 
+			// Test with legacy javac -source 1.8 -target 1.8
 			t.Run("Java language level 8", func(t *testing.T) {
-				// Test default javac -source 1.8 -target 1.8
-				config := testConfig(nil)
+				config := testConfig(nil, bpJava8, nil)
 				if testcase.unbundled {
 					config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true)
 				}
 				if testcase.pdk {
 					config.TestProductVariables.Pdk = proptools.BoolPtr(true)
 				}
-				ctx := testContext(config, bp, nil)
+				ctx := testContext()
 				run(t, ctx, config)
 
-				checkClasspath(t, ctx)
+				checkClasspath(t, ctx, true /* isJava8 */)
 
 				if testcase.host != android.Host {
 					aidl := ctx.ModuleForTests("foo", variant).Rule("aidl")
 
-					aidlFlags := aidl.Args["aidlFlags"]
-					// Trim trailing "-I." to avoid having to specify it in every test
-					aidlFlags = strings.TrimSpace(strings.TrimSuffix(aidlFlags, "-I."))
-
-					if g, w := aidlFlags, testcase.aidl; g != w {
-						t.Errorf("want aidl flags %q, got %q", w, g)
+					if g, w := aidl.RuleParams.Command, testcase.aidl+" -I."; !strings.Contains(g, w) {
+						t.Errorf("want aidl command to contain %q, got %q", w, g)
 					}
 				}
 			})
 
-			// Test again with javac -source 9 -target 9
+			// Test with default javac -source 9 -target 9
 			t.Run("Java language level 9", func(t *testing.T) {
-				config := testConfig(map[string]string{"EXPERIMENTAL_JAVA_LANGUAGE_LEVEL_9": "true"})
+				config := testConfig(nil, bp, nil)
 				if testcase.unbundled {
 					config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true)
 				}
 				if testcase.pdk {
 					config.TestProductVariables.Pdk = proptools.BoolPtr(true)
 				}
-				ctx := testContext(config, bp, nil)
+				ctx := testContext()
 				run(t, ctx, config)
 
-				javac := ctx.ModuleForTests("foo", variant).Rule("javac")
-				got := javac.Args["bootClasspath"]
-				expected := system
-				if testcase.system == "bootclasspath" {
-					expected = bc
-				}
-				if got != expected {
-					t.Errorf("bootclasspath expected %q != got %q", expected, got)
+				checkClasspath(t, ctx, false /* isJava8 */)
+
+				if testcase.host != android.Host {
+					aidl := ctx.ModuleForTests("foo", variant).Rule("aidl")
+
+					if g, w := aidl.RuleParams.Command, testcase.aidl+" -I."; !strings.Contains(g, w) {
+						t.Errorf("want aidl command to contain %q, got %q", w, g)
+					}
 				}
 			})
 
-			// Test again with PLATFORM_VERSION_CODENAME=REL
-			t.Run("REL", func(t *testing.T) {
-				config := testConfig(nil)
+			// Test again with PLATFORM_VERSION_CODENAME=REL, javac -source 8 -target 8
+			t.Run("REL + Java language level 8", func(t *testing.T) {
+				config := testConfig(nil, bpJava8, nil)
 				config.TestProductVariables.Platform_sdk_codename = proptools.StringPtr("REL")
 				config.TestProductVariables.Platform_sdk_final = proptools.BoolPtr(true)
 
@@ -343,12 +383,29 @@
 				if testcase.pdk {
 					config.TestProductVariables.Pdk = proptools.BoolPtr(true)
 				}
-				ctx := testContext(config, bp, nil)
+				ctx := testContext()
 				run(t, ctx, config)
 
-				checkClasspath(t, ctx)
+				checkClasspath(t, ctx, true /* isJava8 */)
+			})
+
+			// Test again with PLATFORM_VERSION_CODENAME=REL, javac -source 9 -target 9
+			t.Run("REL + Java language level 9", func(t *testing.T) {
+				config := testConfig(nil, bp, nil)
+				config.TestProductVariables.Platform_sdk_codename = proptools.StringPtr("REL")
+				config.TestProductVariables.Platform_sdk_final = proptools.BoolPtr(true)
+
+				if testcase.unbundled {
+					config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true)
+				}
+				if testcase.pdk {
+					config.TestProductVariables.Pdk = proptools.BoolPtr(true)
+				}
+				ctx := testContext()
+				run(t, ctx, config)
+
+				checkClasspath(t, ctx, false /* isJava8 */)
 			})
 		})
 	}
-
 }
diff --git a/java/support_libraries.go b/java/support_libraries.go
index 5a72f41..af7c3c2 100644
--- a/java/support_libraries.go
+++ b/java/support_libraries.go
@@ -52,8 +52,6 @@
 			supportAars = append(supportAars, name)
 		case *Library, *Import:
 			supportJars = append(supportJars, name)
-		default:
-			ctx.ModuleErrorf(module, "unknown module type %t", module)
 		}
 	})
 
diff --git a/java/sysprop.go b/java/sysprop.go
new file mode 100644
index 0000000..1a70499
--- /dev/null
+++ b/java/sysprop.go
@@ -0,0 +1,60 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+	"sync"
+
+	"android/soong/android"
+)
+
+type syspropLibraryInterface interface {
+	BaseModuleName() string
+	Owner() string
+	HasPublicStub() bool
+	JavaPublicStubName() string
+}
+
+var (
+	syspropPublicStubsKey  = android.NewOnceKey("syspropPublicStubsJava")
+	syspropPublicStubsLock sync.Mutex
+)
+
+func init() {
+	android.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
+		ctx.BottomUp("sysprop_java", SyspropMutator).Parallel()
+	})
+}
+
+func syspropPublicStubs(config android.Config) map[string]string {
+	return config.Once(syspropPublicStubsKey, func() interface{} {
+		return make(map[string]string)
+	}).(map[string]string)
+}
+
+// gather list of sysprop libraries owned by platform.
+func SyspropMutator(mctx android.BottomUpMutatorContext) {
+	if m, ok := mctx.Module().(syspropLibraryInterface); ok {
+		if m.Owner() != "Platform" || !m.HasPublicStub() {
+			return
+		}
+
+		syspropPublicStubs := syspropPublicStubs(mctx.Config())
+		syspropPublicStubsLock.Lock()
+		defer syspropPublicStubsLock.Unlock()
+
+		syspropPublicStubs[m.BaseModuleName()] = m.JavaPublicStubName()
+	}
+}
diff --git a/java/system_modules.go b/java/system_modules.go
index f71f452..ed2fc18 100644
--- a/java/system_modules.go
+++ b/java/system_modules.go
@@ -28,21 +28,27 @@
 // system modules in a runtime image using the jmod and jlink tools.
 
 func init() {
-	android.RegisterModuleType("java_system_modules", SystemModulesFactory)
+	RegisterSystemModulesBuildComponents(android.InitRegistrationContext)
 
 	pctx.SourcePathVariable("moduleInfoJavaPath", "build/soong/scripts/jars-to-module-info-java.sh")
 }
 
+func RegisterSystemModulesBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("java_system_modules", SystemModulesFactory)
+}
+
 var (
 	jarsTosystemModules = pctx.AndroidStaticRule("jarsTosystemModules", blueprint.RuleParams{
 		Command: `rm -rf ${outDir} ${workDir} && mkdir -p ${workDir}/jmod && ` +
-			`${moduleInfoJavaPath} ${moduleName} $in > ${workDir}/module-info.java && ` +
+			`${moduleInfoJavaPath} java.base $in > ${workDir}/module-info.java && ` +
 			`${config.JavacCmd} --system=none --patch-module=java.base=${classpath} ${workDir}/module-info.java && ` +
 			`${config.SoongZipCmd} -jar -o ${workDir}/classes.jar -C ${workDir} -f ${workDir}/module-info.class && ` +
 			`${config.MergeZipsCmd} -j ${workDir}/module.jar ${workDir}/classes.jar $in && ` +
-			`${config.JmodCmd} create --module-version 9 --target-platform android ` +
-			`  --class-path ${workDir}/module.jar ${workDir}/jmod/${moduleName}.jmod && ` +
-			`${config.JlinkCmd} --module-path ${workDir}/jmod --add-modules ${moduleName} --output ${outDir} ` +
+			// Note: The version of the java.base module created must match the version
+			// of the jlink tool which consumes it.
+			`${config.JmodCmd} create --module-version ${config.JlinkVersion} --target-platform android ` +
+			`  --class-path ${workDir}/module.jar ${workDir}/jmod/java.base.jmod && ` +
+			`${config.JlinkCmd} --module-path ${workDir}/jmod --add-modules java.base --output ${outDir} ` +
 			// Note: The system-modules jlink plugin is disabled because (a) it is not
 			// useful on Android, and (b) it causes errors with later versions of jlink
 			// when the jdk.internal.module is absent from java.base (as it is here).
@@ -58,10 +64,10 @@
 			"${config.JrtFsJar}",
 		},
 	},
-		"moduleName", "classpath", "outDir", "workDir")
+		"classpath", "outDir", "workDir")
 )
 
-func TransformJarsToSystemModules(ctx android.ModuleContext, moduleName string, jars android.Paths) (android.Path, android.Paths) {
+func TransformJarsToSystemModules(ctx android.ModuleContext, jars android.Paths) (android.Path, android.Paths) {
 	outDir := android.PathForModuleOut(ctx, "system")
 	workDir := android.PathForModuleOut(ctx, "modules")
 	outputFile := android.PathForModuleOut(ctx, "system/lib/modules")
@@ -77,10 +83,9 @@
 		Outputs:     outputs,
 		Inputs:      jars,
 		Args: map[string]string{
-			"moduleName": moduleName,
-			"classpath":  strings.Join(jars.Strings(), ":"),
-			"workDir":    workDir.String(),
-			"outDir":     outDir.String(),
+			"classpath": strings.Join(jars.Strings(), ":"),
+			"workDir":   workDir.String(),
+			"outDir":    outDir.String(),
 		},
 	})
 
@@ -101,6 +106,9 @@
 
 	properties SystemModulesProperties
 
+	// The aggregated header jars from all jars specified in the libs property.
+	// Used when system module is added as a dependency to bootclasspath.
+	headerJars android.Paths
 	outputDir  android.Path
 	outputDeps android.Paths
 }
@@ -118,7 +126,9 @@
 		jars = append(jars, dep.HeaderJars()...)
 	})
 
-	system.outputDir, system.outputDeps = TransformJarsToSystemModules(ctx, "java.base", jars)
+	system.headerJars = jars
+
+	system.outputDir, system.outputDeps = TransformJarsToSystemModules(ctx, jars)
 }
 
 func (system *SystemModules) DepsMutator(ctx android.BottomUpMutatorContext) {
@@ -138,7 +148,7 @@
 			fmt.Fprintln(w, makevar, ":=$=", strings.Join(system.properties.Libs, " "))
 			fmt.Fprintln(w)
 
-			makevar = "SOONG_SYSTEM_MODULE_DEPS_" + name
+			makevar = "SOONG_SYSTEM_MODULES_DEPS_" + name
 			fmt.Fprintln(w, makevar, ":=$=", strings.Join(system.outputDeps.Strings(), " "))
 			fmt.Fprintln(w)
 
diff --git a/java/testing.go b/java/testing.go
index 5d116a7..08bae44 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -20,14 +20,115 @@
 	"android/soong/android"
 )
 
-func TestConfig(buildDir string, env map[string]string) android.Config {
+func TestConfig(buildDir string, env map[string]string, bp string, fs map[string][]byte) android.Config {
+	bp += GatherRequiredDepsForTest()
+
+	mockFS := map[string][]byte{
+		"a.java":                 nil,
+		"b.java":                 nil,
+		"c.java":                 nil,
+		"b.kt":                   nil,
+		"a.jar":                  nil,
+		"b.jar":                  nil,
+		"APP_NOTICE":             nil,
+		"GENRULE_NOTICE":         nil,
+		"LIB_NOTICE":             nil,
+		"TOOL_NOTICE":            nil,
+		"AndroidTest.xml":        nil,
+		"java-res/a/a":           nil,
+		"java-res/b/b":           nil,
+		"java-res2/a":            nil,
+		"java-fg/a.java":         nil,
+		"java-fg/b.java":         nil,
+		"java-fg/c.java":         nil,
+		"api/current.txt":        nil,
+		"api/removed.txt":        nil,
+		"api/system-current.txt": nil,
+		"api/system-removed.txt": nil,
+		"api/test-current.txt":   nil,
+		"api/test-removed.txt":   nil,
+		"framework/aidl/a.aidl":  nil,
+
+		"prebuilts/ndk/current/sources/cxx-stl/llvm-libc++/libs/arm64-v8a/libc++_shared.so": nil,
+
+		"prebuilts/sdk/14/public/android.jar":         nil,
+		"prebuilts/sdk/14/public/framework.aidl":      nil,
+		"prebuilts/sdk/14/system/android.jar":         nil,
+		"prebuilts/sdk/17/public/android.jar":         nil,
+		"prebuilts/sdk/17/public/framework.aidl":      nil,
+		"prebuilts/sdk/17/system/android.jar":         nil,
+		"prebuilts/sdk/29/public/android.jar":         nil,
+		"prebuilts/sdk/29/public/framework.aidl":      nil,
+		"prebuilts/sdk/29/system/android.jar":         nil,
+		"prebuilts/sdk/current/core/android.jar":      nil,
+		"prebuilts/sdk/current/public/android.jar":    nil,
+		"prebuilts/sdk/current/public/framework.aidl": nil,
+		"prebuilts/sdk/current/public/core.jar":       nil,
+		"prebuilts/sdk/current/system/android.jar":    nil,
+		"prebuilts/sdk/current/test/android.jar":      nil,
+		"prebuilts/sdk/28/public/api/foo.txt":         nil,
+		"prebuilts/sdk/28/system/api/foo.txt":         nil,
+		"prebuilts/sdk/28/test/api/foo.txt":           nil,
+		"prebuilts/sdk/28/public/api/foo-removed.txt": nil,
+		"prebuilts/sdk/28/system/api/foo-removed.txt": nil,
+		"prebuilts/sdk/28/test/api/foo-removed.txt":   nil,
+		"prebuilts/sdk/28/public/api/bar.txt":         nil,
+		"prebuilts/sdk/28/system/api/bar.txt":         nil,
+		"prebuilts/sdk/28/test/api/bar.txt":           nil,
+		"prebuilts/sdk/28/public/api/bar-removed.txt": nil,
+		"prebuilts/sdk/28/system/api/bar-removed.txt": nil,
+		"prebuilts/sdk/28/test/api/bar-removed.txt":   nil,
+		"prebuilts/sdk/tools/core-lambda-stubs.jar":   nil,
+		"prebuilts/sdk/Android.bp":                    []byte(`prebuilt_apis { name: "sdk", api_dirs: ["14", "28", "current"],}`),
+
+		"prebuilts/apk/app.apk":        nil,
+		"prebuilts/apk/app_arm.apk":    nil,
+		"prebuilts/apk/app_arm64.apk":  nil,
+		"prebuilts/apk/app_xhdpi.apk":  nil,
+		"prebuilts/apk/app_xxhdpi.apk": nil,
+
+		// For framework-res, which is an implicit dependency for framework
+		"AndroidManifest.xml":                        nil,
+		"build/make/target/product/security/testkey": nil,
+
+		"build/soong/scripts/jar-wrapper.sh": nil,
+
+		"build/make/core/verify_uses_libraries.sh": nil,
+
+		"build/make/core/proguard.flags":             nil,
+		"build/make/core/proguard_basic_keeps.flags": nil,
+
+		"jdk8/jre/lib/jce.jar": nil,
+		"jdk8/jre/lib/rt.jar":  nil,
+		"jdk8/lib/tools.jar":   nil,
+
+		"bar-doc/a.java":                 nil,
+		"bar-doc/b.java":                 nil,
+		"bar-doc/IFoo.aidl":              nil,
+		"bar-doc/IBar.aidl":              nil,
+		"bar-doc/known_oj_tags.txt":      nil,
+		"external/doclava/templates-sdk": nil,
+
+		"cert/new_cert.x509.pem": nil,
+		"cert/new_cert.pk8":      nil,
+
+		"testdata/data": nil,
+
+		"stubs-sources/foo/Foo.java": nil,
+		"stubs/sources/foo/Foo.java": nil,
+	}
+
+	for k, v := range fs {
+		mockFS[k] = v
+	}
+
 	if env == nil {
 		env = make(map[string]string)
 	}
 	if env["ANDROID_JAVA8_HOME"] == "" {
 		env["ANDROID_JAVA8_HOME"] = "jdk8"
 	}
-	config := android.TestArchConfig(buildDir, env)
+	config := android.TestArchConfig(buildDir, env, bp, mockFS)
 
 	return config
 }
@@ -102,18 +203,20 @@
 	`
 
 	systemModules := []string{
-		"core-system-modules",
 		"core-current-stubs-system-modules",
 		"core-platform-api-stubs-system-modules",
-		"android_stubs_current_system_modules",
-		"android_system_stubs_current_system_modules",
-		"android_test_stubs_current_system_modules",
 	}
 
 	for _, extra := range systemModules {
 		bp += fmt.Sprintf(`
 			java_system_modules {
-				name: "%s",
+				name: "%[1]s",
+				libs: ["%[1]s-lib"],
+			}
+			java_library {
+				name: "%[1]s-lib",
+				sdk_version: "none",
+				system_modules: "none",
 			}
 		`, extra)
 	}
diff --git a/java/tradefed.go b/java/tradefed.go
new file mode 100644
index 0000000..ebbdec1
--- /dev/null
+++ b/java/tradefed.go
@@ -0,0 +1,37 @@
+// 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 java
+
+import (
+	"android/soong/android"
+)
+
+func init() {
+	android.RegisterModuleType("tradefed_java_library_host", tradefedJavaLibraryFactory)
+}
+
+// tradefed_java_library_factory wraps java_library and installs an additional
+// copy of the output jar to $HOST_OUT/tradefed.
+func tradefedJavaLibraryFactory() android.Module {
+	module := LibraryHostFactory().(*Library)
+	module.InstallMixin = tradefedJavaLibraryInstall
+	return module
+}
+
+func tradefedJavaLibraryInstall(ctx android.ModuleContext, path android.Path) android.Paths {
+	installedPath := ctx.InstallFile(android.PathForModuleInstall(ctx, "tradefed"),
+		ctx.ModuleName()+".jar", path)
+	return android.Paths{installedPath}
+}
diff --git a/makedeps/Android.bp b/makedeps/Android.bp
new file mode 100644
index 0000000..b77b08f
--- /dev/null
+++ b/makedeps/Android.bp
@@ -0,0 +1,21 @@
+// 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.
+
+bootstrap_go_package {
+    name: "soong-makedeps",
+    pkgPath: "android/soong/makedeps",
+    deps: ["androidmk-parser"],
+    srcs: ["deps.go"],
+    testSrcs: ["deps_test.go"],
+}
diff --git a/cmd/dep_fixer/deps.go b/makedeps/deps.go
similarity index 91%
rename from cmd/dep_fixer/deps.go
rename to makedeps/deps.go
index 64c97f5..db49532 100644
--- a/cmd/dep_fixer/deps.go
+++ b/makedeps/deps.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package main
+package makedeps
 
 import (
 	"bytes"
@@ -57,10 +57,12 @@
 				return nil, fmt.Errorf("%sunsupported variable expansion: %v", pos(node), x.Target.Dump())
 			}
 			outputs := x.Target.Words()
-			if len(outputs) == 0 {
-				return nil, fmt.Errorf("%smissing output: %v", pos(node), x)
+			if len(outputs) > 0 {
+				ret.Output = outputs[0].Value(nil)
+			} else {
+				// TODO(b/141372861): put this back
+				//return nil, fmt.Errorf("%smissing output: %v", pos(node), x)
 			}
-			ret.Output = outputs[0].Value(nil)
 
 			if !x.Prerequisites.Const() {
 				return nil, fmt.Errorf("%sunsupported variable expansion: %v", pos(node), x.Prerequisites.Dump())
diff --git a/cmd/dep_fixer/deps_test.go b/makedeps/deps_test.go
similarity index 96%
rename from cmd/dep_fixer/deps_test.go
rename to makedeps/deps_test.go
index 0a779b7..ac2f699 100644
--- a/cmd/dep_fixer/deps_test.go
+++ b/makedeps/deps_test.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package main
+package makedeps
 
 import (
 	"bytes"
@@ -147,6 +147,20 @@
 				},
 			},
 		},
+		{
+			// TODO(b/141372861): remove this
+			// AIDL produces a dep file with no output file for a parcelable (b/
+			name: "AIDL parcelable",
+			input: ` : \
+  frameworks/base/tests/net/integration/src/com/android/server/net/integrationtests/HttpResponse.aidl
+`,
+			output: Deps{
+				Output: "",
+				Inputs: []string{
+					"frameworks/base/tests/net/integration/src/com/android/server/net/integrationtests/HttpResponse.aidl",
+				},
+			},
+		},
 	}
 
 	for _, tc := range testCases {
diff --git a/partner/Android.bp b/partner/Android.bp
new file mode 100644
index 0000000..f2ced8d
--- /dev/null
+++ b/partner/Android.bp
@@ -0,0 +1,49 @@
+// 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.
+
+//
+// Sample project for creating an extended androidmk
+//
+
+blueprint_go_binary {
+    name: "partner_androidmk",
+    srcs: [
+        "androidmk/androidmk.go",
+    ],
+    testSrcs: [
+        "androidmk/androidmk_test.go",
+    ],
+    deps: [
+        "androidmk-lib",
+        "partner-bpfix-extensions",
+    ],
+}
+
+blueprint_go_binary {
+    name: "partner_bpfix",
+    srcs: [
+        "bpfix/bpfix.go",
+    ],
+    deps: [
+        "bpfix-cmd",
+        "partner-bpfix-extensions",
+    ],
+}
+
+bootstrap_go_package {
+    name: "partner-bpfix-extensions",
+    pkgPath: "android/soong/partner/bpfix/extensions",
+    srcs: ["bpfix/extensions/headers.go"],
+    deps: ["bpfix-lib"],
+}
diff --git a/partner/androidmk/androidmk.go b/partner/androidmk/androidmk.go
new file mode 100644
index 0000000..f49981b
--- /dev/null
+++ b/partner/androidmk/androidmk.go
@@ -0,0 +1,58 @@
+// Copyright 2017 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 main
+
+import (
+	"bytes"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"os"
+
+	"android/soong/androidmk/androidmk"
+
+	_ "android/soong/partner/bpfix/extensions"
+)
+
+var usage = func() {
+	fmt.Fprintf(os.Stderr, "usage: %s [flags] <inputFile>\n"+
+		"\n%s parses <inputFile> as an Android.mk file and attempts to output an analogous Android.bp file (to standard out)\n", os.Args[0], os.Args[0])
+	flag.PrintDefaults()
+	os.Exit(1)
+}
+
+func main() {
+	flag.Usage = usage
+	flag.Parse()
+	if len(flag.Args()) != 1 {
+		usage()
+	}
+	filePathToRead := flag.Arg(0)
+	b, err := ioutil.ReadFile(filePathToRead)
+	if err != nil {
+		fmt.Println(err.Error())
+		return
+	}
+
+	output, errs := androidmk.ConvertFile(os.Args[1], bytes.NewBuffer(b))
+	if len(errs) > 0 {
+		for _, err := range errs {
+			fmt.Fprintln(os.Stderr, "ERROR: ", err)
+		}
+		os.Exit(1)
+	}
+
+	fmt.Print(output)
+}
diff --git a/partner/androidmk/androidmk_test.go b/partner/androidmk/androidmk_test.go
new file mode 100644
index 0000000..6bae836
--- /dev/null
+++ b/partner/androidmk/androidmk_test.go
@@ -0,0 +1,73 @@
+// 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 main
+
+import (
+	"bytes"
+	"fmt"
+	"strings"
+	"testing"
+
+	"android/soong/androidmk/androidmk"
+	"android/soong/bpfix/bpfix"
+
+	_ "android/soong/partner/bpfix/extensions"
+)
+
+var testCases = []struct {
+	desc     string
+	in       string
+	expected string
+}{
+	{
+		desc: "headers replacement",
+		in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := test
+LOCAL_SRC_FILES := a.c
+LOCAL_C_INCLUDES := test1 $(TARGET_OUT_HEADERS)/my_headers test2
+include $(BUILD_SHARED_LIBRARY)`,
+		expected: `
+cc_library_shared {
+    name: "test",
+	srcs: ["a.c"],
+	include_dirs: [
+		"test1",
+
+		"test2",
+	],
+	header_libs: ["my_header_lib"]
+}`,
+	},
+}
+
+func TestEndToEnd(t *testing.T) {
+	for i, test := range testCases {
+		expected, err := bpfix.Reformat(test.expected)
+		if err != nil {
+			t.Error(err)
+		}
+
+		got, errs := androidmk.ConvertFile(fmt.Sprintf("<testcase %d>", i), bytes.NewBufferString(test.in))
+		if len(errs) > 0 {
+			t.Errorf("Unexpected errors: %q", errs)
+			continue
+		}
+
+		if got != expected {
+			t.Errorf("failed testcase '%s'\ninput:\n%s\n\nexpected:\n%s\ngot:\n%s\n", test.desc, strings.TrimSpace(test.in), expected, got)
+		}
+	}
+}
diff --git a/partner/bpfix/bpfix.go b/partner/bpfix/bpfix.go
new file mode 100644
index 0000000..687fe1c
--- /dev/null
+++ b/partner/bpfix/bpfix.go
@@ -0,0 +1,27 @@
+// Copyright 2017 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.
+
+// This file provides a command-line interface to bpfix
+
+package main
+
+import (
+	"android/soong/bpfix/cmd_lib"
+
+	_ "android/soong/partner/bpfix/extensions"
+)
+
+func main() {
+	cmd_lib.Run()
+}
diff --git a/partner/bpfix/extensions/headers.go b/partner/bpfix/extensions/headers.go
new file mode 100644
index 0000000..169dab6
--- /dev/null
+++ b/partner/bpfix/extensions/headers.go
@@ -0,0 +1,120 @@
+package extensions
+
+import (
+	"strings"
+
+	"github.com/google/blueprint/parser"
+
+	"android/soong/bpfix/bpfix"
+)
+
+var fixSteps = bpfix.FixStepsExtension{
+	Name: "partner-include-dirs",
+	Steps: []bpfix.FixStep{
+		{
+			Name: "fixIncludeDirs",
+			Fix:  fixIncludeDirs,
+		},
+	},
+}
+
+func init() {
+	bpfix.RegisterFixStepExtension(&fixSteps)
+}
+
+type includeDirFix struct {
+	libName  string
+	libType  string
+	variable string
+	subdir   string
+}
+
+var commonIncludeDirs = []includeDirFix{
+	{
+		libName:  "my_header_lib",
+		libType:  "header_libs",
+		variable: "TARGET_OUT_HEADERS",
+		subdir:   "/my_headers",
+	},
+}
+
+func findHeaderLib(e parser.Expression) (*includeDirFix, bool) {
+	if op, ok := e.(*parser.Operator); ok {
+		if op.Operator != '+' {
+			return nil, false
+		}
+		arg0, ok := op.Args[0].(*parser.Variable)
+		arg1, ok1 := op.Args[1].(*parser.String)
+		if !ok || !ok1 {
+			return nil, false
+		}
+		for _, lib := range commonIncludeDirs {
+			if arg0.Name == lib.variable && arg1.Value == lib.subdir {
+				return &lib, true
+			}
+		}
+	}
+	return nil, false
+}
+func searchThroughOperatorList(mod *parser.Module, e parser.Expression) {
+	if list, ok := e.(*parser.List); ok {
+		newList := make([]parser.Expression, 0, len(list.Values))
+		for _, item := range list.Values {
+			if lib, found := findHeaderLib(item); found {
+				if lib.libName != "" {
+					addLibrary(mod, lib.libType, lib.libName)
+				}
+			} else {
+				newList = append(newList, item)
+			}
+		}
+		list.Values = newList
+	}
+	if op, ok := e.(*parser.Operator); ok {
+		searchThroughOperatorList(mod, op.Args[0])
+		searchThroughOperatorList(mod, op.Args[1])
+	}
+}
+func getLiteralListProperty(mod *parser.Module, name string) (list *parser.List, found bool) {
+	prop, ok := mod.GetProperty(name)
+	if !ok {
+		return nil, false
+	}
+	list, ok = prop.Value.(*parser.List)
+	return list, ok
+}
+func addLibrary(mod *parser.Module, libType string, libName string) {
+	var list, ok = getLiteralListProperty(mod, libType)
+	if !ok {
+		list = new(parser.List)
+		prop := new(parser.Property)
+		prop.Name = libType
+		prop.Value = list
+		mod.Properties = append(mod.Properties, prop)
+	} else {
+		for _, v := range list.Values {
+			if stringValue, ok := v.(*parser.String); ok && stringValue.Value == libName {
+				return
+			}
+		}
+	}
+	lib := new(parser.String)
+	lib.Value = libName
+	list.Values = append(list.Values, lib)
+}
+func fixIncludeDirs(f *bpfix.Fixer) error {
+	tree := f.Tree()
+	for _, def := range tree.Defs {
+		mod, ok := def.(*parser.Module)
+		if !ok {
+			continue
+		}
+		if !strings.HasPrefix(mod.Type, "cc_") {
+			continue
+		}
+		if prop, ok := mod.GetProperty("include_dirs"); ok {
+			searchThroughOperatorList(mod, prop.Value)
+		}
+	}
+	return nil
+}
diff --git a/python/androidmk.go b/python/androidmk.go
index 1e51e7b..8e8e8ef 100644
--- a/python/androidmk.go
+++ b/python/androidmk.go
@@ -76,6 +76,10 @@
 					p.testConfig.String())
 			}
 		}
+
+		if !BoolDefault(p.binaryProperties.Auto_gen_config, true) {
+			fmt.Fprintln(w, "LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG := true")
+		}
 	})
 	base.subAndroidMk(ret, p.binaryDecorator.pythonInstaller)
 }
@@ -89,12 +93,11 @@
 
 	ret.Required = append(ret.Required, "libc++")
 	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
-		path := installer.path.RelPathString()
-		dir, file := filepath.Split(path)
+		path, file := filepath.Split(installer.path.ToMakePath().String())
 		stem := strings.TrimSuffix(file, filepath.Ext(file))
 
 		fmt.Fprintln(w, "LOCAL_MODULE_SUFFIX := "+filepath.Ext(file))
-		fmt.Fprintln(w, "LOCAL_MODULE_PATH := $(OUT_DIR)/"+filepath.Clean(dir))
+		fmt.Fprintln(w, "LOCAL_MODULE_PATH := "+path)
 		fmt.Fprintln(w, "LOCAL_MODULE_STEM := "+stem)
 		fmt.Fprintln(w, "LOCAL_SHARED_LIBRARIES := "+strings.Join(installer.androidMkSharedLibs, " "))
 	})
diff --git a/python/binary.go b/python/binary.go
index 140f07a..695fa12 100644
--- a/python/binary.go
+++ b/python/binary.go
@@ -47,6 +47,11 @@
 	// false it will act much like the normal `python` executable, but with the sources and
 	// libraries automatically included in the PYTHONPATH.
 	Autorun *bool `android:"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 binaryDecorator struct {
diff --git a/python/installer.go b/python/installer.go
index 62f36f4..396f036 100644
--- a/python/installer.go
+++ b/python/installer.go
@@ -33,7 +33,7 @@
 	dir64    string
 	relative string
 
-	path android.OutputPath
+	path android.InstallPath
 
 	androidMkSharedLibs []string
 }
@@ -47,12 +47,12 @@
 
 var _ installer = (*pythonInstaller)(nil)
 
-func (installer *pythonInstaller) installDir(ctx android.ModuleContext) android.OutputPath {
+func (installer *pythonInstaller) installDir(ctx android.ModuleContext) android.InstallPath {
 	dir := installer.dir
 	if ctx.Arch().ArchType.Multilib == "lib64" && installer.dir64 != "" {
 		dir = installer.dir64
 	}
-	if !ctx.Host() && !ctx.Arch().Native {
+	if !ctx.Host() && ctx.Config().HasMultilibConflict(ctx.Arch().ArchType) {
 		dir = filepath.Join(dir, ctx.Arch().ArchType.String())
 	}
 	return android.PathForModuleInstall(ctx, dir, installer.relative)
diff --git a/python/proto.go b/python/proto.go
index 85ed1a5..b71e047 100644
--- a/python/proto.go
+++ b/python/proto.go
@@ -34,7 +34,7 @@
 	// Proto generated python files have an unknown package name in the path, so package the entire output directory
 	// into a srcszip.
 	zipCmd := rule.Command().
-		Tool(ctx.Config().HostToolPath(ctx, "soong_zip")).
+		BuiltTool(ctx, "soong_zip").
 		FlagWithOutput("-o ", srcsZipFile)
 	if pkgPath != "" {
 		zipCmd.FlagWithArg("-P ", pkgPath)
diff --git a/python/python.go b/python/python.go
index ad08909..c67c577 100644
--- a/python/python.go
+++ b/python/python.go
@@ -306,22 +306,17 @@
 			if p.bootstrapper.autorun() {
 				launcherModule = "py2-launcher-autorun"
 			}
-			ctx.AddFarVariationDependencies([]blueprint.Variation{
-				{Mutator: "arch", Variation: ctx.Target().String()},
-			}, launcherTag, launcherModule)
+			ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherTag, launcherModule)
 
 			// Add py2-launcher shared lib dependencies. Ideally, these should be
 			// derived from the `shared_libs` property of "py2-launcher". However, we
 			// cannot read the property at this stage and it will be too late to add
 			// dependencies later.
-			ctx.AddFarVariationDependencies([]blueprint.Variation{
-				{Mutator: "arch", Variation: ctx.Target().String()},
-			}, launcherSharedLibTag, "libsqlite")
+			ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherSharedLibTag, "libsqlite")
 
 			if ctx.Target().Os.Bionic() {
-				ctx.AddFarVariationDependencies([]blueprint.Variation{
-					{Mutator: "arch", Variation: ctx.Target().String()},
-				}, launcherSharedLibTag, "libc", "libdl", "libm")
+				ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherSharedLibTag,
+					"libc", "libdl", "libm")
 			}
 		}
 
@@ -331,9 +326,24 @@
 				p.properties.Version.Py3.Libs)...)
 
 		if p.bootstrapper != nil && p.isEmbeddedLauncherEnabled(pyVersion3) {
-			//TODO(nanzhang): Add embedded launcher for Python3.
-			ctx.PropertyErrorf("version.py3.embedded_launcher",
-				"is not supported yet for Python3.")
+			ctx.AddVariationDependencies(nil, pythonLibTag, "py3-stdlib")
+
+			launcherModule := "py3-launcher"
+			if p.bootstrapper.autorun() {
+				launcherModule = "py3-launcher-autorun"
+			}
+			ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherTag, launcherModule)
+
+			// Add py3-launcher shared lib dependencies. Ideally, these should be
+			// derived from the `shared_libs` property of "py3-launcher". However, we
+			// cannot read the property at this stage and it will be too late to add
+			// dependencies later.
+			ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherSharedLibTag, "libsqlite")
+
+			if ctx.Target().Os.Bionic() {
+				ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherSharedLibTag,
+					"libc", "libdl", "libm")
+			}
 		}
 	default:
 		panic(fmt.Errorf("unknown Python Actual_version: %q for module: %q.",
@@ -375,11 +385,11 @@
 	// Only Python binaries and test has non-empty bootstrapper.
 	if p.bootstrapper != nil {
 		p.walkTransitiveDeps(ctx)
-		// TODO(nanzhang): Since embedded launcher is not supported for Python3 for now,
-		// so we initialize "embedded_launcher" to false.
 		embeddedLauncher := false
 		if p.properties.Actual_version == pyVersion2 {
 			embeddedLauncher = p.isEmbeddedLauncherEnabled(pyVersion2)
+		} else {
+			embeddedLauncher = p.isEmbeddedLauncherEnabled(pyVersion3)
 		}
 		p.installSource = p.bootstrapper.bootstrap(ctx, p.properties.Actual_version,
 			embeddedLauncher, p.srcsPathMappings, p.srcsZip, p.depsSrcsZips)
diff --git a/python/python_test.go b/python/python_test.go
index e5fe126..1245ca1 100644
--- a/python/python_test.go
+++ b/python/python_test.go
@@ -28,6 +28,8 @@
 	"android/soong/android"
 )
 
+var buildDir string
+
 type pyModule struct {
 	name          string
 	actualVersion string
@@ -50,7 +52,7 @@
 	noSrcFileErr      = moduleVariantErrTemplate + "doesn't have any source files!"
 	badSrcFileExtErr  = moduleVariantErrTemplate + "srcs: found non (.py|.proto) file: %q!"
 	badDataFileExtErr = moduleVariantErrTemplate + "data: found (.py|.proto) file: %q!"
-	bpFile            = "Blueprints"
+	bpFile            = "Android.bp"
 
 	data = []struct {
 		desc      string
@@ -71,7 +73,7 @@
 			},
 			errors: []string{
 				fmt.Sprintf(noSrcFileErr,
-					"dir/Blueprints:1:1", "lib1", "PY3"),
+					"dir/Android.bp:1:1", "lib1", "PY3"),
 			},
 		},
 		{
@@ -90,7 +92,7 @@
 			},
 			errors: []string{
 				fmt.Sprintf(badSrcFileExtErr,
-					"dir/Blueprints:3:11", "lib1", "PY3", "dir/file1.exe"),
+					"dir/Android.bp:3:11", "lib1", "PY3", "dir/file1.exe"),
 			},
 		},
 		{
@@ -113,7 +115,7 @@
 			},
 			errors: []string{
 				fmt.Sprintf(badDataFileExtErr,
-					"dir/Blueprints:6:11", "lib1", "PY3", "dir/file2.py"),
+					"dir/Android.bp:6:11", "lib1", "PY3", "dir/file2.py"),
 			},
 		},
 		{
@@ -149,9 +151,9 @@
 			},
 			errors: []string{
 				fmt.Sprintf(pkgPathErrTemplate,
-					"dir/Blueprints:11:15", "lib2", "PY3", "a/c/../../../"),
+					"dir/Android.bp:11:15", "lib2", "PY3", "a/c/../../../"),
 				fmt.Sprintf(pkgPathErrTemplate,
-					"dir/Blueprints:19:15", "lib3", "PY3", "/a/c/../../"),
+					"dir/Android.bp:19:15", "lib3", "PY3", "/a/c/../../"),
 			},
 		},
 		{
@@ -174,11 +176,11 @@
 				"dir/-e/f/file1.py": nil,
 			},
 			errors: []string{
-				fmt.Sprintf(badIdentifierErrTemplate, "dir/Blueprints:4:11",
+				fmt.Sprintf(badIdentifierErrTemplate, "dir/Android.bp:4:11",
 					"lib1", "PY3", "a/b/c/-e/f/file1.py", "-e"),
-				fmt.Sprintf(badIdentifierErrTemplate, "dir/Blueprints:4:11",
+				fmt.Sprintf(badIdentifierErrTemplate, "dir/Android.bp:4:11",
 					"lib1", "PY3", "a/b/c/.file1.py", ".file1"),
-				fmt.Sprintf(badIdentifierErrTemplate, "dir/Blueprints:4:11",
+				fmt.Sprintf(badIdentifierErrTemplate, "dir/Android.bp:4:11",
 					"lib1", "PY3", "a/b/c/123/file1.py", "123"),
 			},
 		},
@@ -211,7 +213,7 @@
 				"dir/file1.py":   nil,
 			},
 			errors: []string{
-				fmt.Sprintf(dupRunfileErrTemplate, "dir/Blueprints:9:6",
+				fmt.Sprintf(dupRunfileErrTemplate, "dir/Android.bp:9:6",
 					"lib2", "PY3", "a/b/c/file1.py", "lib2", "dir/file1.py",
 					"lib1", "dir/c/file1.py"),
 			},
@@ -324,23 +326,18 @@
 )
 
 func TestPythonModule(t *testing.T) {
-	config, buildDir := setupBuildEnv(t)
-	defer tearDownBuildEnv(buildDir)
 	for _, d := range data {
 		t.Run(d.desc, func(t *testing.T) {
+			config := android.TestConfig(buildDir, nil, "", d.mockFiles)
 			ctx := android.NewTestContext()
 			ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
 				ctx.BottomUp("version_split", versionSplitMutator()).Parallel()
 			})
-			ctx.RegisterModuleType("python_library_host",
-				android.ModuleFactoryAdaptor(PythonLibraryHostFactory))
-			ctx.RegisterModuleType("python_binary_host",
-				android.ModuleFactoryAdaptor(PythonBinaryHostFactory))
-			ctx.RegisterModuleType("python_defaults",
-				android.ModuleFactoryAdaptor(defaultsFactory))
+			ctx.RegisterModuleType("python_library_host", PythonLibraryHostFactory)
+			ctx.RegisterModuleType("python_binary_host", PythonBinaryHostFactory)
+			ctx.RegisterModuleType("python_defaults", defaultsFactory)
 			ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
-			ctx.Register()
-			ctx.MockFileSystem(d.mockFiles)
+			ctx.Register(config)
 			_, testErrs := ctx.ParseBlueprintsFiles(bpFile)
 			android.FailIfErrored(t, testErrs)
 			_, actErrs := ctx.PrepareBuildActions(config)
@@ -428,17 +425,25 @@
 	return
 }
 
-func setupBuildEnv(t *testing.T) (config android.Config, buildDir string) {
-	buildDir, err := ioutil.TempDir("", buildNamePrefix)
+func setUp() {
+	var err error
+	buildDir, err = ioutil.TempDir("", "soong_python_test")
 	if err != nil {
-		t.Fatal(err)
+		panic(err)
 	}
-
-	config = android.TestConfig(buildDir, nil)
-
-	return
 }
 
-func tearDownBuildEnv(buildDir string) {
+func tearDown() {
 	os.RemoveAll(buildDir)
 }
+
+func TestMain(m *testing.M) {
+	run := func() int {
+		setUp()
+		defer tearDown()
+
+		return m.Run()
+	}
+
+	os.Exit(run())
+}
diff --git a/python/test.go b/python/test.go
index 55b0ab5..f684fd5 100644
--- a/python/test.go
+++ b/python/test.go
@@ -50,7 +50,8 @@
 
 func (test *testDecorator) install(ctx android.ModuleContext, file android.Path) {
 	test.testConfig = tradefed.AutoGenPythonBinaryHostTestConfig(ctx, test.testProperties.Test_config,
-		test.testProperties.Test_config_template, test.binaryDecorator.binaryProperties.Test_suites)
+		test.testProperties.Test_config_template, test.binaryDecorator.binaryProperties.Test_suites,
+		test.binaryDecorator.binaryProperties.Auto_gen_config)
 
 	test.binaryDecorator.pythonInstaller.dir = "nativetest"
 	test.binaryDecorator.pythonInstaller.dir64 = "nativetest64"
diff --git a/python/tests/Android.bp b/python/tests/Android.bp
index 1f4305c..c8bf420 100644
--- a/python/tests/Android.bp
+++ b/python/tests/Android.bp
@@ -27,6 +27,22 @@
         },
         py3: {
             enabled: false,
+            embedded_launcher: true,
+        },
+    },
+}
+
+python_test_host {
+    name: "par_test3",
+    main: "par_test.py",
+    srcs: [
+        "par_test.py",
+        "testpkg/par_test.py",
+    ],
+
+    version: {
+        py3: {
+            embedded_launcher: true,
         },
     },
 }
diff --git a/python/tests/par_test.py b/python/tests/par_test.py
index 1fafe0f..56a5063 100644
--- a/python/tests/par_test.py
+++ b/python/tests/par_test.py
@@ -44,6 +44,13 @@
 assert_equal("sys.path[1]", sys.path[1], os.path.join(archive, "internal"))
 assert_equal("sys.path[2]", sys.path[2], os.path.join(archive, "internal", "stdlib"))
 
+if os.getenv('ARGTEST', False):
+    assert_equal("len(sys.argv)", len(sys.argv), 3)
+    assert_equal("sys.argv[1]", sys.argv[1], "--arg1")
+    assert_equal("sys.argv[2]", sys.argv[2], "arg2")
+else:
+    assert_equal("len(sys.argv)", len(sys.argv), 1)
+
 if failed:
     sys.exit(1)
 
diff --git a/python/tests/runtest.sh b/python/tests/runtest.sh
index a319558..21187ed 100755
--- a/python/tests/runtest.sh
+++ b/python/tests/runtest.sh
@@ -23,8 +23,8 @@
   exit 1
 fi
 
-if [ ! -f $ANDROID_HOST_OUT/nativetest64/par_test/par_test ]; then
-  echo "Run 'm par_test' first"
+if [[ ( ! -f $ANDROID_HOST_OUT/nativetest64/par_test/par_test ) || ( ! -f $ANDROID_HOST_OUT/nativetest64/par_test3/par_test3 ) ]]; then
+  echo "Run 'm par_test par_test3' first"
   exit 1
 fi
 
@@ -36,4 +36,12 @@
 PYTHONHOME=/usr $ANDROID_HOST_OUT/nativetest64/par_test/par_test
 PYTHONPATH=/usr $ANDROID_HOST_OUT/nativetest64/par_test/par_test
 
+ARGTEST=true $ANDROID_HOST_OUT/nativetest64/par_test/par_test --arg1 arg2
+
+PYTHONHOME= PYTHONPATH= $ANDROID_HOST_OUT/nativetest64/par_test3/par_test3
+PYTHONHOME=/usr $ANDROID_HOST_OUT/nativetest64/par_test3/par_test3
+PYTHONPATH=/usr $ANDROID_HOST_OUT/nativetest64/par_test3/par_test3
+
+ARGTEST=true $ANDROID_HOST_OUT/nativetest64/par_test3/par_test3 --arg1 arg2
+
 echo "Passed!"
diff --git a/python/tests/testpkg/par_test.py b/python/tests/testpkg/par_test.py
index 22dd095..ffad430 100644
--- a/python/tests/testpkg/par_test.py
+++ b/python/tests/testpkg/par_test.py
@@ -29,7 +29,13 @@
 
 assert_equal("__name__", __name__, "testpkg.par_test")
 assert_equal("__file__", __file__, os.path.join(archive, "testpkg/par_test.py"))
-assert_equal("__package__", __package__, "testpkg")
+
+# Python3 is returning None here for me, and I haven't found any problems caused by this.
+if sys.version_info[0] == 2:
+  assert_equal("__package__", __package__, "testpkg")
+else:
+  assert_equal("__package__", __package__, None)
+
 assert_equal("__loader__.archive", __loader__.archive, archive)
 assert_equal("__loader__.prefix", __loader__.prefix, "testpkg/")
 
diff --git a/rust/OWNERS b/rust/OWNERS
new file mode 100644
index 0000000..82713f9
--- /dev/null
+++ b/rust/OWNERS
@@ -0,0 +1,5 @@
+# Additional owner/reviewers for rust rules, including parent directory owners.
+per-file * = chh@google.com, ivanlozano@google.com, jeffv@google.com, srhines@google.com
+
+# Limited owners/reviewers of the whitelist.
+per-file whitelist.go = chh@google.com, ivanlozano@google.com, jeffv@google.com, jgalenson@google.com, srhines@google.com
diff --git a/rust/androidmk.go b/rust/androidmk.go
new file mode 100644
index 0000000..0fba739
--- /dev/null
+++ b/rust/androidmk.go
@@ -0,0 +1,151 @@
+// Copyright 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rust
+
+import (
+	"fmt"
+	"io"
+	"path/filepath"
+	"strings"
+
+	"android/soong/android"
+)
+
+type AndroidMkContext interface {
+	Name() string
+	Target() android.Target
+	subAndroidMk(*android.AndroidMkData, interface{})
+}
+
+type subAndroidMkProvider interface {
+	AndroidMk(AndroidMkContext, *android.AndroidMkData)
+}
+
+func (mod *Module) subAndroidMk(data *android.AndroidMkData, obj interface{}) {
+	if mod.subAndroidMkOnce == nil {
+		mod.subAndroidMkOnce = make(map[subAndroidMkProvider]bool)
+	}
+	if androidmk, ok := obj.(subAndroidMkProvider); ok {
+		if !mod.subAndroidMkOnce[androidmk] {
+			mod.subAndroidMkOnce[androidmk] = true
+			androidmk.AndroidMk(mod, data)
+		}
+	}
+}
+
+func (mod *Module) AndroidMk() android.AndroidMkData {
+	ret := android.AndroidMkData{
+		OutputFile: mod.outputFile,
+		Include:    "$(BUILD_SYSTEM)/soong_rust_prebuilt.mk",
+		Extra: []android.AndroidMkExtraFunc{
+			func(w io.Writer, outputFile android.Path) {
+				if len(mod.Properties.AndroidMkRlibs) > 0 {
+					fmt.Fprintln(w, "LOCAL_RLIB_LIBRARIES := "+strings.Join(mod.Properties.AndroidMkRlibs, " "))
+				}
+				if len(mod.Properties.AndroidMkDylibs) > 0 {
+					fmt.Fprintln(w, "LOCAL_DYLIB_LIBRARIES := "+strings.Join(mod.Properties.AndroidMkDylibs, " "))
+				}
+				if len(mod.Properties.AndroidMkProcMacroLibs) > 0 {
+					fmt.Fprintln(w, "LOCAL_PROC_MACRO_LIBRARIES := "+strings.Join(mod.Properties.AndroidMkProcMacroLibs, " "))
+				}
+				if len(mod.Properties.AndroidMkSharedLibs) > 0 {
+					fmt.Fprintln(w, "LOCAL_SHARED_LIBRARIES := "+strings.Join(mod.Properties.AndroidMkSharedLibs, " "))
+				}
+				if len(mod.Properties.AndroidMkStaticLibs) > 0 {
+					fmt.Fprintln(w, "LOCAL_STATIC_LIBRARIES := "+strings.Join(mod.Properties.AndroidMkStaticLibs, " "))
+				}
+			},
+		},
+	}
+
+	mod.subAndroidMk(&ret, mod.compiler)
+
+	ret.SubName += mod.Properties.SubName
+
+	return ret
+}
+
+func (binary *binaryDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
+	ctx.subAndroidMk(ret, binary.baseCompiler)
+
+	ret.Class = "EXECUTABLES"
+	ret.DistFile = binary.distFile
+	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
+		fmt.Fprintln(w, "LOCAL_SOONG_UNSTRIPPED_BINARY :=", binary.unstrippedOutputFile.String())
+	})
+}
+
+func (test *testDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
+	test.binaryDecorator.AndroidMk(ctx, ret)
+	ret.Class = "NATIVE_TESTS"
+	ret.SubName = test.getMutatedModuleSubName(ctx.Name())
+	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
+		if len(test.Properties.Test_suites) > 0 {
+			fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE :=",
+				strings.Join(test.Properties.Test_suites, " "))
+		}
+		if test.testConfig != nil {
+			fmt.Fprintln(w, "LOCAL_FULL_TEST_CONFIG :=", test.testConfig.String())
+		}
+		if !BoolDefault(test.Properties.Auto_gen_config, true) {
+			fmt.Fprintln(w, "LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG := true")
+		}
+	})
+	// TODO(chh): add test data with androidMkWriteTestData(test.data, ctx, ret)
+}
+
+func (library *libraryDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
+	ctx.subAndroidMk(ret, library.baseCompiler)
+
+	if library.rlib() {
+		ret.Class = "RLIB_LIBRARIES"
+	} else if library.dylib() {
+		ret.Class = "DYLIB_LIBRARIES"
+	} else if library.static() {
+		ret.Class = "STATIC_LIBRARIES"
+	} else if library.shared() {
+		ret.Class = "SHARED_LIBRARIES"
+	}
+
+	ret.DistFile = library.distFile
+	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
+		if !library.rlib() {
+			fmt.Fprintln(w, "LOCAL_SOONG_UNSTRIPPED_BINARY :=", library.unstrippedOutputFile.String())
+		}
+	})
+}
+
+func (procMacro *procMacroDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
+	ctx.subAndroidMk(ret, procMacro.baseCompiler)
+
+	ret.Class = "PROC_MACRO_LIBRARIES"
+	ret.DistFile = procMacro.distFile
+
+}
+
+func (compiler *baseCompiler) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
+	// Soong installation is only supported for host modules. Have Make
+	// installation trigger Soong installation.
+	if ctx.Target().Os.Class == android.Host {
+		ret.OutputFile = android.OptionalPathForPath(compiler.path)
+	}
+	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
+		path, file := filepath.Split(compiler.path.ToMakePath().String())
+		stem, suffix, _ := android.SplitFileExt(file)
+		fmt.Fprintln(w, "LOCAL_MODULE_SUFFIX := "+suffix)
+		fmt.Fprintln(w, "LOCAL_MODULE_PATH := "+path)
+		fmt.Fprintln(w, "LOCAL_MODULE_STEM := "+stem)
+	})
+}
diff --git a/rust/binary.go b/rust/binary.go
new file mode 100644
index 0000000..fda056e
--- /dev/null
+++ b/rust/binary.go
@@ -0,0 +1,120 @@
+// Copyright 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rust
+
+import (
+	"android/soong/android"
+)
+
+func init() {
+	android.RegisterModuleType("rust_binary", RustBinaryFactory)
+	android.RegisterModuleType("rust_binary_host", RustBinaryHostFactory)
+}
+
+type BinaryCompilerProperties struct {
+	// path to the main source file that contains the program entry point (e.g. src/main.rs)
+	Srcs []string `android:"path,arch_variant"`
+
+	// passes -C prefer-dynamic to rustc, which tells it to dynamically link the stdlib
+	// (assuming it has no dylib dependencies already)
+	Prefer_dynamic *bool
+}
+
+type binaryDecorator struct {
+	*baseCompiler
+
+	Properties           BinaryCompilerProperties
+	distFile             android.OptionalPath
+	unstrippedOutputFile android.Path
+}
+
+var _ compiler = (*binaryDecorator)(nil)
+
+// rust_binary produces a binary that is runnable on a device.
+func RustBinaryFactory() android.Module {
+	module, _ := NewRustBinary(android.HostAndDeviceSupported)
+	return module.Init()
+}
+
+func RustBinaryHostFactory() android.Module {
+	module, _ := NewRustBinary(android.HostSupported)
+	return module.Init()
+}
+
+func NewRustBinary(hod android.HostOrDeviceSupported) (*Module, *binaryDecorator) {
+	module := newModule(hod, android.MultilibFirst)
+
+	binary := &binaryDecorator{
+		baseCompiler: NewBaseCompiler("bin", "", InstallInSystem),
+	}
+
+	module.compiler = binary
+
+	return module, binary
+}
+
+func (binary *binaryDecorator) preferDynamic() bool {
+	return Bool(binary.Properties.Prefer_dynamic)
+}
+
+func (binary *binaryDecorator) compilerFlags(ctx ModuleContext, flags Flags) Flags {
+	flags = binary.baseCompiler.compilerFlags(ctx, flags)
+
+	if ctx.toolchain().Bionic() {
+		// no-undefined-version breaks dylib compilation since __rust_*alloc* functions aren't defined,
+		// but we can apply this to binaries.
+		flags.LinkFlags = append(flags.LinkFlags,
+			"-Wl,--gc-sections",
+			"-Wl,-z,nocopyreloc",
+			"-Wl,--no-undefined-version")
+	}
+
+	if binary.preferDynamic() {
+		flags.RustFlags = append(flags.RustFlags, "-C prefer-dynamic")
+	}
+	return flags
+}
+
+func (binary *binaryDecorator) compilerDeps(ctx DepsContext, deps Deps) Deps {
+	deps = binary.baseCompiler.compilerDeps(ctx, deps)
+
+	if ctx.toolchain().Bionic() {
+		deps = binary.baseCompiler.bionicDeps(ctx, deps)
+		deps.CrtBegin = "crtbegin_dynamic"
+		deps.CrtEnd = "crtend_android"
+	}
+
+	return deps
+}
+
+func (binary *binaryDecorator) compilerProps() []interface{} {
+	return append(binary.baseCompiler.compilerProps(),
+		&binary.Properties)
+}
+
+func (binary *binaryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path {
+	fileName := binary.getStem(ctx) + ctx.toolchain().ExecutableSuffix()
+
+	srcPath := srcPathFromModuleSrcs(ctx, binary.Properties.Srcs)
+
+	outputFile := android.PathForModuleOut(ctx, fileName)
+	binary.unstrippedOutputFile = outputFile
+
+	flags.RustFlags = append(flags.RustFlags, deps.depFlags...)
+
+	TransformSrcToBinary(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
+
+	return outputFile
+}
diff --git a/rust/binary_test.go b/rust/binary_test.go
new file mode 100644
index 0000000..ab2dae1
--- /dev/null
+++ b/rust/binary_test.go
@@ -0,0 +1,55 @@
+// Copyright 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rust
+
+import (
+	"strings"
+	"testing"
+)
+
+// Test that the prefer_dynamic property is handled correctly.
+func TestPreferDynamicBinary(t *testing.T) {
+	ctx := testRust(t, `
+		rust_binary_host {
+			name: "fizz-buzz-dynamic",
+			srcs: ["foo.rs"],
+			prefer_dynamic: true,
+		}
+
+		rust_binary_host {
+			name: "fizz-buzz",
+			srcs: ["foo.rs"],
+		}`)
+
+	fizzBuzz := ctx.ModuleForTests("fizz-buzz", "linux_glibc_x86_64").Output("fizz-buzz")
+	fizzBuzzDynamic := ctx.ModuleForTests("fizz-buzz-dynamic", "linux_glibc_x86_64").Output("fizz-buzz-dynamic")
+
+	// Do not compile binary modules with the --test flag.
+	flags := fizzBuzzDynamic.Args["rustcFlags"]
+	if strings.Contains(flags, "--test") {
+		t.Errorf("extra --test flag, rustcFlags: %#v", flags)
+	}
+	if !strings.Contains(flags, "prefer-dynamic") {
+		t.Errorf("missing prefer-dynamic flag, rustcFlags: %#v", flags)
+	}
+
+	flags = fizzBuzz.Args["rustcFlags"]
+	if strings.Contains(flags, "--test") {
+		t.Errorf("extra --test flag, rustcFlags: %#v", flags)
+	}
+	if strings.Contains(flags, "prefer-dynamic") {
+		t.Errorf("unexpected prefer-dynamic flag, rustcFlags: %#v", flags)
+	}
+}
diff --git a/rust/builder.go b/rust/builder.go
new file mode 100644
index 0000000..27eeec2
--- /dev/null
+++ b/rust/builder.go
@@ -0,0 +1,159 @@
+// Copyright 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rust
+
+import (
+	"strings"
+
+	"github.com/google/blueprint"
+
+	"android/soong/android"
+)
+
+var (
+	_     = pctx.SourcePathVariable("rustcCmd", "${config.RustBin}/rustc")
+	rustc = pctx.AndroidStaticRule("rustc",
+		blueprint.RuleParams{
+			Command: "$rustcCmd " +
+				"-C linker=${config.RustLinker} " +
+				"-C link-args=\"${crtBegin} ${config.RustLinkerArgs} ${linkFlags} ${crtEnd}\" " +
+				"--emit link -o $out --emit dep-info=$out.d $in ${libFlags} $rustcFlags",
+			CommandDeps: []string{"$rustcCmd"},
+			// Rustc deps-info writes out make compatible dep files: https://github.com/rust-lang/rust/issues/7633
+			Deps:    blueprint.DepsGCC,
+			Depfile: "$out.d",
+		},
+		"rustcFlags", "linkFlags", "libFlags", "crtBegin", "crtEnd")
+)
+
+func init() {
+
+}
+
+func TransformSrcToBinary(ctx android.ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
+	outputFile android.WritablePath, includeDirs []string) {
+	flags.RustFlags = append(flags.RustFlags, "-C lto")
+
+	transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "bin", includeDirs)
+}
+
+func TransformSrctoRlib(ctx android.ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
+	outputFile android.WritablePath, includeDirs []string) {
+	transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "rlib", includeDirs)
+}
+
+func TransformSrctoDylib(ctx android.ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
+	outputFile android.WritablePath, includeDirs []string) {
+	transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "dylib", includeDirs)
+}
+
+func TransformSrctoStatic(ctx android.ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
+	outputFile android.WritablePath, includeDirs []string) {
+	flags.RustFlags = append(flags.RustFlags, "-C lto")
+	transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "staticlib", includeDirs)
+}
+
+func TransformSrctoShared(ctx android.ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
+	outputFile android.WritablePath, includeDirs []string) {
+	flags.RustFlags = append(flags.RustFlags, "-C lto")
+	transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "cdylib", includeDirs)
+}
+
+func TransformSrctoProcMacro(ctx android.ModuleContext, mainSrc android.Path, deps PathDeps,
+	flags Flags, outputFile android.WritablePath, includeDirs []string) {
+	transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "proc-macro", includeDirs)
+}
+
+func rustLibsToPaths(libs RustLibraries) android.Paths {
+	var paths android.Paths
+	for _, lib := range libs {
+		paths = append(paths, lib.Path)
+	}
+	return paths
+}
+
+func transformSrctoCrate(ctx android.ModuleContext, main android.Path, deps PathDeps, flags Flags,
+	outputFile android.WritablePath, crate_type string, includeDirs []string) {
+
+	var inputs android.Paths
+	var implicits android.Paths
+	var libFlags, rustcFlags, linkFlags []string
+	crate_name := ctx.(ModuleContext).CrateName()
+	targetTriple := ctx.(ModuleContext).toolchain().RustTriple()
+
+	inputs = append(inputs, main)
+
+	// Collect rustc flags
+	rustcFlags = append(rustcFlags, flags.GlobalRustFlags...)
+	rustcFlags = append(rustcFlags, flags.RustFlags...)
+	rustcFlags = append(rustcFlags, "--crate-type="+crate_type)
+	if crate_name != "" {
+		rustcFlags = append(rustcFlags, "--crate-name="+crate_name)
+	}
+	if targetTriple != "" {
+		rustcFlags = append(rustcFlags, "--target="+targetTriple)
+		linkFlags = append(linkFlags, "-target "+targetTriple)
+	}
+	// TODO once we have static libraries in the host prebuilt .bp, this
+	// should be unconditionally added.
+	if !ctx.Host() {
+		// If we're on a device build, do not use an implicit sysroot
+		rustcFlags = append(rustcFlags, "--sysroot=/dev/null")
+	}
+	// Collect linker flags
+	linkFlags = append(linkFlags, flags.GlobalLinkFlags...)
+	linkFlags = append(linkFlags, flags.LinkFlags...)
+
+	// Collect library/crate flags
+	for _, lib := range deps.RLibs {
+		libFlags = append(libFlags, "--extern "+lib.CrateName+"="+lib.Path.String())
+	}
+	for _, lib := range deps.DyLibs {
+		libFlags = append(libFlags, "--extern "+lib.CrateName+"="+lib.Path.String())
+	}
+	for _, proc_macro := range deps.ProcMacros {
+		libFlags = append(libFlags, "--extern "+proc_macro.CrateName+"="+proc_macro.Path.String())
+	}
+
+	for _, path := range includeDirs {
+		libFlags = append(libFlags, "-L "+path)
+	}
+
+	// Collect dependencies
+	implicits = append(implicits, rustLibsToPaths(deps.RLibs)...)
+	implicits = append(implicits, rustLibsToPaths(deps.DyLibs)...)
+	implicits = append(implicits, rustLibsToPaths(deps.ProcMacros)...)
+	implicits = append(implicits, deps.StaticLibs...)
+	implicits = append(implicits, deps.SharedLibs...)
+	if deps.CrtBegin.Valid() {
+		implicits = append(implicits, deps.CrtBegin.Path(), deps.CrtEnd.Path())
+	}
+
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        rustc,
+		Description: "rustc " + main.Rel(),
+		Output:      outputFile,
+		Inputs:      inputs,
+		Implicits:   implicits,
+		Args: map[string]string{
+			"rustcFlags": strings.Join(rustcFlags, " "),
+			"linkFlags":  strings.Join(linkFlags, " "),
+			"libFlags":   strings.Join(libFlags, " "),
+			"crtBegin":   deps.CrtBegin.String(),
+			"crtEnd":     deps.CrtEnd.String(),
+		},
+	})
+
+}
diff --git a/rust/compiler.go b/rust/compiler.go
new file mode 100644
index 0000000..4593165
--- /dev/null
+++ b/rust/compiler.go
@@ -0,0 +1,259 @@
+// Copyright 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rust
+
+import (
+	"fmt"
+	"path/filepath"
+
+	"github.com/google/blueprint/proptools"
+
+	"android/soong/android"
+	"android/soong/rust/config"
+)
+
+func getEdition(compiler *baseCompiler) string {
+	return proptools.StringDefault(compiler.Properties.Edition, config.DefaultEdition)
+}
+
+func getDenyWarnings(compiler *baseCompiler) bool {
+	return BoolDefault(compiler.Properties.Deny_warnings, config.DefaultDenyWarnings)
+}
+
+func (compiler *baseCompiler) setNoStdlibs() {
+	compiler.Properties.No_stdlibs = proptools.BoolPtr(true)
+}
+
+func NewBaseCompiler(dir, dir64 string, location installLocation) *baseCompiler {
+	return &baseCompiler{
+		Properties: BaseCompilerProperties{},
+		dir:        dir,
+		dir64:      dir64,
+		location:   location,
+	}
+}
+
+type installLocation int
+
+const (
+	InstallInSystem installLocation = 0
+	InstallInData                   = iota
+)
+
+type BaseCompilerProperties struct {
+	// whether to pass "-D warnings" to rustc. Defaults to true.
+	Deny_warnings *bool
+
+	// flags to pass to rustc
+	Flags []string `android:"path,arch_variant"`
+
+	// flags to pass to the linker
+	Ld_flags []string `android:"path,arch_variant"`
+
+	// list of rust rlib crate dependencies
+	Rlibs []string `android:"arch_variant"`
+
+	// list of rust dylib crate dependencies
+	Dylibs []string `android:"arch_variant"`
+
+	// list of rust proc_macro crate dependencies
+	Proc_macros []string `android:"arch_variant"`
+
+	// list of C shared library dependencies
+	Shared_libs []string `android:"arch_variant"`
+
+	// list of C static library dependencies
+	Static_libs []string `android:"arch_variant"`
+
+	// crate name, required for libraries. This must be the expected extern crate name used in source
+	Crate_name string `android:"arch_variant"`
+
+	// list of features to enable for this crate
+	Features []string `android:"arch_variant"`
+
+	// specific rust edition that should be used if the default version is not desired
+	Edition *string `android:"arch_variant"`
+
+	// sets name of the output
+	Stem *string `android:"arch_variant"`
+
+	// append to name of output
+	Suffix *string `android:"arch_variant"`
+
+	// install to a subdirectory of the default install path for the module
+	Relative_install_path *string `android:"arch_variant"`
+
+	// whether to suppress inclusion of standard crates - defaults to false
+	No_stdlibs *bool
+}
+
+type baseCompiler struct {
+	Properties    BaseCompilerProperties
+	pathDeps      android.Paths
+	rustFlagsDeps android.Paths
+	linkFlagsDeps android.Paths
+	flags         string
+	linkFlags     string
+	depFlags      []string
+	linkDirs      []string
+	edition       string
+	src           android.Path //rustc takes a single src file
+
+	// Install related
+	dir      string
+	dir64    string
+	subDir   string
+	relative string
+	path     android.InstallPath
+	location installLocation
+}
+
+var _ compiler = (*baseCompiler)(nil)
+
+func (compiler *baseCompiler) inData() bool {
+	return compiler.location == InstallInData
+}
+
+func (compiler *baseCompiler) compilerProps() []interface{} {
+	return []interface{}{&compiler.Properties}
+}
+
+func (compiler *baseCompiler) featuresToFlags(features []string) []string {
+	flags := []string{}
+	for _, feature := range features {
+		flags = append(flags, "--cfg 'feature=\""+feature+"\"'")
+	}
+	return flags
+}
+
+func (compiler *baseCompiler) compilerFlags(ctx ModuleContext, flags Flags) Flags {
+
+	if getDenyWarnings(compiler) {
+		flags.RustFlags = append(flags.RustFlags, "-D warnings")
+	}
+	flags.RustFlags = append(flags.RustFlags, compiler.Properties.Flags...)
+	flags.RustFlags = append(flags.RustFlags, compiler.featuresToFlags(compiler.Properties.Features)...)
+	flags.RustFlags = append(flags.RustFlags, "--edition="+getEdition(compiler))
+	flags.LinkFlags = append(flags.LinkFlags, compiler.Properties.Ld_flags...)
+	flags.GlobalRustFlags = append(flags.GlobalRustFlags, config.GlobalRustFlags...)
+	flags.GlobalRustFlags = append(flags.GlobalRustFlags, ctx.toolchain().ToolchainRustFlags())
+	flags.GlobalLinkFlags = append(flags.GlobalLinkFlags, ctx.toolchain().ToolchainLinkFlags())
+
+	if ctx.Host() && !ctx.Windows() {
+		rpath_prefix := `\$$ORIGIN/`
+		if ctx.Darwin() {
+			rpath_prefix = "@loader_path/"
+		}
+
+		var rpath string
+		if ctx.toolchain().Is64Bit() {
+			rpath = "lib64"
+		} else {
+			rpath = "lib"
+		}
+		flags.LinkFlags = append(flags.LinkFlags, "-Wl,-rpath,"+rpath_prefix+rpath)
+		flags.LinkFlags = append(flags.LinkFlags, "-Wl,-rpath,"+rpath_prefix+"../"+rpath)
+	}
+
+	return flags
+}
+
+func (compiler *baseCompiler) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path {
+	panic(fmt.Errorf("baseCrater doesn't know how to crate things!"))
+}
+
+func (compiler *baseCompiler) compilerDeps(ctx DepsContext, deps Deps) Deps {
+	deps.Rlibs = append(deps.Rlibs, compiler.Properties.Rlibs...)
+	deps.Dylibs = append(deps.Dylibs, compiler.Properties.Dylibs...)
+	deps.ProcMacros = append(deps.ProcMacros, compiler.Properties.Proc_macros...)
+	deps.StaticLibs = append(deps.StaticLibs, compiler.Properties.Static_libs...)
+	deps.SharedLibs = append(deps.SharedLibs, compiler.Properties.Shared_libs...)
+
+	if !Bool(compiler.Properties.No_stdlibs) {
+		for _, stdlib := range config.Stdlibs {
+			// If we're building for host, use the compiler's stdlibs
+			if ctx.Host() {
+				stdlib = stdlib + "_" + ctx.toolchain().RustTriple()
+			}
+
+			// This check is technically insufficient - on the host, where
+			// static linking is the default, if one of our static
+			// dependencies uses a dynamic library, we need to dynamically
+			// link the stdlib as well.
+			if (len(deps.Dylibs) > 0) || (!ctx.Host()) {
+				// Dynamically linked stdlib
+				deps.Dylibs = append(deps.Dylibs, stdlib)
+			}
+		}
+	}
+	return deps
+}
+
+func (compiler *baseCompiler) bionicDeps(ctx DepsContext, deps Deps) Deps {
+	deps.SharedLibs = append(deps.SharedLibs, "liblog")
+	deps.SharedLibs = append(deps.SharedLibs, "libc")
+	deps.SharedLibs = append(deps.SharedLibs, "libm")
+	deps.SharedLibs = append(deps.SharedLibs, "libdl")
+
+	//TODO(b/141331117) libstd requires libgcc on Android
+	deps.StaticLibs = append(deps.StaticLibs, "libgcc")
+
+	return deps
+}
+
+func (compiler *baseCompiler) crateName() string {
+	return compiler.Properties.Crate_name
+}
+
+func (compiler *baseCompiler) installDir(ctx ModuleContext) android.InstallPath {
+	dir := compiler.dir
+	if ctx.toolchain().Is64Bit() && compiler.dir64 != "" {
+		dir = compiler.dir64
+	}
+	if !ctx.Host() || ctx.Target().NativeBridge == android.NativeBridgeEnabled {
+		dir = filepath.Join(dir, ctx.Arch().ArchType.String())
+	}
+	return android.PathForModuleInstall(ctx, dir, compiler.subDir,
+		compiler.relativeInstallPath(), compiler.relative)
+}
+
+func (compiler *baseCompiler) install(ctx ModuleContext, file android.Path) {
+	compiler.path = ctx.InstallFile(compiler.installDir(ctx), file.Base(), file)
+}
+
+func (compiler *baseCompiler) getStem(ctx ModuleContext) string {
+	return compiler.getStemWithoutSuffix(ctx) + String(compiler.Properties.Suffix)
+}
+
+func (compiler *baseCompiler) getStemWithoutSuffix(ctx BaseModuleContext) string {
+	stem := ctx.baseModuleName()
+	if String(compiler.Properties.Stem) != "" {
+		stem = String(compiler.Properties.Stem)
+	}
+
+	return stem
+}
+
+func (compiler *baseCompiler) relativeInstallPath() string {
+	return String(compiler.Properties.Relative_install_path)
+}
+
+func srcPathFromModuleSrcs(ctx ModuleContext, srcs []string) android.Path {
+	srcPaths := android.PathsForModuleSrc(ctx, srcs)
+	if len(srcPaths) != 1 {
+		ctx.PropertyErrorf("srcs", "srcs can only contain one path for rust modules")
+	}
+	return srcPaths[0]
+}
diff --git a/rust/compiler_test.go b/rust/compiler_test.go
new file mode 100644
index 0000000..bbf9f8d
--- /dev/null
+++ b/rust/compiler_test.go
@@ -0,0 +1,76 @@
+// Copyright 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rust
+
+import (
+	"strings"
+	"testing"
+)
+
+// Test that feature flags are being correctly generated.
+func TestFeaturesToFlags(t *testing.T) {
+	ctx := testRust(t, `
+		rust_library_host_dylib {
+			name: "libfoo",
+			srcs: ["foo.rs"],
+			crate_name: "foo",
+			features: [
+				"fizz",
+				"buzz"
+			],
+		}`)
+
+	libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Rule("rustc")
+
+	if !strings.Contains(libfooDylib.Args["rustcFlags"], "cfg 'feature=\"fizz\"'") ||
+		!strings.Contains(libfooDylib.Args["rustcFlags"], "cfg 'feature=\"buzz\"'") {
+		t.Fatalf("missing fizz and buzz feature flags for libfoo dylib, rustcFlags: %#v", libfooDylib.Args["rustcFlags"])
+	}
+}
+
+// Test that we reject multiple source files.
+func TestEnforceSingleSourceFile(t *testing.T) {
+
+	singleSrcError := "srcs can only contain one path for rust modules"
+
+	// Test libraries
+	testRustError(t, singleSrcError, `
+		rust_library_host {
+			name: "foo-bar-library",
+			srcs: ["foo.rs", "src/bar.rs"],
+		}`)
+
+	// Test binaries
+	testRustError(t, singleSrcError, `
+			rust_binary_host {
+				name: "foo-bar-binary",
+				srcs: ["foo.rs", "src/bar.rs"],
+			}`)
+
+	// Test proc_macros
+	testRustError(t, singleSrcError, `
+		rust_proc_macro {
+			name: "foo-bar-proc-macro",
+			srcs: ["foo.rs", "src/bar.rs"],
+		}`)
+
+	// Test prebuilts
+	testRustError(t, singleSrcError, `
+		rust_prebuilt_dylib {
+			name: "foo-bar-prebuilt",
+			srcs: ["liby.so", "libz.so"],
+		  host_supported: true,
+		}`)
+}
diff --git a/rust/config/arm64_device.go b/rust/config/arm64_device.go
new file mode 100644
index 0000000..60796d8
--- /dev/null
+++ b/rust/config/arm64_device.go
@@ -0,0 +1,93 @@
+// Copyright 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+import (
+	"strings"
+
+	"android/soong/android"
+)
+
+var (
+	Arm64RustFlags            = []string{}
+	Arm64ArchFeatureRustFlags = map[string][]string{}
+	Arm64LinkFlags            = []string{
+		"-Wl,--icf=safe",
+		"-Wl,-z,max-page-size=4096",
+
+		"-Wl,--execute-only",
+		"-Wl,-z,separate-code",
+	}
+
+	Arm64ArchVariantRustFlags = map[string][]string{
+		"armv8-a":  []string{},
+		"armv8-2a": []string{},
+	}
+)
+
+func init() {
+	registerToolchainFactory(android.Android, android.Arm64, Arm64ToolchainFactory)
+
+	pctx.StaticVariable("Arm64ToolchainRustFlags", strings.Join(Arm64RustFlags, " "))
+	pctx.StaticVariable("Arm64ToolchainLinkFlags", strings.Join(Arm64LinkFlags, " "))
+
+	for variant, rustFlags := range Arm64ArchVariantRustFlags {
+		pctx.StaticVariable("Arm64"+variant+"VariantRustFlags",
+			strings.Join(rustFlags, " "))
+	}
+
+}
+
+type toolchainArm64 struct {
+	toolchain64Bit
+	toolchainRustFlags string
+}
+
+func (t *toolchainArm64) RustTriple() string {
+	return "aarch64-linux-android"
+}
+
+func (t *toolchainArm64) ToolchainLinkFlags() string {
+	return "${config.DeviceGlobalLinkFlags} ${config.Arm64ToolchainLinkFlags}"
+}
+
+func (t *toolchainArm64) ToolchainRustFlags() string {
+	return t.toolchainRustFlags
+}
+
+func (t *toolchainArm64) RustFlags() string {
+	return "${config.Arm64ToolchainRustFlags}"
+}
+
+func (t *toolchainArm64) Supported() bool {
+	return true
+}
+
+func Arm64ToolchainFactory(arch android.Arch) Toolchain {
+	toolchainRustFlags := []string{
+		"${config.Arm64ToolchainRustFlags}",
+		"${config.Arm64" + arch.ArchVariant + "VariantRustFlags}",
+	}
+
+	toolchainRustFlags = append(toolchainRustFlags, deviceGlobalRustFlags...)
+
+	for _, feature := range arch.ArchFeatures {
+		toolchainRustFlags = append(toolchainRustFlags, Arm64ArchFeatureRustFlags[feature]...)
+	}
+
+	return &toolchainArm64{
+		toolchainRustFlags: strings.Join(toolchainRustFlags, " "),
+	}
+}
diff --git a/rust/config/arm_device.go b/rust/config/arm_device.go
new file mode 100644
index 0000000..aedb42b
--- /dev/null
+++ b/rust/config/arm_device.go
@@ -0,0 +1,92 @@
+// Copyright 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+import (
+	"strings"
+
+	"android/soong/android"
+)
+
+var (
+	ArmRustFlags            = []string{}
+	ArmArchFeatureRustFlags = map[string][]string{}
+	ArmLinkFlags            = []string{
+		"-Wl,--icf=safe",
+		"-Wl,-m,armelf",
+	}
+
+	ArmArchVariantRustFlags = map[string][]string{
+		"armv7-a":      []string{},
+		"armv7-a-neon": []string{},
+		"armv8-a":      []string{},
+		"armv8-2a":     []string{},
+	}
+)
+
+func init() {
+	registerToolchainFactory(android.Android, android.Arm, ArmToolchainFactory)
+
+	pctx.StaticVariable("ArmToolchainRustFlags", strings.Join(ArmRustFlags, " "))
+	pctx.StaticVariable("ArmToolchainLinkFlags", strings.Join(ArmLinkFlags, " "))
+
+	for variant, rustFlags := range ArmArchVariantRustFlags {
+		pctx.StaticVariable("Arm"+variant+"VariantRustFlags",
+			strings.Join(rustFlags, " "))
+	}
+
+}
+
+type toolchainArm struct {
+	toolchain64Bit
+	toolchainRustFlags string
+}
+
+func (t *toolchainArm) RustTriple() string {
+	return "arm-linux-androideabi"
+}
+
+func (t *toolchainArm) ToolchainLinkFlags() string {
+	return "${config.DeviceGlobalLinkFlags} ${config.ArmToolchainLinkFlags}"
+}
+
+func (t *toolchainArm) ToolchainRustFlags() string {
+	return t.toolchainRustFlags
+}
+
+func (t *toolchainArm) RustFlags() string {
+	return "${config.ArmToolchainRustFlags}"
+}
+
+func (t *toolchainArm) Supported() bool {
+	return true
+}
+
+func ArmToolchainFactory(arch android.Arch) Toolchain {
+	toolchainRustFlags := []string{
+		"${config.ArmToolchainRustFlags}",
+		"${config.Arm" + arch.ArchVariant + "VariantRustFlags}",
+	}
+
+	toolchainRustFlags = append(toolchainRustFlags, deviceGlobalRustFlags...)
+
+	for _, feature := range arch.ArchFeatures {
+		toolchainRustFlags = append(toolchainRustFlags, ArmArchFeatureRustFlags[feature]...)
+	}
+
+	return &toolchainArm{
+		toolchainRustFlags: strings.Join(toolchainRustFlags, " "),
+	}
+}
diff --git a/rust/config/global.go b/rust/config/global.go
new file mode 100644
index 0000000..fb9b14b
--- /dev/null
+++ b/rust/config/global.go
@@ -0,0 +1,90 @@
+// Copyright 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+import (
+	"strings"
+
+	"android/soong/android"
+	_ "android/soong/cc/config"
+)
+
+var pctx = android.NewPackageContext("android/soong/rust/config")
+
+var (
+	RustDefaultVersion = "1.40.0"
+	RustDefaultBase    = "prebuilts/rust/"
+	DefaultEdition     = "2018"
+	Stdlibs            = []string{
+		"libstd",
+		"libtest",
+	}
+
+	DefaultDenyWarnings = true
+
+	GlobalRustFlags = []string{
+		"--remap-path-prefix $$(pwd)=",
+		"-C codegen-units=1",
+		"-C opt-level=3",
+		"-C relocation-model=pic",
+	}
+
+	deviceGlobalRustFlags = []string{}
+
+	deviceGlobalLinkFlags = []string{
+		"-Bdynamic",
+		"-nostdlib",
+		"-Wl,-z,noexecstack",
+		"-Wl,-z,relro",
+		"-Wl,-z,now",
+		"-Wl,--build-id=md5",
+		"-Wl,--warn-shared-textrel",
+		"-Wl,--fatal-warnings",
+
+		"-Wl,--pack-dyn-relocs=android+relr",
+		"-Wl,--use-android-relr-tags",
+		"-Wl,--no-undefined",
+		"-Wl,--hash-style=gnu",
+	}
+)
+
+func init() {
+	pctx.SourcePathVariable("RustDefaultBase", RustDefaultBase)
+	pctx.VariableConfigMethod("HostPrebuiltTag", android.Config.PrebuiltOS)
+
+	pctx.VariableFunc("RustBase", func(ctx android.PackageVarContext) string {
+		if override := ctx.Config().Getenv("RUST_PREBUILTS_BASE"); override != "" {
+			return override
+		}
+		return "${RustDefaultBase}"
+	})
+
+	pctx.VariableFunc("RustVersion", func(ctx android.PackageVarContext) string {
+		if override := ctx.Config().Getenv("RUST_PREBUILTS_VERSION"); override != "" {
+			return override
+		}
+		return RustDefaultVersion
+	})
+
+	pctx.StaticVariable("RustPath", "${RustBase}/${HostPrebuiltTag}/${RustVersion}")
+	pctx.StaticVariable("RustBin", "${RustPath}/bin")
+
+	pctx.ImportAs("ccConfig", "android/soong/cc/config")
+	pctx.StaticVariable("RustLinker", "${ccConfig.ClangBin}/clang++")
+	pctx.StaticVariable("RustLinkerArgs", "-B ${ccConfig.ClangBin} -fuse-ld=lld")
+
+	pctx.StaticVariable("DeviceGlobalLinkFlags", strings.Join(deviceGlobalLinkFlags, " "))
+
+}
diff --git a/rust/config/toolchain.go b/rust/config/toolchain.go
new file mode 100644
index 0000000..616d88b
--- /dev/null
+++ b/rust/config/toolchain.go
@@ -0,0 +1,130 @@
+// Copyright 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+import (
+	"android/soong/android"
+)
+
+type Toolchain interface {
+	RustTriple() string
+	ToolchainRustFlags() string
+	ToolchainLinkFlags() string
+
+	SharedLibSuffix() string
+	StaticLibSuffix() string
+	RlibSuffix() string
+	DylibSuffix() string
+	ProcMacroSuffix() string
+	ExecutableSuffix() string
+
+	Is64Bit() bool
+	Supported() bool
+
+	Bionic() bool
+}
+
+type toolchainBase struct {
+}
+
+func (toolchainBase) RustTriple() string {
+	panic("toolchainBase does not define a triple.")
+}
+
+func (toolchainBase) ToolchainRustFlags() string {
+	panic("toolchainBase does not provide rust flags.")
+}
+
+func (toolchainBase) ToolchainLinkFlags() string {
+	panic("toolchainBase does not provide link flags.")
+}
+
+func (toolchainBase) Is64Bit() bool {
+	panic("toolchainBase cannot determine datapath width.")
+}
+
+func (toolchainBase) Bionic() bool {
+	return true
+}
+
+type toolchain64Bit struct {
+	toolchainBase
+}
+
+func (toolchain64Bit) Is64Bit() bool {
+	return true
+}
+
+type toolchain32Bit struct {
+	toolchainBase
+}
+
+func (toolchain32Bit) Is64Bit() bool {
+	return false
+}
+
+func (toolchain32Bit) Bionic() bool {
+	return true
+}
+
+func (toolchainBase) ExecutableSuffix() string {
+	return ""
+}
+
+func (toolchainBase) SharedLibSuffix() string {
+	return ".so"
+}
+
+func (toolchainBase) StaticLibSuffix() string {
+	return ".a"
+}
+
+func (toolchainBase) RlibSuffix() string {
+	return ".rlib"
+}
+func (toolchainBase) DylibSuffix() string {
+	return ".dylib.so"
+}
+
+func (toolchainBase) ProcMacroSuffix() string {
+	return ".so"
+}
+
+func (toolchainBase) Supported() bool {
+	return false
+}
+
+func toolchainBaseFactory() Toolchain {
+	return &toolchainBase{}
+}
+
+type toolchainFactory func(arch android.Arch) Toolchain
+
+var toolchainFactories = make(map[android.OsType]map[android.ArchType]toolchainFactory)
+
+func registerToolchainFactory(os android.OsType, arch android.ArchType, factory toolchainFactory) {
+	if toolchainFactories[os] == nil {
+		toolchainFactories[os] = make(map[android.ArchType]toolchainFactory)
+	}
+	toolchainFactories[os][arch] = factory
+}
+
+func FindToolchain(os android.OsType, arch android.Arch) Toolchain {
+	factory := toolchainFactories[os][arch.ArchType]
+	if factory == nil {
+		return toolchainBaseFactory()
+	}
+	return factory(arch)
+}
diff --git a/rust/config/whitelist.go b/rust/config/whitelist.go
new file mode 100644
index 0000000..7dfb002
--- /dev/null
+++ b/rust/config/whitelist.go
@@ -0,0 +1,28 @@
+package config
+
+var (
+	RustAllowedPaths = []string{
+		"external/rust",
+		"external/crosvm",
+		"external/adhd",
+		"prebuilts/rust",
+	}
+
+	RustModuleTypes = []string{
+		"rust_binary",
+		"rust_binary_host",
+		"rust_library",
+		"rust_library_dylib",
+		"rust_library_rlib",
+		"rust_library_shared",
+		"rust_library_static",
+		"rust_library_host",
+		"rust_library_host_dylib",
+		"rust_library_host_rlib",
+		"rust_library_host_shared",
+		"rust_library_host_static",
+		"rust_proc_macro",
+		"rust_test",
+		"rust_test_host",
+	}
+)
diff --git a/rust/config/x86_64_device.go b/rust/config/x86_64_device.go
new file mode 100644
index 0000000..9a6c00b
--- /dev/null
+++ b/rust/config/x86_64_device.go
@@ -0,0 +1,94 @@
+// Copyright 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+import (
+	"strings"
+
+	"android/soong/android"
+)
+
+var (
+	x86_64RustFlags            = []string{}
+	x86_64ArchFeatureRustFlags = map[string][]string{}
+	x86_64LinkFlags            = []string{}
+
+	x86_64ArchVariantRustFlags = map[string][]string{
+		"":            []string{},
+		"broadwell":   []string{"-C target-cpu=broadwell"},
+		"haswell":     []string{"-C target-cpu=haswell"},
+		"ivybridge":   []string{"-C target-cpu=ivybridge"},
+		"sandybridge": []string{"-C target-cpu=sandybridge"},
+		"silvermont":  []string{"-C target-cpu=silvermont"},
+		"skylake":     []string{"-C target-cpu=skylake"},
+		//TODO: Add target-cpu=stoneyridge when rustc supports it.
+		"stoneyridge": []string{""},
+	}
+)
+
+func init() {
+	registerToolchainFactory(android.Android, android.X86_64, x86_64ToolchainFactory)
+
+	pctx.StaticVariable("X86_64ToolchainRustFlags", strings.Join(x86_64RustFlags, " "))
+	pctx.StaticVariable("X86_64ToolchainLinkFlags", strings.Join(x86_64LinkFlags, " "))
+
+	for variant, rustFlags := range x86_64ArchVariantRustFlags {
+		pctx.StaticVariable("X86_64"+variant+"VariantRustFlags",
+			strings.Join(rustFlags, " "))
+	}
+
+}
+
+type toolchainX86_64 struct {
+	toolchain64Bit
+	toolchainRustFlags string
+}
+
+func (t *toolchainX86_64) RustTriple() string {
+	return "x86_64-linux-android"
+}
+
+func (t *toolchainX86_64) ToolchainLinkFlags() string {
+	return "${config.DeviceGlobalLinkFlags} ${config.X86_64ToolchainLinkFlags}"
+}
+
+func (t *toolchainX86_64) ToolchainRustFlags() string {
+	return t.toolchainRustFlags
+}
+
+func (t *toolchainX86_64) RustFlags() string {
+	return "${config.X86_64ToolchainRustFlags}"
+}
+
+func (t *toolchainX86_64) Supported() bool {
+	return true
+}
+
+func x86_64ToolchainFactory(arch android.Arch) Toolchain {
+	toolchainRustFlags := []string{
+		"${config.X86_64ToolchainRustFlags}",
+		"${config.X86_64" + arch.ArchVariant + "VariantRustFlags}",
+	}
+
+	toolchainRustFlags = append(toolchainRustFlags, deviceGlobalRustFlags...)
+
+	for _, feature := range arch.ArchFeatures {
+		toolchainRustFlags = append(toolchainRustFlags, x86_64ArchFeatureRustFlags[feature]...)
+	}
+
+	return &toolchainX86_64{
+		toolchainRustFlags: strings.Join(toolchainRustFlags, " "),
+	}
+}
diff --git a/rust/config/x86_darwin_host.go b/rust/config/x86_darwin_host.go
new file mode 100644
index 0000000..7cfc59c
--- /dev/null
+++ b/rust/config/x86_darwin_host.go
@@ -0,0 +1,81 @@
+// Copyright 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+import (
+	"strings"
+
+	"android/soong/android"
+)
+
+var (
+	DarwinRustFlags      = []string{}
+	DarwinRustLinkFlags  = []string{}
+	darwinX8664Rustflags = []string{}
+	darwinX8664Linkflags = []string{}
+)
+
+func init() {
+	registerToolchainFactory(android.Darwin, android.X86_64, darwinX8664ToolchainFactory)
+	pctx.StaticVariable("DarwinToolchainRustFlags", strings.Join(DarwinRustFlags, " "))
+	pctx.StaticVariable("DarwinToolchainLinkFlags", strings.Join(DarwinRustLinkFlags, " "))
+	pctx.StaticVariable("DarwinToolchainX8664RustFlags", strings.Join(darwinX8664Rustflags, " "))
+	pctx.StaticVariable("DarwinToolchainX8664LinkFlags", strings.Join(darwinX8664Linkflags, " "))
+
+}
+
+type toolchainDarwin struct {
+	toolchainRustFlags string
+	toolchainLinkFlags string
+}
+
+type toolchainDarwinX8664 struct {
+	toolchain64Bit
+	toolchainDarwin
+}
+
+func (toolchainDarwinX8664) Supported() bool {
+	return true
+}
+
+func (toolchainDarwinX8664) Bionic() bool {
+	return false
+}
+
+func (t *toolchainDarwinX8664) Name() string {
+	return "x86_64"
+}
+
+func (t *toolchainDarwinX8664) RustTriple() string {
+	return "x86_64-apple-darwin"
+}
+
+func (t *toolchainDarwin) ShlibSuffix() string {
+	return ".dylib"
+}
+
+func (t *toolchainDarwinX8664) ToolchainLinkFlags() string {
+	return "${config.DarwinToolchainLinkFlags} ${config.DarwinToolchainX8664LinkFlags}"
+}
+
+func (t *toolchainDarwinX8664) ToolchainRustFlags() string {
+	return "${config.DarwinToolchainRustFlags} ${config.DarwinToolchainX8664RustFlags}"
+}
+
+func darwinX8664ToolchainFactory(arch android.Arch) Toolchain {
+	return toolchainDarwinX8664Singleton
+}
+
+var toolchainDarwinX8664Singleton Toolchain = &toolchainDarwinX8664{}
diff --git a/rust/config/x86_device.go b/rust/config/x86_device.go
new file mode 100644
index 0000000..ec19b3c
--- /dev/null
+++ b/rust/config/x86_device.go
@@ -0,0 +1,97 @@
+// Copyright 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+import (
+	"strings"
+
+	"android/soong/android"
+)
+
+var (
+	x86RustFlags            = []string{}
+	x86ArchFeatureRustFlags = map[string][]string{}
+	x86LinkFlags            = []string{}
+
+	x86ArchVariantRustFlags = map[string][]string{
+		"":            []string{},
+		"atom":        []string{"-C target-cpu=atom"},
+		"broadwell":   []string{"-C target-cpu=broadwell"},
+		"haswell":     []string{"-C target-cpu=haswell"},
+		"ivybridge":   []string{"-C target-cpu=ivybridge"},
+		"sandybridge": []string{"-C target-cpu=sandybridge"},
+		"silvermont":  []string{"-C target-cpu=silvermont"},
+		"skylake":     []string{"-C target-cpu=skylake"},
+		//TODO: Add target-cpu=stoneyridge when rustc supports it.
+		"stoneyridge": []string{""},
+		// use prescott for x86_64, like cc/config/x86_device.go
+		"x86_64": []string{"-C target-cpu=prescott"},
+	}
+)
+
+func init() {
+	registerToolchainFactory(android.Android, android.X86, x86ToolchainFactory)
+
+	pctx.StaticVariable("X86ToolchainRustFlags", strings.Join(x86RustFlags, " "))
+	pctx.StaticVariable("X86ToolchainLinkFlags", strings.Join(x86LinkFlags, " "))
+
+	for variant, rustFlags := range x86ArchVariantRustFlags {
+		pctx.StaticVariable("X86"+variant+"VariantRustFlags",
+			strings.Join(rustFlags, " "))
+	}
+
+}
+
+type toolchainX86 struct {
+	toolchain32Bit
+	toolchainRustFlags string
+}
+
+func (t *toolchainX86) RustTriple() string {
+	return "i686-linux-android"
+}
+
+func (t *toolchainX86) ToolchainLinkFlags() string {
+	return "${config.DeviceGlobalLinkFlags} ${config.X86ToolchainLinkFlags}"
+}
+
+func (t *toolchainX86) ToolchainRustFlags() string {
+	return t.toolchainRustFlags
+}
+
+func (t *toolchainX86) RustFlags() string {
+	return "${config.X86ToolchainRustFlags}"
+}
+
+func (t *toolchainX86) Supported() bool {
+	return true
+}
+
+func x86ToolchainFactory(arch android.Arch) Toolchain {
+	toolchainRustFlags := []string{
+		"${config.X86ToolchainRustFlags}",
+		"${config.X86" + arch.ArchVariant + "VariantRustFlags}",
+	}
+
+	toolchainRustFlags = append(toolchainRustFlags, deviceGlobalRustFlags...)
+
+	for _, feature := range arch.ArchFeatures {
+		toolchainRustFlags = append(toolchainRustFlags, x86ArchFeatureRustFlags[feature]...)
+	}
+
+	return &toolchainX86{
+		toolchainRustFlags: strings.Join(toolchainRustFlags, " "),
+	}
+}
diff --git a/rust/config/x86_linux_host.go b/rust/config/x86_linux_host.go
new file mode 100644
index 0000000..5376e5b
--- /dev/null
+++ b/rust/config/x86_linux_host.go
@@ -0,0 +1,117 @@
+// Copyright 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+import (
+	"strings"
+
+	"android/soong/android"
+)
+
+var (
+	LinuxRustFlags      = []string{}
+	LinuxRustLinkFlags  = []string{}
+	linuxX86Rustflags   = []string{}
+	linuxX86Linkflags   = []string{}
+	linuxX8664Rustflags = []string{}
+	linuxX8664Linkflags = []string{}
+)
+
+func init() {
+	registerToolchainFactory(android.Linux, android.X86_64, linuxX8664ToolchainFactory)
+	registerToolchainFactory(android.Linux, android.X86, linuxX86ToolchainFactory)
+
+	pctx.StaticVariable("LinuxToolchainRustFlags", strings.Join(LinuxRustFlags, " "))
+	pctx.StaticVariable("LinuxToolchainLinkFlags", strings.Join(LinuxRustLinkFlags, " "))
+	pctx.StaticVariable("LinuxToolchainX86RustFlags", strings.Join(linuxX86Rustflags, " "))
+	pctx.StaticVariable("LinuxToolchainX86LinkFlags", strings.Join(linuxX86Linkflags, " "))
+	pctx.StaticVariable("LinuxToolchainX8664RustFlags", strings.Join(linuxX8664Rustflags, " "))
+	pctx.StaticVariable("LinuxToolchainX8664LinkFlags", strings.Join(linuxX8664Linkflags, " "))
+
+}
+
+type toolchainLinux struct {
+	toolchainRustFlags string
+	toolchainLinkFlags string
+}
+
+type toolchainLinuxX86 struct {
+	toolchain32Bit
+	toolchainLinux
+}
+
+type toolchainLinuxX8664 struct {
+	toolchain64Bit
+	toolchainLinux
+}
+
+func (toolchainLinuxX8664) Supported() bool {
+	return true
+}
+
+func (toolchainLinuxX8664) Bionic() bool {
+	return false
+}
+
+func (t *toolchainLinuxX8664) Name() string {
+	return "x86_64"
+}
+
+func (t *toolchainLinuxX8664) RustTriple() string {
+	return "x86_64-unknown-linux-gnu"
+}
+
+func (t *toolchainLinuxX8664) ToolchainLinkFlags() string {
+	return "${config.LinuxToolchainLinkFlags} ${config.LinuxToolchainX8664LinkFlags}"
+}
+
+func (t *toolchainLinuxX8664) ToolchainRustFlags() string {
+	return "${config.LinuxToolchainRustFlags} ${config.LinuxToolchainX8664RustFlags}"
+}
+
+func linuxX8664ToolchainFactory(arch android.Arch) Toolchain {
+	return toolchainLinuxX8664Singleton
+}
+
+func (toolchainLinuxX86) Supported() bool {
+	return true
+}
+
+func (toolchainLinuxX86) Bionic() bool {
+	return false
+}
+
+func (t *toolchainLinuxX86) Name() string {
+	return "x86"
+}
+
+func (t *toolchainLinuxX86) RustTriple() string {
+	return "i686-unknown-linux-gnu"
+}
+
+func (t *toolchainLinuxX86) ToolchainLinkFlags() string {
+	return "${config.LinuxToolchainLinkFlags} ${config.LinuxToolchainX86LinkFlags}"
+}
+
+func (t *toolchainLinuxX86) ToolchainRustFlags() string {
+	return "${config.LinuxToolchainRustFlags} ${config.LinuxToolchainX86RustFlags}"
+}
+
+func linuxX86ToolchainFactory(arch android.Arch) Toolchain {
+	return toolchainLinuxX86Singleton
+}
+
+var toolchainLinuxX8664Singleton Toolchain = &toolchainLinuxX8664{}
+var toolchainLinuxX86Singleton Toolchain = &toolchainLinuxX86{}
diff --git a/rust/library.go b/rust/library.go
new file mode 100644
index 0000000..43819ce
--- /dev/null
+++ b/rust/library.go
@@ -0,0 +1,431 @@
+// Copyright 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rust
+
+import (
+	"regexp"
+	"strings"
+
+	"android/soong/android"
+	"android/soong/rust/config"
+)
+
+func init() {
+	android.RegisterModuleType("rust_library", RustLibraryFactory)
+	android.RegisterModuleType("rust_library_dylib", RustLibraryDylibFactory)
+	android.RegisterModuleType("rust_library_rlib", RustLibraryRlibFactory)
+	android.RegisterModuleType("rust_library_host", RustLibraryHostFactory)
+	android.RegisterModuleType("rust_library_host_dylib", RustLibraryDylibHostFactory)
+	android.RegisterModuleType("rust_library_host_rlib", RustLibraryRlibHostFactory)
+	android.RegisterModuleType("rust_library_shared", RustLibrarySharedFactory)
+	android.RegisterModuleType("rust_library_static", RustLibraryStaticFactory)
+	android.RegisterModuleType("rust_library_host_shared", RustLibrarySharedHostFactory)
+	android.RegisterModuleType("rust_library_host_static", RustLibraryStaticHostFactory)
+}
+
+type VariantLibraryProperties struct {
+	Enabled *bool `android:"arch_variant"`
+}
+
+type LibraryCompilerProperties struct {
+	Rlib   VariantLibraryProperties `android:"arch_variant"`
+	Dylib  VariantLibraryProperties `android:"arch_variant"`
+	Shared VariantLibraryProperties `android:"arch_variant"`
+	Static VariantLibraryProperties `android:"arch_variant"`
+
+	// path to the source file that is the main entry point of the program (e.g. src/lib.rs)
+	Srcs []string `android:"path,arch_variant"`
+
+	// path to include directories to pass to cc_* modules, only relevant for static/shared variants.
+	Include_dirs []string `android:"path,arch_variant"`
+}
+
+type LibraryMutatedProperties struct {
+	// Build a dylib variant
+	BuildDylib bool `blueprint:"mutated"`
+	// Build an rlib variant
+	BuildRlib bool `blueprint:"mutated"`
+	// Build a shared library variant
+	BuildShared bool `blueprint:"mutated"`
+	// Build a static library variant
+	BuildStatic bool `blueprint:"mutated"`
+
+	// This variant is a dylib
+	VariantIsDylib bool `blueprint:"mutated"`
+	// This variant is an rlib
+	VariantIsRlib bool `blueprint:"mutated"`
+	// This variant is a shared library
+	VariantIsShared bool `blueprint:"mutated"`
+	// This variant is a static library
+	VariantIsStatic bool `blueprint:"mutated"`
+}
+
+type libraryDecorator struct {
+	*baseCompiler
+
+	Properties           LibraryCompilerProperties
+	MutatedProperties    LibraryMutatedProperties
+	distFile             android.OptionalPath
+	unstrippedOutputFile android.Path
+	includeDirs          android.Paths
+}
+
+type libraryInterface interface {
+	rlib() bool
+	dylib() bool
+	static() bool
+	shared() bool
+
+	// Returns true if the build options for the module have selected a particular build type
+	buildRlib() bool
+	buildDylib() bool
+	buildShared() bool
+	buildStatic() bool
+
+	// Sets a particular variant type
+	setRlib()
+	setDylib()
+	setShared()
+	setStatic()
+
+	// Build a specific library variant
+	BuildOnlyRlib()
+	BuildOnlyDylib()
+	BuildOnlyStatic()
+	BuildOnlyShared()
+}
+
+func (library *libraryDecorator) exportedDirs() []string {
+	return library.linkDirs
+}
+
+func (library *libraryDecorator) exportedDepFlags() []string {
+	return library.depFlags
+}
+
+func (library *libraryDecorator) reexportDirs(dirs ...string) {
+	library.linkDirs = android.FirstUniqueStrings(append(library.linkDirs, dirs...))
+}
+
+func (library *libraryDecorator) reexportDepFlags(flags ...string) {
+	library.depFlags = android.FirstUniqueStrings(append(library.depFlags, flags...))
+}
+
+func (library *libraryDecorator) rlib() bool {
+	return library.MutatedProperties.VariantIsRlib
+}
+
+func (library *libraryDecorator) dylib() bool {
+	return library.MutatedProperties.VariantIsDylib
+}
+
+func (library *libraryDecorator) shared() bool {
+	return library.MutatedProperties.VariantIsShared
+}
+
+func (library *libraryDecorator) static() bool {
+	return library.MutatedProperties.VariantIsStatic
+}
+
+func (library *libraryDecorator) buildRlib() bool {
+	return library.MutatedProperties.BuildRlib && BoolDefault(library.Properties.Rlib.Enabled, true)
+}
+
+func (library *libraryDecorator) buildDylib() bool {
+	return library.MutatedProperties.BuildDylib && BoolDefault(library.Properties.Dylib.Enabled, true)
+}
+
+func (library *libraryDecorator) buildShared() bool {
+	return library.MutatedProperties.BuildShared && BoolDefault(library.Properties.Shared.Enabled, true)
+}
+
+func (library *libraryDecorator) buildStatic() bool {
+	return library.MutatedProperties.BuildStatic && BoolDefault(library.Properties.Static.Enabled, true)
+}
+
+func (library *libraryDecorator) setRlib() {
+	library.MutatedProperties.VariantIsRlib = true
+	library.MutatedProperties.VariantIsDylib = false
+	library.MutatedProperties.VariantIsStatic = false
+	library.MutatedProperties.VariantIsShared = false
+}
+
+func (library *libraryDecorator) setDylib() {
+	library.MutatedProperties.VariantIsRlib = false
+	library.MutatedProperties.VariantIsDylib = true
+	library.MutatedProperties.VariantIsStatic = false
+	library.MutatedProperties.VariantIsShared = false
+}
+
+func (library *libraryDecorator) setShared() {
+	library.MutatedProperties.VariantIsStatic = false
+	library.MutatedProperties.VariantIsShared = true
+	library.MutatedProperties.VariantIsRlib = false
+	library.MutatedProperties.VariantIsDylib = false
+}
+
+func (library *libraryDecorator) setStatic() {
+	library.MutatedProperties.VariantIsStatic = true
+	library.MutatedProperties.VariantIsShared = false
+	library.MutatedProperties.VariantIsRlib = false
+	library.MutatedProperties.VariantIsDylib = false
+}
+
+var _ compiler = (*libraryDecorator)(nil)
+var _ libraryInterface = (*libraryDecorator)(nil)
+
+// rust_library produces all variants.
+func RustLibraryFactory() android.Module {
+	module, _ := NewRustLibrary(android.HostAndDeviceSupported)
+	return module.Init()
+}
+
+// rust_library_dylib produces a dylib.
+func RustLibraryDylibFactory() android.Module {
+	module, library := NewRustLibrary(android.HostAndDeviceSupported)
+	library.BuildOnlyDylib()
+	return module.Init()
+}
+
+// rust_library_rlib produces an rlib.
+func RustLibraryRlibFactory() android.Module {
+	module, library := NewRustLibrary(android.HostAndDeviceSupported)
+	library.BuildOnlyRlib()
+	return module.Init()
+}
+
+// rust_library_shared produces a shared library.
+func RustLibrarySharedFactory() android.Module {
+	module, library := NewRustLibrary(android.HostAndDeviceSupported)
+	library.BuildOnlyShared()
+	return module.Init()
+}
+
+// rust_library_static produces a static library.
+func RustLibraryStaticFactory() android.Module {
+	module, library := NewRustLibrary(android.HostAndDeviceSupported)
+	library.BuildOnlyStatic()
+	return module.Init()
+}
+
+// rust_library_host produces all variants.
+func RustLibraryHostFactory() android.Module {
+	module, _ := NewRustLibrary(android.HostSupported)
+	return module.Init()
+}
+
+// rust_library_dylib_host produces a dylib.
+func RustLibraryDylibHostFactory() android.Module {
+	module, library := NewRustLibrary(android.HostSupported)
+	library.BuildOnlyDylib()
+	return module.Init()
+}
+
+// rust_library_rlib_host produces an rlib.
+func RustLibraryRlibHostFactory() android.Module {
+	module, library := NewRustLibrary(android.HostSupported)
+	library.BuildOnlyRlib()
+	return module.Init()
+}
+
+// rust_library_static_host produces a static library.
+func RustLibraryStaticHostFactory() android.Module {
+	module, library := NewRustLibrary(android.HostSupported)
+	library.BuildOnlyStatic()
+	return module.Init()
+}
+
+// rust_library_shared_host produces an shared library.
+func RustLibrarySharedHostFactory() android.Module {
+	module, library := NewRustLibrary(android.HostSupported)
+	library.BuildOnlyShared()
+	return module.Init()
+}
+
+func (library *libraryDecorator) BuildOnlyDylib() {
+	library.MutatedProperties.BuildRlib = false
+	library.MutatedProperties.BuildShared = false
+	library.MutatedProperties.BuildStatic = false
+
+}
+
+func (library *libraryDecorator) BuildOnlyRlib() {
+	library.MutatedProperties.BuildDylib = false
+	library.MutatedProperties.BuildShared = false
+	library.MutatedProperties.BuildStatic = false
+}
+
+func (library *libraryDecorator) BuildOnlyStatic() {
+	library.MutatedProperties.BuildShared = false
+	library.MutatedProperties.BuildRlib = false
+	library.MutatedProperties.BuildDylib = false
+
+}
+
+func (library *libraryDecorator) BuildOnlyShared() {
+	library.MutatedProperties.BuildStatic = false
+	library.MutatedProperties.BuildRlib = false
+	library.MutatedProperties.BuildDylib = false
+}
+
+func NewRustLibrary(hod android.HostOrDeviceSupported) (*Module, *libraryDecorator) {
+	module := newModule(hod, android.MultilibFirst)
+
+	library := &libraryDecorator{
+		MutatedProperties: LibraryMutatedProperties{
+			BuildDylib:  true,
+			BuildRlib:   true,
+			BuildShared: true,
+			BuildStatic: true,
+		},
+		baseCompiler: NewBaseCompiler("lib", "lib64", InstallInSystem),
+	}
+
+	module.compiler = library
+
+	return module, library
+}
+
+func (library *libraryDecorator) compilerProps() []interface{} {
+	return append(library.baseCompiler.compilerProps(),
+		&library.Properties,
+		&library.MutatedProperties)
+}
+
+func (library *libraryDecorator) compilerDeps(ctx DepsContext, deps Deps) Deps {
+
+	// TODO(b/144861059) Remove if C libraries support dylib linkage in the future.
+	if !ctx.Host() && (library.static() || library.shared()) {
+		library.setNoStdlibs()
+		for _, stdlib := range config.Stdlibs {
+			deps.Rlibs = append(deps.Rlibs, stdlib+".static")
+		}
+	}
+
+	deps = library.baseCompiler.compilerDeps(ctx, deps)
+
+	if ctx.toolchain().Bionic() && (library.dylib() || library.shared()) {
+		deps = library.baseCompiler.bionicDeps(ctx, deps)
+	}
+
+	return deps
+}
+func (library *libraryDecorator) compilerFlags(ctx ModuleContext, flags Flags) Flags {
+	flags = library.baseCompiler.compilerFlags(ctx, flags)
+	if library.shared() || library.static() {
+		library.includeDirs = append(library.includeDirs, android.PathsForModuleSrc(ctx, library.Properties.Include_dirs)...)
+	}
+	return flags
+}
+
+func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path {
+	var outputFile android.WritablePath
+
+	srcPath := srcPathFromModuleSrcs(ctx, library.Properties.Srcs)
+
+	flags.RustFlags = append(flags.RustFlags, deps.depFlags...)
+
+	if library.dylib() || library.shared() {
+		// We need prefer-dynamic for now to avoid linking in the static stdlib. See:
+		// https://github.com/rust-lang/rust/issues/19680
+		// https://github.com/rust-lang/rust/issues/34909
+		flags.RustFlags = append(flags.RustFlags, "-C prefer-dynamic")
+	}
+
+	if library.rlib() {
+		fileName := library.getStem(ctx) + ctx.toolchain().RlibSuffix()
+		outputFile = android.PathForModuleOut(ctx, fileName)
+
+		TransformSrctoRlib(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
+	} else if library.dylib() {
+		fileName := library.getStem(ctx) + ctx.toolchain().DylibSuffix()
+		outputFile = android.PathForModuleOut(ctx, fileName)
+
+		TransformSrctoDylib(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
+	} else if library.static() {
+		fileName := library.getStem(ctx) + ctx.toolchain().StaticLibSuffix()
+		outputFile = android.PathForModuleOut(ctx, fileName)
+
+		TransformSrctoStatic(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
+	} else if library.shared() {
+		fileName := library.getStem(ctx) + ctx.toolchain().SharedLibSuffix()
+		outputFile = android.PathForModuleOut(ctx, fileName)
+
+		TransformSrctoShared(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
+	}
+
+	if library.rlib() || library.dylib() {
+		library.reexportDirs(deps.linkDirs...)
+		library.reexportDepFlags(deps.depFlags...)
+	}
+	library.unstrippedOutputFile = outputFile
+
+	return outputFile
+}
+
+func (library *libraryDecorator) getStem(ctx ModuleContext) string {
+	stem := library.baseCompiler.getStemWithoutSuffix(ctx)
+	validateLibraryStem(ctx, stem, library.crateName())
+
+	return stem + String(library.baseCompiler.Properties.Suffix)
+}
+
+var validCrateName = regexp.MustCompile("[^a-zA-Z0-9_]+")
+
+func validateLibraryStem(ctx BaseModuleContext, filename string, crate_name string) {
+	if crate_name == "" {
+		ctx.PropertyErrorf("crate_name", "crate_name must be defined.")
+	}
+
+	// crate_names are used for the library output file, and rustc expects these
+	// to be alphanumeric with underscores allowed.
+	if validCrateName.MatchString(crate_name) {
+		ctx.PropertyErrorf("crate_name",
+			"library crate_names must be alphanumeric with underscores allowed")
+	}
+
+	// Libraries are expected to begin with "lib" followed by the crate_name
+	if !strings.HasPrefix(filename, "lib"+crate_name) {
+		ctx.ModuleErrorf("Invalid name or stem property; library filenames must start with lib<crate_name>")
+	}
+}
+
+func LibraryMutator(mctx android.BottomUpMutatorContext) {
+	if m, ok := mctx.Module().(*Module); ok && m.compiler != nil {
+		switch library := m.compiler.(type) {
+		case libraryInterface:
+
+			// We only build the rust library variants here. This assumes that
+			// LinkageMutator runs first and there's an empty variant
+			// if rust variants are required.
+			if !library.static() && !library.shared() {
+				if library.buildRlib() && library.buildDylib() {
+					modules := mctx.CreateLocalVariations("rlib", "dylib")
+					rlib := modules[0].(*Module)
+					dylib := modules[1].(*Module)
+
+					rlib.compiler.(libraryInterface).setRlib()
+					dylib.compiler.(libraryInterface).setDylib()
+				} else if library.buildRlib() {
+					modules := mctx.CreateLocalVariations("rlib")
+					modules[0].(*Module).compiler.(libraryInterface).setRlib()
+				} else if library.buildDylib() {
+					modules := mctx.CreateLocalVariations("dylib")
+					modules[0].(*Module).compiler.(libraryInterface).setDylib()
+				}
+			}
+		}
+	}
+}
diff --git a/rust/library_test.go b/rust/library_test.go
new file mode 100644
index 0000000..9f9f374
--- /dev/null
+++ b/rust/library_test.go
@@ -0,0 +1,116 @@
+// Copyright 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rust
+
+import (
+	"strings"
+	"testing"
+)
+
+// Test that variants are being generated correctly, and that crate-types are correct.
+func TestLibraryVariants(t *testing.T) {
+
+	ctx := testRust(t, `
+		rust_library_host {
+			name: "libfoo",
+			srcs: ["foo.rs"],
+			crate_name: "foo",
+		}`)
+
+	// Test all variants are being built.
+	libfooRlib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_rlib").Output("libfoo.rlib")
+	libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Output("libfoo.dylib.so")
+	libfooStatic := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_static").Output("libfoo.a")
+	libfooShared := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_shared").Output("libfoo.so")
+
+	rlibCrateType := "rlib"
+	dylibCrateType := "dylib"
+	sharedCrateType := "cdylib"
+	staticCrateType := "static"
+
+	// Test crate type for rlib is correct.
+	if !strings.Contains(libfooRlib.Args["rustcFlags"], "crate-type="+rlibCrateType) {
+		t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", rlibCrateType, libfooRlib.Args["rustcFlags"])
+	}
+
+	// Test crate type for dylib is correct.
+	if !strings.Contains(libfooDylib.Args["rustcFlags"], "crate-type="+dylibCrateType) {
+		t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", dylibCrateType, libfooDylib.Args["rustcFlags"])
+	}
+
+	// Test crate type for C static libraries is correct.
+	if !strings.Contains(libfooStatic.Args["rustcFlags"], "crate-type="+staticCrateType) {
+		t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", staticCrateType, libfooStatic.Args["rustcFlags"])
+	}
+
+	// Test crate type for C shared libraries is correct.
+	if !strings.Contains(libfooShared.Args["rustcFlags"], "crate-type="+sharedCrateType) {
+		t.Errorf("missing crate-type for shared variant, expecting %#v, got rustcFlags: %#v", sharedCrateType, libfooShared.Args["rustcFlags"])
+	}
+
+}
+
+// Test that dylibs are not statically linking the standard library.
+func TestDylibPreferDynamic(t *testing.T) {
+	ctx := testRust(t, `
+		rust_library_host_dylib {
+			name: "libfoo",
+			srcs: ["foo.rs"],
+			crate_name: "foo",
+		}`)
+
+	libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Output("libfoo.dylib.so")
+
+	if !strings.Contains(libfooDylib.Args["rustcFlags"], "prefer-dynamic") {
+		t.Errorf("missing prefer-dynamic flag for libfoo dylib, rustcFlags: %#v", libfooDylib.Args["rustcFlags"])
+	}
+}
+
+func TestValidateLibraryStem(t *testing.T) {
+	testRustError(t, "crate_name must be defined.", `
+			rust_library_host {
+				name: "libfoo",
+				srcs: ["foo.rs"],
+			}`)
+
+	testRustError(t, "library crate_names must be alphanumeric with underscores allowed", `
+			rust_library_host {
+				name: "libfoo-bar",
+				srcs: ["foo.rs"],
+				crate_name: "foo-bar"
+			}`)
+
+	testRustError(t, "Invalid name or stem property; library filenames must start with lib<crate_name>", `
+			rust_library_host {
+				name: "foobar",
+				srcs: ["foo.rs"],
+				crate_name: "foo_bar"
+			}`)
+	testRustError(t, "Invalid name or stem property; library filenames must start with lib<crate_name>", `
+			rust_library_host {
+				name: "foobar",
+				stem: "libfoo",
+				srcs: ["foo.rs"],
+				crate_name: "foo_bar"
+			}`)
+	testRustError(t, "Invalid name or stem property; library filenames must start with lib<crate_name>", `
+			rust_library_host {
+				name: "foobar",
+				stem: "foo_bar",
+				srcs: ["foo.rs"],
+				crate_name: "foo_bar"
+			}`)
+
+}
diff --git a/rust/prebuilt.go b/rust/prebuilt.go
new file mode 100644
index 0000000..45bef9e
--- /dev/null
+++ b/rust/prebuilt.go
@@ -0,0 +1,71 @@
+// Copyright 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rust
+
+import (
+	"android/soong/android"
+)
+
+func init() {
+	android.RegisterModuleType("rust_prebuilt_dylib", PrebuiltDylibFactory)
+}
+
+type PrebuiltProperties struct {
+	// path to the prebuilt file
+	Srcs []string `android:"path,arch_variant"`
+}
+
+type prebuiltLibraryDecorator struct {
+	*libraryDecorator
+	Properties PrebuiltProperties
+}
+
+var _ compiler = (*prebuiltLibraryDecorator)(nil)
+
+func PrebuiltDylibFactory() android.Module {
+	module, _ := NewPrebuiltDylib(android.HostAndDeviceSupported)
+	return module.Init()
+}
+
+func NewPrebuiltDylib(hod android.HostOrDeviceSupported) (*Module, *prebuiltLibraryDecorator) {
+	module, library := NewRustLibrary(hod)
+	library.BuildOnlyDylib()
+	library.setNoStdlibs()
+	library.setDylib()
+	prebuilt := &prebuiltLibraryDecorator{
+		libraryDecorator: library,
+	}
+	module.compiler = prebuilt
+	module.AddProperties(&library.Properties)
+	return module, prebuilt
+}
+
+func (prebuilt *prebuiltLibraryDecorator) compilerProps() []interface{} {
+	return append(prebuilt.baseCompiler.compilerProps(),
+		&prebuilt.Properties)
+}
+
+func (prebuilt *prebuiltLibraryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path {
+	srcPath := srcPathFromModuleSrcs(ctx, prebuilt.Properties.Srcs)
+
+	prebuilt.unstrippedOutputFile = srcPath
+
+	return srcPath
+}
+
+func (prebuilt *prebuiltLibraryDecorator) compilerDeps(ctx DepsContext, deps Deps) Deps {
+	deps = prebuilt.baseCompiler.compilerDeps(ctx, deps)
+	return deps
+}
diff --git a/rust/proc_macro.go b/rust/proc_macro.go
new file mode 100644
index 0000000..10ea1e3
--- /dev/null
+++ b/rust/proc_macro.go
@@ -0,0 +1,86 @@
+// Copyright 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rust
+
+import (
+	"android/soong/android"
+)
+
+func init() {
+	android.RegisterModuleType("rust_proc_macro", ProcMacroFactory)
+}
+
+type ProcMacroCompilerProperties struct {
+	// path to the source file that is the main entry point of the program (e.g. src/lib.rs)
+	Srcs []string `android:"path,arch_variant"`
+
+	// set name of the procMacro
+	Stem   *string `android:"arch_variant"`
+	Suffix *string `android:"arch_variant"`
+}
+
+type procMacroDecorator struct {
+	*baseCompiler
+
+	Properties           ProcMacroCompilerProperties
+	distFile             android.OptionalPath
+	unstrippedOutputFile android.Path
+}
+
+type procMacroInterface interface {
+}
+
+var _ compiler = (*procMacroDecorator)(nil)
+
+func ProcMacroFactory() android.Module {
+	module, _ := NewProcMacro(android.HostSupportedNoCross)
+	return module.Init()
+}
+
+func NewProcMacro(hod android.HostOrDeviceSupported) (*Module, *procMacroDecorator) {
+	module := newModule(hod, android.MultilibFirst)
+
+	procMacro := &procMacroDecorator{
+		baseCompiler: NewBaseCompiler("lib", "lib64", InstallInSystem),
+	}
+
+	module.compiler = procMacro
+
+	return module, procMacro
+}
+
+func (procMacro *procMacroDecorator) compilerProps() []interface{} {
+	return append(procMacro.baseCompiler.compilerProps(),
+		&procMacro.Properties)
+}
+
+func (procMacro *procMacroDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path {
+	fileName := procMacro.getStem(ctx) + ctx.toolchain().ProcMacroSuffix()
+	outputFile := android.PathForModuleOut(ctx, fileName)
+
+	srcPath := srcPathFromModuleSrcs(ctx, procMacro.Properties.Srcs)
+
+	procMacro.unstrippedOutputFile = outputFile
+
+	TransformSrctoProcMacro(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
+	return outputFile
+}
+
+func (procMacro *procMacroDecorator) getStem(ctx ModuleContext) string {
+	stem := procMacro.baseCompiler.getStemWithoutSuffix(ctx)
+	validateLibraryStem(ctx, stem, procMacro.crateName())
+
+	return stem + String(procMacro.baseCompiler.Properties.Suffix)
+}
diff --git a/rust/rust.go b/rust/rust.go
new file mode 100644
index 0000000..14513fb
--- /dev/null
+++ b/rust/rust.go
@@ -0,0 +1,767 @@
+// Copyright 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rust
+
+import (
+	"fmt"
+	"strings"
+
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+
+	"android/soong/android"
+	"android/soong/cc"
+	"android/soong/rust/config"
+)
+
+var pctx = android.NewPackageContext("android/soong/rust")
+
+func init() {
+	// Only allow rust modules to be defined for certain projects
+
+	android.AddNeverAllowRules(
+		android.NeverAllow().
+			NotIn(config.RustAllowedPaths...).
+			ModuleType(config.RustModuleTypes...))
+
+	android.RegisterModuleType("rust_defaults", defaultsFactory)
+	android.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
+		ctx.BottomUp("rust_libraries", LibraryMutator).Parallel()
+		ctx.BottomUp("rust_unit_tests", TestPerSrcMutator).Parallel()
+	})
+	pctx.Import("android/soong/rust/config")
+}
+
+type Flags struct {
+	GlobalRustFlags []string      // Flags that apply globally to rust
+	GlobalLinkFlags []string      // Flags that apply globally to linker
+	RustFlags       []string      // Flags that apply to rust
+	LinkFlags       []string      // Flags that apply to linker
+	RustFlagsDeps   android.Paths // Files depended on by compiler flags
+	Toolchain       config.Toolchain
+}
+
+type BaseProperties struct {
+	AndroidMkRlibs         []string
+	AndroidMkDylibs        []string
+	AndroidMkProcMacroLibs []string
+	AndroidMkSharedLibs    []string
+	AndroidMkStaticLibs    []string
+	SubName                string `blueprint:"mutated"`
+}
+
+type Module struct {
+	android.ModuleBase
+	android.DefaultableModuleBase
+
+	Properties BaseProperties
+
+	hod      android.HostOrDeviceSupported
+	multilib android.Multilib
+
+	compiler         compiler
+	cachedToolchain  config.Toolchain
+	subAndroidMkOnce map[subAndroidMkProvider]bool
+	outputFile       android.OptionalPath
+}
+
+var _ android.ImageInterface = (*Module)(nil)
+
+func (mod *Module) ImageMutatorBegin(ctx android.BaseModuleContext) {}
+
+func (mod *Module) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
+	return true
+}
+
+func (mod *Module) RecoveryVariantNeeded(android.BaseModuleContext) bool {
+	return mod.InRecovery()
+}
+
+func (mod *Module) ExtraImageVariations(android.BaseModuleContext) []string {
+	return nil
+}
+
+func (c *Module) SetImageVariation(ctx android.BaseModuleContext, variant string, module android.Module) {
+}
+
+func (mod *Module) BuildStubs() bool {
+	return false
+}
+
+func (mod *Module) HasStubsVariants() bool {
+	return false
+}
+
+func (mod *Module) SelectedStl() string {
+	return ""
+}
+
+func (mod *Module) NonCcVariants() bool {
+	if mod.compiler != nil {
+		if library, ok := mod.compiler.(libraryInterface); ok {
+			if library.buildRlib() || library.buildDylib() {
+				return true
+			} else {
+				return false
+			}
+		}
+	}
+	panic(fmt.Errorf("NonCcVariants called on non-library module: %q", mod.BaseModuleName()))
+}
+
+func (mod *Module) ApiLevel() string {
+	panic(fmt.Errorf("Called ApiLevel on Rust module %q; stubs libraries are not yet supported.", mod.BaseModuleName()))
+}
+
+func (mod *Module) Static() bool {
+	if mod.compiler != nil {
+		if library, ok := mod.compiler.(libraryInterface); ok {
+			return library.static()
+		}
+	}
+	panic(fmt.Errorf("Static called on non-library module: %q", mod.BaseModuleName()))
+}
+
+func (mod *Module) Shared() bool {
+	if mod.compiler != nil {
+		if library, ok := mod.compiler.(libraryInterface); ok {
+			return library.static()
+		}
+	}
+	panic(fmt.Errorf("Shared called on non-library module: %q", mod.BaseModuleName()))
+}
+
+func (mod *Module) Toc() android.OptionalPath {
+	if mod.compiler != nil {
+		if _, ok := mod.compiler.(libraryInterface); ok {
+			return android.OptionalPath{}
+		}
+	}
+	panic(fmt.Errorf("Toc() called on non-library module: %q", mod.BaseModuleName()))
+}
+
+func (mod *Module) OnlyInRecovery() bool {
+	return false
+}
+
+func (mod *Module) UseVndk() bool {
+	return false
+}
+
+func (mod *Module) MustUseVendorVariant() bool {
+	return false
+}
+
+func (mod *Module) IsVndk() bool {
+	return false
+}
+
+func (mod *Module) HasVendorVariant() bool {
+	return false
+}
+
+func (mod *Module) SdkVersion() string {
+	return ""
+}
+
+func (mod *Module) ToolchainLibrary() bool {
+	return false
+}
+
+func (mod *Module) NdkPrebuiltStl() bool {
+	return false
+}
+
+func (mod *Module) StubDecorator() bool {
+	return false
+}
+
+type Deps struct {
+	Dylibs     []string
+	Rlibs      []string
+	ProcMacros []string
+	SharedLibs []string
+	StaticLibs []string
+
+	CrtBegin, CrtEnd string
+}
+
+type PathDeps struct {
+	DyLibs     RustLibraries
+	RLibs      RustLibraries
+	SharedLibs android.Paths
+	StaticLibs android.Paths
+	ProcMacros RustLibraries
+	linkDirs   []string
+	depFlags   []string
+	//ReexportedDeps android.Paths
+
+	CrtBegin android.OptionalPath
+	CrtEnd   android.OptionalPath
+}
+
+type RustLibraries []RustLibrary
+
+type RustLibrary struct {
+	Path      android.Path
+	CrateName string
+}
+
+type compiler interface {
+	compilerFlags(ctx ModuleContext, flags Flags) Flags
+	compilerProps() []interface{}
+	compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path
+	compilerDeps(ctx DepsContext, deps Deps) Deps
+	crateName() string
+
+	inData() bool
+	install(ctx ModuleContext, path android.Path)
+	relativeInstallPath() string
+}
+
+func defaultsFactory() android.Module {
+	return DefaultsFactory()
+}
+
+type Defaults struct {
+	android.ModuleBase
+	android.DefaultsModuleBase
+}
+
+func DefaultsFactory(props ...interface{}) android.Module {
+	module := &Defaults{}
+
+	module.AddProperties(props...)
+	module.AddProperties(
+		&BaseProperties{},
+		&BaseCompilerProperties{},
+		&BinaryCompilerProperties{},
+		&LibraryCompilerProperties{},
+		&ProcMacroCompilerProperties{},
+		&PrebuiltProperties{},
+		&TestProperties{},
+	)
+
+	android.InitDefaultsModule(module)
+	return module
+}
+
+func (mod *Module) CrateName() string {
+	return mod.compiler.crateName()
+}
+
+func (mod *Module) CcLibrary() bool {
+	if mod.compiler != nil {
+		if _, ok := mod.compiler.(*libraryDecorator); ok {
+			return true
+		}
+	}
+	return false
+}
+
+func (mod *Module) CcLibraryInterface() bool {
+	if mod.compiler != nil {
+		if _, ok := mod.compiler.(libraryInterface); ok {
+			return true
+		}
+	}
+	return false
+}
+
+func (mod *Module) IncludeDirs() android.Paths {
+	if mod.compiler != nil {
+		if library, ok := mod.compiler.(*libraryDecorator); ok {
+			return library.includeDirs
+		}
+	}
+	panic(fmt.Errorf("IncludeDirs called on non-library module: %q", mod.BaseModuleName()))
+}
+
+func (mod *Module) SetStatic() {
+	if mod.compiler != nil {
+		if library, ok := mod.compiler.(libraryInterface); ok {
+			library.setStatic()
+			return
+		}
+	}
+	panic(fmt.Errorf("SetStatic called on non-library module: %q", mod.BaseModuleName()))
+}
+
+func (mod *Module) SetShared() {
+	if mod.compiler != nil {
+		if library, ok := mod.compiler.(libraryInterface); ok {
+			library.setShared()
+			return
+		}
+	}
+	panic(fmt.Errorf("SetShared called on non-library module: %q", mod.BaseModuleName()))
+}
+
+func (mod *Module) SetBuildStubs() {
+	panic("SetBuildStubs not yet implemented for rust modules")
+}
+
+func (mod *Module) SetStubsVersions(string) {
+	panic("SetStubsVersions not yet implemented for rust modules")
+}
+
+func (mod *Module) BuildStaticVariant() bool {
+	if mod.compiler != nil {
+		if library, ok := mod.compiler.(libraryInterface); ok {
+			return library.buildStatic()
+		}
+	}
+	panic(fmt.Errorf("BuildStaticVariant called on non-library module: %q", mod.BaseModuleName()))
+}
+
+func (mod *Module) BuildSharedVariant() bool {
+	if mod.compiler != nil {
+		if library, ok := mod.compiler.(libraryInterface); ok {
+			return library.buildShared()
+		}
+	}
+	panic(fmt.Errorf("BuildSharedVariant called on non-library module: %q", mod.BaseModuleName()))
+}
+
+// Rust module deps don't have a link order (?)
+func (mod *Module) SetDepsInLinkOrder([]android.Path) {}
+
+func (mod *Module) GetDepsInLinkOrder() []android.Path {
+	return []android.Path{}
+}
+
+func (mod *Module) GetStaticVariant() cc.LinkableInterface {
+	return nil
+}
+
+func (mod *Module) AllStaticDeps() []string {
+	// TODO(jiyong): do this for rust?
+	return nil
+}
+
+func (mod *Module) Module() android.Module {
+	return mod
+}
+
+func (mod *Module) StubsVersions() []string {
+	// For now, Rust has no stubs versions.
+	if mod.compiler != nil {
+		if _, ok := mod.compiler.(*libraryDecorator); ok {
+			return []string{}
+		}
+	}
+	panic(fmt.Errorf("StubsVersions called on non-library module: %q", mod.BaseModuleName()))
+}
+
+func (mod *Module) OutputFile() android.OptionalPath {
+	return mod.outputFile
+}
+
+func (mod *Module) InRecovery() bool {
+	// For now, Rust has no notion of the recovery image
+	return false
+}
+func (mod *Module) HasStaticVariant() bool {
+	if mod.GetStaticVariant() != nil {
+		return true
+	}
+	return false
+}
+
+var _ cc.LinkableInterface = (*Module)(nil)
+
+func (mod *Module) Init() android.Module {
+	mod.AddProperties(&mod.Properties)
+
+	if mod.compiler != nil {
+		mod.AddProperties(mod.compiler.compilerProps()...)
+	}
+	android.InitAndroidArchModule(mod, mod.hod, mod.multilib)
+
+	android.InitDefaultableModule(mod)
+
+	// Explicitly disable unsupported targets.
+	android.AddLoadHook(mod, func(ctx android.LoadHookContext) {
+		disableTargets := struct {
+			Target struct {
+				Linux_bionic struct {
+					Enabled *bool
+				}
+			}
+		}{}
+		disableTargets.Target.Linux_bionic.Enabled = proptools.BoolPtr(false)
+
+		ctx.AppendProperties(&disableTargets)
+	})
+
+	return mod
+}
+
+func newBaseModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *Module {
+	return &Module{
+		hod:      hod,
+		multilib: multilib,
+	}
+}
+func newModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *Module {
+	module := newBaseModule(hod, multilib)
+	return module
+}
+
+type ModuleContext interface {
+	android.ModuleContext
+	ModuleContextIntf
+}
+
+type BaseModuleContext interface {
+	android.BaseModuleContext
+	ModuleContextIntf
+}
+
+type DepsContext interface {
+	android.BottomUpMutatorContext
+	ModuleContextIntf
+}
+
+type ModuleContextIntf interface {
+	toolchain() config.Toolchain
+	baseModuleName() string
+	CrateName() string
+}
+
+type depsContext struct {
+	android.BottomUpMutatorContext
+	moduleContextImpl
+}
+
+type moduleContext struct {
+	android.ModuleContext
+	moduleContextImpl
+}
+
+type moduleContextImpl struct {
+	mod *Module
+	ctx BaseModuleContext
+}
+
+func (ctx *moduleContextImpl) toolchain() config.Toolchain {
+	return ctx.mod.toolchain(ctx.ctx)
+}
+
+func (mod *Module) toolchain(ctx android.BaseModuleContext) config.Toolchain {
+	if mod.cachedToolchain == nil {
+		mod.cachedToolchain = config.FindToolchain(ctx.Os(), ctx.Arch())
+	}
+	return mod.cachedToolchain
+}
+
+func (d *Defaults) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+}
+
+func (mod *Module) GenerateAndroidBuildActions(actx android.ModuleContext) {
+	ctx := &moduleContext{
+		ModuleContext: actx,
+		moduleContextImpl: moduleContextImpl{
+			mod: mod,
+		},
+	}
+	ctx.ctx = ctx
+
+	toolchain := mod.toolchain(ctx)
+
+	if !toolchain.Supported() {
+		// This toolchain's unsupported, there's nothing to do for this mod.
+		return
+	}
+
+	deps := mod.depsToPaths(ctx)
+	flags := Flags{
+		Toolchain: toolchain,
+	}
+
+	if mod.compiler != nil {
+		flags = mod.compiler.compilerFlags(ctx, flags)
+		outputFile := mod.compiler.compile(ctx, flags, deps)
+		mod.outputFile = android.OptionalPathForPath(outputFile)
+		mod.compiler.install(ctx, mod.outputFile.Path())
+	}
+}
+
+func (mod *Module) deps(ctx DepsContext) Deps {
+	deps := Deps{}
+
+	if mod.compiler != nil {
+		deps = mod.compiler.compilerDeps(ctx, deps)
+	}
+
+	deps.Rlibs = android.LastUniqueStrings(deps.Rlibs)
+	deps.Dylibs = android.LastUniqueStrings(deps.Dylibs)
+	deps.ProcMacros = android.LastUniqueStrings(deps.ProcMacros)
+	deps.SharedLibs = android.LastUniqueStrings(deps.SharedLibs)
+	deps.StaticLibs = android.LastUniqueStrings(deps.StaticLibs)
+
+	return deps
+
+}
+
+func (ctx *moduleContextImpl) baseModuleName() string {
+	return ctx.mod.ModuleBase.BaseModuleName()
+}
+
+func (ctx *moduleContextImpl) CrateName() string {
+	return ctx.mod.CrateName()
+}
+
+type dependencyTag struct {
+	blueprint.BaseDependencyTag
+	name       string
+	library    bool
+	proc_macro bool
+}
+
+var (
+	rlibDepTag       = dependencyTag{name: "rlibTag", library: true}
+	dylibDepTag      = dependencyTag{name: "dylib", library: true}
+	procMacroDepTag  = dependencyTag{name: "procMacro", proc_macro: true}
+	testPerSrcDepTag = dependencyTag{name: "rust_unit_tests"}
+)
+
+func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
+	var depPaths PathDeps
+
+	directRlibDeps := []*Module{}
+	directDylibDeps := []*Module{}
+	directProcMacroDeps := []*Module{}
+	directSharedLibDeps := [](cc.LinkableInterface){}
+	directStaticLibDeps := [](cc.LinkableInterface){}
+
+	ctx.VisitDirectDeps(func(dep android.Module) {
+		depName := ctx.OtherModuleName(dep)
+		depTag := ctx.OtherModuleDependencyTag(dep)
+		if rustDep, ok := dep.(*Module); ok {
+			//Handle Rust Modules
+
+			linkFile := rustDep.outputFile
+			if !linkFile.Valid() {
+				ctx.ModuleErrorf("Invalid output file when adding dep %q to %q", depName, ctx.ModuleName())
+			}
+
+			switch depTag {
+			case dylibDepTag:
+				dylib, ok := rustDep.compiler.(libraryInterface)
+				if !ok || !dylib.dylib() {
+					ctx.ModuleErrorf("mod %q not an dylib library", depName)
+					return
+				}
+				directDylibDeps = append(directDylibDeps, rustDep)
+				mod.Properties.AndroidMkDylibs = append(mod.Properties.AndroidMkDylibs, depName)
+			case rlibDepTag:
+				rlib, ok := rustDep.compiler.(libraryInterface)
+				if !ok || !rlib.rlib() {
+					ctx.ModuleErrorf("mod %q not an rlib library", depName)
+					return
+				}
+				directRlibDeps = append(directRlibDeps, rustDep)
+				mod.Properties.AndroidMkRlibs = append(mod.Properties.AndroidMkRlibs, depName)
+			case procMacroDepTag:
+				directProcMacroDeps = append(directProcMacroDeps, rustDep)
+				mod.Properties.AndroidMkProcMacroLibs = append(mod.Properties.AndroidMkProcMacroLibs, depName)
+			}
+
+			//Append the dependencies exportedDirs
+			if lib, ok := rustDep.compiler.(*libraryDecorator); ok {
+				depPaths.linkDirs = append(depPaths.linkDirs, lib.exportedDirs()...)
+				depPaths.depFlags = append(depPaths.depFlags, lib.exportedDepFlags()...)
+			}
+
+			// Append this dependencies output to this mod's linkDirs so they can be exported to dependencies
+			// This can be probably be refactored by defining a common exporter interface similar to cc's
+			if depTag == dylibDepTag || depTag == rlibDepTag || depTag == procMacroDepTag {
+				linkDir := linkPathFromFilePath(linkFile.Path())
+				if lib, ok := mod.compiler.(*libraryDecorator); ok {
+					lib.linkDirs = append(lib.linkDirs, linkDir)
+				} else if procMacro, ok := mod.compiler.(*procMacroDecorator); ok {
+					procMacro.linkDirs = append(procMacro.linkDirs, linkDir)
+				}
+			}
+
+		}
+
+		if ccDep, ok := dep.(cc.LinkableInterface); ok {
+			//Handle C dependencies
+			if _, ok := ccDep.(*Module); !ok {
+				if ccDep.Module().Target().Os != ctx.Os() {
+					ctx.ModuleErrorf("OS mismatch between %q and %q", ctx.ModuleName(), depName)
+					return
+				}
+				if ccDep.Module().Target().Arch.ArchType != ctx.Arch().ArchType {
+					ctx.ModuleErrorf("Arch mismatch between %q and %q", ctx.ModuleName(), depName)
+					return
+				}
+			}
+
+			linkFile := ccDep.OutputFile()
+			linkPath := linkPathFromFilePath(linkFile.Path())
+			libName := libNameFromFilePath(linkFile.Path())
+			if !linkFile.Valid() {
+				ctx.ModuleErrorf("Invalid output file when adding dep %q to %q", depName, ctx.ModuleName())
+			}
+
+			exportDep := false
+
+			switch depTag {
+			case cc.StaticDepTag:
+				depPaths.linkDirs = append(depPaths.linkDirs, linkPath)
+				depPaths.depFlags = append(depPaths.depFlags, "-l"+libName)
+				directStaticLibDeps = append(directStaticLibDeps, ccDep)
+				mod.Properties.AndroidMkStaticLibs = append(mod.Properties.AndroidMkStaticLibs, depName)
+			case cc.SharedDepTag:
+				depPaths.linkDirs = append(depPaths.linkDirs, linkPath)
+				depPaths.depFlags = append(depPaths.depFlags, "-l"+libName)
+				directSharedLibDeps = append(directSharedLibDeps, ccDep)
+				mod.Properties.AndroidMkSharedLibs = append(mod.Properties.AndroidMkSharedLibs, depName)
+				exportDep = true
+			case cc.CrtBeginDepTag:
+				depPaths.CrtBegin = linkFile
+			case cc.CrtEndDepTag:
+				depPaths.CrtEnd = linkFile
+			}
+
+			// Make sure these dependencies are propagated
+			if lib, ok := mod.compiler.(*libraryDecorator); ok && exportDep {
+				lib.linkDirs = append(lib.linkDirs, linkPath)
+				lib.depFlags = append(lib.depFlags, "-l"+libName)
+			} else if procMacro, ok := mod.compiler.(*procMacroDecorator); ok && exportDep {
+				procMacro.linkDirs = append(procMacro.linkDirs, linkPath)
+				procMacro.depFlags = append(procMacro.depFlags, "-l"+libName)
+			}
+
+		}
+	})
+
+	var rlibDepFiles RustLibraries
+	for _, dep := range directRlibDeps {
+		rlibDepFiles = append(rlibDepFiles, RustLibrary{Path: dep.outputFile.Path(), CrateName: dep.CrateName()})
+	}
+	var dylibDepFiles RustLibraries
+	for _, dep := range directDylibDeps {
+		dylibDepFiles = append(dylibDepFiles, RustLibrary{Path: dep.outputFile.Path(), CrateName: dep.CrateName()})
+	}
+	var procMacroDepFiles RustLibraries
+	for _, dep := range directProcMacroDeps {
+		procMacroDepFiles = append(procMacroDepFiles, RustLibrary{Path: dep.outputFile.Path(), CrateName: dep.CrateName()})
+	}
+
+	var staticLibDepFiles android.Paths
+	for _, dep := range directStaticLibDeps {
+		staticLibDepFiles = append(staticLibDepFiles, dep.OutputFile().Path())
+	}
+
+	var sharedLibDepFiles android.Paths
+	for _, dep := range directSharedLibDeps {
+		sharedLibDepFiles = append(sharedLibDepFiles, dep.OutputFile().Path())
+	}
+
+	depPaths.RLibs = append(depPaths.RLibs, rlibDepFiles...)
+	depPaths.DyLibs = append(depPaths.DyLibs, dylibDepFiles...)
+	depPaths.SharedLibs = append(depPaths.SharedLibs, sharedLibDepFiles...)
+	depPaths.StaticLibs = append(depPaths.StaticLibs, staticLibDepFiles...)
+	depPaths.ProcMacros = append(depPaths.ProcMacros, procMacroDepFiles...)
+
+	// Dedup exported flags from dependencies
+	depPaths.linkDirs = android.FirstUniqueStrings(depPaths.linkDirs)
+	depPaths.depFlags = android.FirstUniqueStrings(depPaths.depFlags)
+
+	return depPaths
+}
+
+func (mod *Module) InstallInData() bool {
+	if mod.compiler == nil {
+		return false
+	}
+	return mod.compiler.inData()
+}
+
+func linkPathFromFilePath(filepath android.Path) string {
+	return strings.Split(filepath.String(), filepath.Base())[0]
+}
+func libNameFromFilePath(filepath android.Path) string {
+	libName := strings.Split(filepath.Base(), filepath.Ext())[0]
+	if strings.HasPrefix(libName, "lib") {
+		libName = libName[3:]
+	}
+	return libName
+}
+func (mod *Module) DepsMutator(actx android.BottomUpMutatorContext) {
+	ctx := &depsContext{
+		BottomUpMutatorContext: actx,
+		moduleContextImpl: moduleContextImpl{
+			mod: mod,
+		},
+	}
+	ctx.ctx = ctx
+
+	deps := mod.deps(ctx)
+	commonDepVariations := []blueprint.Variation{}
+	commonDepVariations = append(commonDepVariations,
+		blueprint.Variation{Mutator: "version", Variation: ""})
+	if !mod.Host() {
+		commonDepVariations = append(commonDepVariations,
+			blueprint.Variation{Mutator: "image", Variation: android.CoreVariation})
+	}
+
+	actx.AddVariationDependencies(
+		append(commonDepVariations, []blueprint.Variation{
+			{Mutator: "rust_libraries", Variation: "rlib"},
+			{Mutator: "link", Variation: ""}}...),
+		rlibDepTag, deps.Rlibs...)
+	actx.AddVariationDependencies(
+		append(commonDepVariations, []blueprint.Variation{
+			{Mutator: "rust_libraries", Variation: "dylib"},
+			{Mutator: "link", Variation: ""}}...),
+		dylibDepTag, deps.Dylibs...)
+
+	actx.AddVariationDependencies(append(commonDepVariations,
+		blueprint.Variation{Mutator: "link", Variation: "shared"}),
+		cc.SharedDepTag, deps.SharedLibs...)
+	actx.AddVariationDependencies(append(commonDepVariations,
+		blueprint.Variation{Mutator: "link", Variation: "static"}),
+		cc.StaticDepTag, deps.StaticLibs...)
+
+	if deps.CrtBegin != "" {
+		actx.AddVariationDependencies(commonDepVariations, cc.CrtBeginDepTag, deps.CrtBegin)
+	}
+	if deps.CrtEnd != "" {
+		actx.AddVariationDependencies(commonDepVariations, cc.CrtEndDepTag, deps.CrtEnd)
+	}
+
+	// proc_macros are compiler plugins, and so we need the host arch variant as a dependendcy.
+	actx.AddFarVariationDependencies(ctx.Config().BuildOSTarget.Variations(), procMacroDepTag, deps.ProcMacros...)
+}
+
+func (mod *Module) Name() string {
+	name := mod.ModuleBase.Name()
+	if p, ok := mod.compiler.(interface {
+		Name(string) string
+	}); ok {
+		name = p.Name(name)
+	}
+	return name
+}
+
+var Bool = proptools.Bool
+var BoolDefault = proptools.BoolDefault
+var String = proptools.String
+var StringPtr = proptools.StringPtr
diff --git a/rust/rust_test.go b/rust/rust_test.go
new file mode 100644
index 0000000..3be9ee7
--- /dev/null
+++ b/rust/rust_test.go
@@ -0,0 +1,266 @@
+// Copyright 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rust
+
+import (
+	"io/ioutil"
+	"os"
+	"runtime"
+	"strings"
+	"testing"
+
+	"android/soong/android"
+)
+
+var (
+	buildDir string
+)
+
+func setUp() {
+	var err error
+	buildDir, err = ioutil.TempDir("", "soong_rust_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 testConfig(bp string) android.Config {
+	bp = bp + GatherRequiredDepsForTest()
+
+	fs := map[string][]byte{
+		"foo.rs":     nil,
+		"src/bar.rs": nil,
+		"liby.so":    nil,
+		"libz.so":    nil,
+	}
+
+	return android.TestArchConfig(buildDir, nil, bp, fs)
+}
+
+func testRust(t *testing.T, bp string) *android.TestContext {
+	// TODO (b/140435149)
+	if runtime.GOOS != "linux" {
+		t.Skip("Only the Linux toolchain is supported for Rust")
+	}
+
+	t.Helper()
+	config := testConfig(bp)
+
+	t.Helper()
+	ctx := CreateTestContext()
+	ctx.Register(config)
+
+	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+	android.FailIfErrored(t, errs)
+	_, errs = ctx.PrepareBuildActions(config)
+	android.FailIfErrored(t, errs)
+
+	return ctx
+}
+
+func testRustError(t *testing.T, pattern string, bp string) {
+	// TODO (b/140435149)
+	if runtime.GOOS != "linux" {
+		t.Skip("Only the Linux toolchain is supported for Rust")
+	}
+
+	t.Helper()
+	config := testConfig(bp)
+
+	ctx := CreateTestContext()
+	ctx.Register(config)
+
+	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+	if len(errs) > 0 {
+		android.FailIfNoMatchingErrors(t, pattern, errs)
+		return
+	}
+
+	_, errs = ctx.PrepareBuildActions(config)
+	if len(errs) > 0 {
+		android.FailIfNoMatchingErrors(t, pattern, errs)
+		return
+	}
+
+	t.Fatalf("missing expected error %q (0 errors are returned)", pattern)
+}
+
+// Test that we can extract the lib name from a lib path.
+func TestLibNameFromFilePath(t *testing.T) {
+	libBarPath := android.PathForTesting("out/soong/.intermediates/external/libbar/libbar/linux_glibc_x86_64_shared/libbar.so")
+	libLibPath := android.PathForTesting("out/soong/.intermediates/external/libbar/libbar/linux_glibc_x86_64_shared/liblib.dylib.so")
+
+	libBarName := libNameFromFilePath(libBarPath)
+	libLibName := libNameFromFilePath(libLibPath)
+
+	expectedResult := "bar"
+	if libBarName != expectedResult {
+		t.Errorf("libNameFromFilePath returned the wrong name; expected '%#v', got '%#v'", expectedResult, libBarName)
+	}
+
+	expectedResult = "lib.dylib"
+	if libLibName != expectedResult {
+		t.Errorf("libNameFromFilePath returned the wrong name; expected '%#v', got '%#v'", expectedResult, libLibPath)
+	}
+}
+
+// Test that we can extract the link path from a lib path.
+func TestLinkPathFromFilePath(t *testing.T) {
+	barPath := android.PathForTesting("out/soong/.intermediates/external/libbar/libbar/linux_glibc_x86_64_shared/libbar.so")
+	libName := linkPathFromFilePath(barPath)
+	expectedResult := "out/soong/.intermediates/external/libbar/libbar/linux_glibc_x86_64_shared/"
+
+	if libName != expectedResult {
+		t.Errorf("libNameFromFilePath returned the wrong name; expected '%#v', got '%#v'", expectedResult, libName)
+	}
+}
+
+// Test to make sure dependencies are being picked up correctly.
+func TestDepsTracking(t *testing.T) {
+	ctx := testRust(t, `
+		rust_library_host_static {
+			name: "libstatic",
+			srcs: ["foo.rs"],
+			crate_name: "static",
+		}
+		rust_library_host_shared {
+			name: "libshared",
+			srcs: ["foo.rs"],
+			crate_name: "shared",
+		}
+		rust_library_host_dylib {
+			name: "libdylib",
+			srcs: ["foo.rs"],
+			crate_name: "dylib",
+		}
+		rust_library_host_rlib {
+			name: "librlib",
+			srcs: ["foo.rs"],
+			crate_name: "rlib",
+		}
+		rust_proc_macro {
+			name: "libpm",
+			srcs: ["foo.rs"],
+			crate_name: "pm",
+		}
+		rust_binary_host {
+			name: "fizz-buzz",
+			dylibs: ["libdylib"],
+			rlibs: ["librlib"],
+			proc_macros: ["libpm"],
+			static_libs: ["libstatic"],
+			shared_libs: ["libshared"],
+			srcs: ["foo.rs"],
+		}
+	`)
+	module := ctx.ModuleForTests("fizz-buzz", "linux_glibc_x86_64").Module().(*Module)
+
+	// Since dependencies are added to AndroidMk* properties, we can check these to see if they've been picked up.
+	if !android.InList("libdylib", module.Properties.AndroidMkDylibs) {
+		t.Errorf("Dylib dependency not detected (dependency missing from AndroidMkDylibs)")
+	}
+
+	if !android.InList("librlib", module.Properties.AndroidMkRlibs) {
+		t.Errorf("Rlib dependency not detected (dependency missing from AndroidMkRlibs)")
+	}
+
+	if !android.InList("libpm", module.Properties.AndroidMkProcMacroLibs) {
+		t.Errorf("Proc_macro dependency not detected (dependency missing from AndroidMkProcMacroLibs)")
+	}
+
+	if !android.InList("libshared", module.Properties.AndroidMkSharedLibs) {
+		t.Errorf("Shared library dependency not detected (dependency missing from AndroidMkSharedLibs)")
+	}
+
+	if !android.InList("libstatic", module.Properties.AndroidMkStaticLibs) {
+		t.Errorf("Static library dependency not detected (dependency missing from AndroidMkStaticLibs)")
+	}
+}
+
+// Test to make sure proc_macros use host variants when building device modules.
+func TestProcMacroDeviceDeps(t *testing.T) {
+	ctx := testRust(t, `
+		rust_library_host_rlib {
+			name: "libbar",
+			srcs: ["foo.rs"],
+			crate_name: "bar",
+		}
+		// Make a dummy libstd to let resolution go through
+		rust_library_dylib {
+			name: "libstd",
+			crate_name: "std",
+			srcs: ["foo.rs"],
+			no_stdlibs: true,
+		}
+		rust_library_dylib {
+			name: "libterm",
+			crate_name: "term",
+			srcs: ["foo.rs"],
+			no_stdlibs: true,
+		}
+		rust_library_dylib {
+			name: "libtest",
+			crate_name: "test",
+			srcs: ["foo.rs"],
+			no_stdlibs: true,
+		}
+		rust_proc_macro {
+			name: "libpm",
+			rlibs: ["libbar"],
+			srcs: ["foo.rs"],
+			crate_name: "pm",
+		}
+		rust_binary {
+			name: "fizz-buzz",
+			proc_macros: ["libpm"],
+			srcs: ["foo.rs"],
+		}
+	`)
+	rustc := ctx.ModuleForTests("libpm", "linux_glibc_x86_64").Rule("rustc")
+
+	if !strings.Contains(rustc.Args["libFlags"], "libbar/linux_glibc_x86_64") {
+		t.Errorf("Proc_macro is not using host variant of dependent modules.")
+	}
+}
+
+// Test that no_stdlibs suppresses dependencies on rust standard libraries
+func TestNoStdlibs(t *testing.T) {
+	ctx := testRust(t, `
+		rust_binary {
+			name: "fizz-buzz",
+			srcs: ["foo.rs"],
+                        no_stdlibs: true,
+		}`)
+	module := ctx.ModuleForTests("fizz-buzz", "android_arm64_armv8-a").Module().(*Module)
+
+	if android.InList("libstd", module.Properties.AndroidMkDylibs) {
+		t.Errorf("no_stdlibs did not suppress dependency on libstd")
+	}
+}
diff --git a/rust/test.go b/rust/test.go
new file mode 100644
index 0000000..04f844c
--- /dev/null
+++ b/rust/test.go
@@ -0,0 +1,185 @@
+// Copyright 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rust
+
+import (
+	"path/filepath"
+	"strings"
+
+	"android/soong/android"
+	"android/soong/tradefed"
+)
+
+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"`
+
+	// 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"`
+
+	// list of compatibility suites (for example "cts", "vts") that the module should be
+	// installed into.
+	Test_suites []string `android:"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
+}
+
+// A test module is a binary module with extra --test compiler flag
+// and different default installation directory.
+// In golang, inheriance is written as a component.
+type testDecorator struct {
+	*binaryDecorator
+	Properties TestProperties
+	testConfig android.Path
+}
+
+func NewRustTest(hod android.HostOrDeviceSupported) (*Module, *testDecorator) {
+	module := newModule(hod, android.MultilibFirst)
+
+	test := &testDecorator{
+		binaryDecorator: &binaryDecorator{
+			baseCompiler: NewBaseCompiler("nativetest", "nativetest64", InstallInData),
+		},
+	}
+
+	module.compiler = test
+
+	return module, test
+}
+
+func (test *testDecorator) compilerProps() []interface{} {
+	return append(test.binaryDecorator.compilerProps(), &test.Properties)
+}
+
+func (test *testDecorator) getMutatedModuleSubName(moduleName string) string {
+	stem := String(test.baseCompiler.Properties.Stem)
+	if stem != "" && !strings.HasSuffix(moduleName, "_"+stem) {
+		// Avoid repeated suffix in the module name.
+		return "_" + stem
+	}
+	return ""
+}
+
+func (test *testDecorator) install(ctx ModuleContext, file android.Path) {
+	name := ctx.ModuleName()
+	path := test.baseCompiler.relativeInstallPath()
+	// on device, use mutated module name
+	name = name + test.getMutatedModuleSubName(name)
+	if !ctx.Device() { // on host, use mutated module name + arch type + stem name
+		stem := String(test.baseCompiler.Properties.Stem)
+		if stem == "" {
+			stem = name
+		}
+		name = filepath.Join(name, ctx.Arch().ArchType.String(), stem)
+	}
+	test.testConfig = tradefed.AutoGenRustTestConfig(ctx, name,
+		test.Properties.Test_config,
+		test.Properties.Test_config_template,
+		test.Properties.Test_suites,
+		test.Properties.Auto_gen_config)
+	// default relative install path is module name
+	if path == "" {
+		test.baseCompiler.relative = ctx.ModuleName()
+	}
+	test.binaryDecorator.install(ctx, file)
+}
+
+func (test *testDecorator) compilerFlags(ctx ModuleContext, flags Flags) Flags {
+	flags = test.binaryDecorator.compilerFlags(ctx, flags)
+	flags.RustFlags = append(flags.RustFlags, "--test")
+	return flags
+}
+
+func init() {
+	// Rust tests are binary files built with --test.
+	android.RegisterModuleType("rust_test", RustTestFactory)
+	android.RegisterModuleType("rust_test_host", RustTestHostFactory)
+}
+
+func RustTestFactory() android.Module {
+	module, _ := NewRustTest(android.HostAndDeviceSupported)
+	return module.Init()
+}
+
+func RustTestHostFactory() android.Module {
+	module, _ := NewRustTest(android.HostSupported)
+	return module.Init()
+}
+
+func (test *testDecorator) testPerSrc() bool {
+	return true
+}
+
+func (test *testDecorator) srcs() []string {
+	return test.binaryDecorator.Properties.Srcs
+}
+
+func (test *testDecorator) setSrc(name, src string) {
+	test.binaryDecorator.Properties.Srcs = []string{src}
+	test.baseCompiler.Properties.Stem = StringPtr(name)
+}
+
+func (test *testDecorator) unsetSrc() {
+	test.binaryDecorator.Properties.Srcs = nil
+	test.baseCompiler.Properties.Stem = StringPtr("")
+}
+
+type testPerSrc interface {
+	testPerSrc() bool
+	srcs() []string
+	setSrc(string, string)
+	unsetSrc()
+}
+
+var _ testPerSrc = (*testDecorator)(nil)
+
+func TestPerSrcMutator(mctx android.BottomUpMutatorContext) {
+	if m, ok := mctx.Module().(*Module); ok {
+		if test, ok := m.compiler.(testPerSrc); ok {
+			numTests := len(test.srcs())
+			if test.testPerSrc() && numTests > 0 {
+				if duplicate, found := android.CheckDuplicate(test.srcs()); found {
+					mctx.PropertyErrorf("srcs", "found a duplicate entry %q", duplicate)
+					return
+				}
+				// Rust compiler always compiles one source file at a time and
+				// uses the crate name as output file name.
+				// Cargo uses the test source file name as default crate name,
+				// but that can be redefined.
+				// So when there are multiple source files, the source file names will
+				// be the output file names, but when there is only one test file,
+				// use the crate name.
+				testNames := make([]string, numTests)
+				for i, src := range test.srcs() {
+					testNames[i] = strings.TrimSuffix(filepath.Base(src), filepath.Ext(src))
+				}
+				crateName := m.compiler.crateName()
+				if numTests == 1 && crateName != "" {
+					testNames[0] = crateName
+				}
+				// TODO(chh): Add an "all tests" variation like cc/test.go?
+				tests := mctx.CreateLocalVariations(testNames...)
+				for i, src := range test.srcs() {
+					tests[i].(*Module).compiler.(testPerSrc).setSrc(testNames[i], src)
+				}
+			}
+		}
+	}
+}
diff --git a/rust/test_test.go b/rust/test_test.go
new file mode 100644
index 0000000..f131c6e
--- /dev/null
+++ b/rust/test_test.go
@@ -0,0 +1,63 @@
+// Copyright 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rust
+
+import (
+	"strings"
+	"testing"
+)
+
+// Check if rust_test_host accepts multiple source files and applies --test flag.
+func TestRustTest(t *testing.T) {
+	ctx := testRust(t, `
+		rust_test_host {
+			name: "my_test",
+			srcs: ["foo.rs", "src/bar.rs"],
+			crate_name: "new_test", // not used for multiple source files
+			relative_install_path: "rust/my-test",
+		}`)
+
+	for _, name := range []string{"foo", "bar"} {
+		testingModule := ctx.ModuleForTests("my_test", "linux_glibc_x86_64_"+name)
+		testingBuildParams := testingModule.Output(name)
+		rustcFlags := testingBuildParams.Args["rustcFlags"]
+		if !strings.Contains(rustcFlags, "--test") {
+			t.Errorf("%v missing --test flag, rustcFlags: %#v", name, rustcFlags)
+		}
+		outPath := "/my_test/linux_glibc_x86_64_" + name + "/" + name
+		if !strings.Contains(testingBuildParams.Output.String(), outPath) {
+			t.Errorf("wrong output: %v  expect: %v", testingBuildParams.Output, outPath)
+		}
+	}
+}
+
+// crate_name is output file name, when there is only one source file.
+func TestRustTestSingleFile(t *testing.T) {
+	ctx := testRust(t, `
+		rust_test_host {
+			name: "my-test",
+			srcs: ["foo.rs"],
+			crate_name: "new_test",
+			relative_install_path: "my-pkg",
+		}`)
+
+	name := "new_test"
+	testingModule := ctx.ModuleForTests("my-test", "linux_glibc_x86_64_"+name)
+	outPath := "/my-test/linux_glibc_x86_64_" + name + "/" + name
+	testingBuildParams := testingModule.Output(name)
+	if !strings.Contains(testingBuildParams.Output.String(), outPath) {
+		t.Errorf("wrong output: %v  expect: %v", testingBuildParams.Output, outPath)
+	}
+}
diff --git a/rust/testing.go b/rust/testing.go
new file mode 100644
index 0000000..f9adec8
--- /dev/null
+++ b/rust/testing.go
@@ -0,0 +1,114 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rust
+
+import (
+	"android/soong/android"
+	"android/soong/cc"
+)
+
+func GatherRequiredDepsForTest() string {
+	bp := `
+		rust_prebuilt_dylib {
+				name: "libarena_x86_64-unknown-linux-gnu",
+				srcs: [""],
+				host_supported: true,
+		}
+		rust_prebuilt_dylib {
+				name: "libfmt_macros_x86_64-unknown-linux-gnu",
+				srcs: [""],
+				host_supported: true,
+		}
+		rust_prebuilt_dylib {
+				name: "libgraphviz_x86_64-unknown-linux-gnu",
+				srcs: [""],
+				host_supported: true,
+		}
+		rust_prebuilt_dylib {
+				name: "libserialize_x86_64-unknown-linux-gnu",
+				srcs: [""],
+				host_supported: true,
+		}
+		rust_prebuilt_dylib {
+				name: "libstd_x86_64-unknown-linux-gnu",
+				srcs: [""],
+				host_supported: true,
+		}
+		rust_prebuilt_dylib {
+				name: "libsyntax_x86_64-unknown-linux-gnu",
+				srcs: [""],
+				host_supported: true,
+		}
+		rust_prebuilt_dylib {
+				name: "libsyntax_ext_x86_64-unknown-linux-gnu",
+				srcs: [""],
+				host_supported: true,
+		}
+		rust_prebuilt_dylib {
+				name: "libsyntax_pos_x86_64-unknown-linux-gnu",
+				srcs: [""],
+				host_supported: true,
+		}
+		rust_prebuilt_dylib {
+				name: "libterm_x86_64-unknown-linux-gnu",
+				srcs: [""],
+				host_supported: true,
+		}
+		rust_prebuilt_dylib {
+				name: "libtest_x86_64-unknown-linux-gnu",
+				srcs: [""],
+				host_supported: true,
+		}
+
+		//////////////////////////////
+		// Device module requirements
+
+		cc_library {
+			name: "liblog",
+			no_libcrt: true,
+			nocrt: true,
+			system_shared_libs: [],
+		}
+` + cc.GatherRequiredDepsForTest(android.NoOsType)
+	return bp
+}
+
+func CreateTestContext() *android.TestContext {
+	ctx := android.NewTestArchContext()
+	cc.RegisterRequiredBuildComponentsForTest(ctx)
+	ctx.RegisterModuleType("rust_binary", RustBinaryFactory)
+	ctx.RegisterModuleType("rust_binary_host", RustBinaryHostFactory)
+	ctx.RegisterModuleType("rust_test", RustTestFactory)
+	ctx.RegisterModuleType("rust_test_host", RustTestHostFactory)
+	ctx.RegisterModuleType("rust_library", RustLibraryFactory)
+	ctx.RegisterModuleType("rust_library_host", RustLibraryHostFactory)
+	ctx.RegisterModuleType("rust_library_host_rlib", RustLibraryRlibHostFactory)
+	ctx.RegisterModuleType("rust_library_host_dylib", RustLibraryDylibHostFactory)
+	ctx.RegisterModuleType("rust_library_rlib", RustLibraryRlibFactory)
+	ctx.RegisterModuleType("rust_library_dylib", RustLibraryDylibFactory)
+	ctx.RegisterModuleType("rust_library_shared", RustLibrarySharedFactory)
+	ctx.RegisterModuleType("rust_library_static", RustLibraryStaticFactory)
+	ctx.RegisterModuleType("rust_library_host_shared", RustLibrarySharedHostFactory)
+	ctx.RegisterModuleType("rust_library_host_static", RustLibraryStaticHostFactory)
+	ctx.RegisterModuleType("rust_proc_macro", ProcMacroFactory)
+	ctx.RegisterModuleType("rust_prebuilt_dylib", PrebuiltDylibFactory)
+	ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
+		// rust mutators
+		ctx.BottomUp("rust_libraries", LibraryMutator).Parallel()
+		ctx.BottomUp("rust_unit_tests", TestPerSrcMutator).Parallel()
+	})
+
+	return ctx
+}
diff --git a/scripts/Android.bp b/scripts/Android.bp
index 31f5922..4aaff9a 100644
--- a/scripts/Android.bp
+++ b/scripts/Android.bp
@@ -69,3 +69,55 @@
     },
     test_suites: ["general-tests"],
 }
+
+python_binary_host {
+    name: "jsonmodify",
+    main: "jsonmodify.py",
+    srcs: [
+        "jsonmodify.py",
+    ],
+    version: {
+        py2: {
+            enabled: true,
+        },
+        py3: {
+            enabled: false,
+        },
+    }
+}
+
+python_binary_host {
+    name: "test_config_fixer",
+    main: "test_config_fixer.py",
+    srcs: [
+        "test_config_fixer.py",
+        "manifest.py",
+    ],
+    version: {
+        py2: {
+            enabled: true,
+        },
+        py3: {
+            enabled: false,
+        },
+    },
+}
+
+python_test_host {
+    name: "test_config_fixer_test",
+    main: "test_config_fixer_test.py",
+    srcs: [
+        "test_config_fixer_test.py",
+        "test_config_fixer.py",
+        "manifest.py",
+    ],
+    version: {
+        py2: {
+            enabled: true,
+        },
+        py3: {
+            enabled: false,
+        },
+    },
+    test_suites: ["general-tests"],
+}
\ No newline at end of file
diff --git a/scripts/archive_repack.sh b/scripts/archive_repack.sh
new file mode 100755
index 0000000..f09372d
--- /dev/null
+++ b/scripts/archive_repack.sh
@@ -0,0 +1,87 @@
+#!/bin/bash -e
+
+# 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.
+
+# Script to extract and repack an archive with specified object files.
+# Inputs:
+#  Environment:
+#   CLANG_BIN: path to the clang bin directory
+#  Arguments:
+#   -i ${file}: input file
+#   -o ${file}: output file
+#   -d ${file}: deps file
+
+set -o pipefail
+
+OPTSTRING=d:i:o:
+
+usage() {
+    cat <<EOF
+Usage: archive_repack.sh [options] <objects to repack>
+
+OPTIONS:
+    -i <file>: input file
+    -o <file>: output file
+    -d <file>: deps file
+EOF
+    exit 1
+}
+
+while getopts $OPTSTRING opt; do
+    case "$opt" in
+        d) depsfile="${OPTARG}" ;;
+        i) infile="${OPTARG}" ;;
+        o) outfile="${OPTARG}" ;;
+        ?) usage ;;
+    esac
+done
+shift "$(($OPTIND -1))"
+
+if [ -z "${infile}" ]; then
+    echo "-i argument is required"
+    usage
+fi
+
+if [ -z "${outfile}" ]; then
+    echo "-o argument is required"
+    usage
+fi
+
+# Produce deps file
+if [ ! -z "${depsfile}" ]; then
+    cat <<EOF > "${depsfile}"
+${outfile}: ${infile} ${CLANG_BIN}/llvm-ar
+EOF
+fi
+
+# Get absolute path for outfile and llvm-ar.
+LLVM_AR="${PWD}/${CLANG_BIN}/llvm-ar"
+if [[ "$outfile" != /* ]]; then
+    outfile="${PWD}/${outfile}"
+fi
+
+tempdir="${outfile}.tmp"
+
+# Clean up any previous temporary files.
+rm -f "${outfile}"
+rm -rf "${tempdir}"
+
+# Do repack
+# We have to change working directory since ar only allows extracting to CWD.
+mkdir "${tempdir}"
+cp "${infile}" "${tempdir}/archive"
+cd "${tempdir}"
+"${LLVM_AR}" x "archive"
+"${LLVM_AR}" --format=gnu qc "${outfile}" "$@"
diff --git a/scripts/build-aml-prebuilts.sh b/scripts/build-aml-prebuilts.sh
new file mode 100755
index 0000000..7e3a82c
--- /dev/null
+++ b/scripts/build-aml-prebuilts.sh
@@ -0,0 +1,75 @@
+#!/bin/bash -e
+
+export OUT_DIR=${OUT_DIR:-out}
+
+if [ -e ${OUT_DIR}/soong/.soong.in_make ]; then
+  # If ${OUT_DIR} has been created without --skip-make, Soong will create an
+  # ${OUT_DIR}/soong/build.ninja that leaves out many targets which are
+  # expected to be supplied by the .mk files, and that might cause errors in
+  # "m --skip-make" below. We therefore default to a different out dir
+  # location in that case.
+  AML_OUT_DIR=out-aml
+  echo "Avoiding in-make OUT_DIR '${OUT_DIR}' - building in '${AML_OUT_DIR}' instead"
+  OUT_DIR=${AML_OUT_DIR}
+fi
+
+source build/envsetup.sh
+
+my_get_build_var() {
+  # get_build_var will run Soong in normal in-make mode where it creates
+  # .soong.in_make. That would clobber our real out directory, so we need to
+  # run it in a different one.
+  OUT_DIR=${OUT_DIR}/get_build_var get_build_var "$@"
+}
+
+PLATFORM_SDK_VERSION=$(my_get_build_var PLATFORM_SDK_VERSION)
+PLATFORM_VERSION=$(my_get_build_var PLATFORM_VERSION)
+PLATFORM_VERSION_ALL_CODENAMES=$(my_get_build_var PLATFORM_VERSION_ALL_CODENAMES)
+
+# PLATFORM_VERSION_ALL_CODENAMES is a comma separated list like O,P. We need to
+# turn this into ["O","P"].
+PLATFORM_VERSION_ALL_CODENAMES=${PLATFORM_VERSION_ALL_CODENAMES/,/'","'}
+PLATFORM_VERSION_ALL_CODENAMES="[\"${PLATFORM_VERSION_ALL_CODENAMES}\"]"
+
+# Logic from build/make/core/goma.mk
+if [ "${USE_GOMA}" = true ]; then
+  if [ -n "${GOMA_DIR}" ]; then
+    goma_dir="${GOMA_DIR}"
+  else
+    goma_dir="${HOME}/goma"
+  fi
+  GOMA_CC="${goma_dir}/gomacc"
+  export CC_WRAPPER="${CC_WRAPPER}${CC_WRAPPER:+ }${GOMA_CC}"
+  export CXX_WRAPPER="${CXX_WRAPPER}${CXX_WRAPPER:+ }${GOMA_CC}"
+  export JAVAC_WRAPPER="${JAVAC_WRAPPER}${JAVAC_WRAPPER:+ }${GOMA_CC}"
+else
+  USE_GOMA=false
+fi
+
+SOONG_OUT=${OUT_DIR}/soong
+mkdir -p ${SOONG_OUT}
+SOONG_VARS=${SOONG_OUT}/soong.variables
+
+cat > ${SOONG_VARS}.new << EOF
+{
+    "Platform_sdk_version": ${PLATFORM_SDK_VERSION},
+    "Platform_sdk_codename": "${PLATFORM_VERSION}",
+    "Platform_version_active_codenames": ${PLATFORM_VERSION_ALL_CODENAMES},
+
+    "DeviceName": "generic_arm64",
+    "HostArch": "x86_64",
+    "Aml_abis": true,
+
+    "UseGoma": ${USE_GOMA}
+}
+EOF
+
+if [ -f ${SOONG_VARS} ] && cmp -s ${SOONG_VARS} ${SOONG_VARS}.new; then
+  # Don't touch soong.variables if we don't have to, to avoid Soong rebuilding
+  # the ninja file when it isn't necessary.
+  rm ${SOONG_VARS}.new
+else
+  mv ${SOONG_VARS}.new ${SOONG_VARS}
+fi
+
+m --skip-make "$@"
diff --git a/scripts/build-ndk-prebuilts.sh b/scripts/build-ndk-prebuilts.sh
index 947458a..b6ed659 100755
--- a/scripts/build-ndk-prebuilts.sh
+++ b/scripts/build-ndk-prebuilts.sh
@@ -25,7 +25,7 @@
 PLATFORM_SDK_VERSION=$(get_build_var PLATFORM_SDK_VERSION)
 PLATFORM_VERSION_ALL_CODENAMES=$(get_build_var PLATFORM_VERSION_ALL_CODENAMES)
 
-# PLATFORM_VERSION_ALL_CODESNAMES is a comma separated list like O,P. We need to
+# PLATFORM_VERSION_ALL_CODENAMES is a comma separated list like O,P. We need to
 # turn this into ["O","P"].
 PLATFORM_VERSION_ALL_CODENAMES=${PLATFORM_VERSION_ALL_CODENAMES/,/'","'}
 PLATFORM_VERSION_ALL_CODENAMES="[\"${PLATFORM_VERSION_ALL_CODENAMES}\"]"
diff --git a/scripts/freeze-sysprop-api-files.sh b/scripts/freeze-sysprop-api-files.sh
new file mode 100755
index 0000000..1b2ff7c
--- /dev/null
+++ b/scripts/freeze-sysprop-api-files.sh
@@ -0,0 +1,39 @@
+#!/bin/bash -e
+
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This script freezes APIs of a sysprop_library after checking compatibility
+# between latest API and current API.
+#
+# Usage: freeze-sysprop-api-files.sh <modulePath> <moduleName>
+#
+# <modulePath>: the directory, either relative or absolute, which holds the
+# Android.bp file defining sysprop_library.
+#
+# <moduleName>: the name of sysprop_library to freeze API.
+#
+# Example:
+# $ . build/envsetup.sh && lunch aosp_arm64-user
+# $ . build/soong/scripts/freeze-sysprop-api-files.sh \
+#       system/libsysprop/srcs PlatformProperties
+
+if [[ -z "$1" || -z "$2" ]]; then
+  echo "usage: $0 <modulePath> <moduleName>" >&2
+  exit 1
+fi
+
+api_dir=$1/api
+
+m "$2-check-api" && cp -f "${api_dir}/$2-current.txt" "${api_dir}/$2-latest.txt"
diff --git a/scripts/gen-java-current-api-files.sh b/scripts/gen-java-current-api-files.sh
index 517d391..547387a 100755
--- a/scripts/gen-java-current-api-files.sh
+++ b/scripts/gen-java-current-api-files.sh
@@ -15,15 +15,16 @@
 # limitations under the License.
 
 if [[ -z "$1" ]]; then
-  echo "usage: $0 <modulePath>" >&2
+  echo "usage: $0 <modulePath> scopes..." >&2
   exit 1
 fi
 
-api_dir=$1/api
+api_dir=$1
+shift
 
 mkdir -p "$api_dir"
 
-scopes=("" system- test-)
+scopes=("" "$@")
 apis=(current removed)
 
 for scope in "${scopes[@]}"; do
@@ -31,3 +32,4 @@
     touch "${api_dir}/${scope}${api}.txt"
   done
 done
+
diff --git a/scripts/gen-sysprop-api-files.sh b/scripts/gen-sysprop-api-files.sh
new file mode 100755
index 0000000..a4cb506
--- /dev/null
+++ b/scripts/gen-sysprop-api-files.sh
@@ -0,0 +1,26 @@
+#!/bin/bash -e
+
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+if [[ -z "$1" || -z "$2" ]]; then
+  echo "usage: $0 <modulePath> <moduleName>" >&2
+  exit 1
+fi
+
+api_dir=$1/api
+
+mkdir -p "$api_dir"
+touch "${api_dir}/$2-current.txt"
+touch "${api_dir}/$2-latest.txt"
diff --git a/scripts/gen_sorted_bss_symbols.sh b/scripts/gen_sorted_bss_symbols.sh
new file mode 100755
index 0000000..244ed0d
--- /dev/null
+++ b/scripts/gen_sorted_bss_symbols.sh
@@ -0,0 +1,28 @@
+#!/bin/bash -e
+
+# 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.
+
+# Script to generate a symbol ordering file that sorts bss section symbols by
+# their sizes.
+# Inputs:
+#  Environment:
+#   CROSS_COMPILE: prefix added to nm tools
+#  Arguments:
+#   $1: Input ELF file
+#   $2: Output symbol ordering file
+
+set -o pipefail
+
+${CROSS_COMPILE}nm --size-sort $1 | awk '{if ($2 == "b" || $2 == "B") print $3}' > $2
diff --git a/scripts/jar-wrapper.sh b/scripts/jar-wrapper.sh
index 71c1d90..b468041 100644
--- a/scripts/jar-wrapper.sh
+++ b/scripts/jar-wrapper.sh
@@ -48,11 +48,11 @@
     exit 1
 fi
 
-javaOpts=""
+declare -a javaOpts=()
 while expr "x$1" : 'x-J' >/dev/null; do
-    opt=`expr "$1" : '-J\(.*\)'`
-    javaOpts="${javaOpts} -${opt}"
+    opt=`expr "$1" : '-J-\{0,1\}\(.*\)'`
+    javaOpts+=("-${opt}")
     shift
 done
 
-exec java ${javaOpts} -jar ${jardir}/${jarfile} "$@"
+exec java "${javaOpts[@]}" -jar ${jardir}/${jarfile} "$@"
diff --git a/scripts/jsonmodify.py b/scripts/jsonmodify.py
new file mode 100755
index 0000000..4b2c3c2
--- /dev/null
+++ b/scripts/jsonmodify.py
@@ -0,0 +1,121 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+import argparse
+import collections
+import json
+import sys
+
+def follow_path(obj, path):
+  cur = obj
+  last_key = None
+  for key in path.split('.'):
+    if last_key:
+      if last_key not in cur:
+        return None,None
+      cur = cur[last_key]
+    last_key = key
+  if last_key not in cur:
+    return None,None
+  return cur, last_key
+
+
+def ensure_path(obj, path):
+  cur = obj
+  last_key = None
+  for key in path.split('.'):
+    if last_key:
+      if last_key not in cur:
+        cur[last_key] = dict()
+      cur = cur[last_key]
+    last_key = key
+  return cur, last_key
+
+
+class SetValue(str):
+  def apply(self, obj, val):
+    cur, key = ensure_path(obj, self)
+    cur[key] = val
+
+
+class Replace(str):
+  def apply(self, obj, val):
+    cur, key = follow_path(obj, self)
+    if cur:
+      cur[key] = val
+
+
+class Remove(str):
+  def apply(self, obj):
+    cur, key = follow_path(obj, self)
+    if cur:
+      del cur[key]
+
+
+class AppendList(str):
+  def apply(self, obj, *args):
+    cur, key = ensure_path(obj, self)
+    if key not in cur:
+      cur[key] = list()
+    if not isinstance(cur[key], list):
+      raise ValueError(self + " should be a array.")
+    cur[key].extend(args)
+
+
+def main():
+  parser = argparse.ArgumentParser()
+  parser.add_argument('-o', '--out',
+                      help='write result to a file. If omitted, print to stdout',
+                      metavar='output',
+                      action='store')
+  parser.add_argument('input', nargs='?', help='JSON file')
+  parser.add_argument("-v", "--value", type=SetValue,
+                      help='set value of the key specified by path. If path doesn\'t exist, creates new one.',
+                      metavar=('path', 'value'),
+                      nargs=2, dest='patch', default=[], action='append')
+  parser.add_argument("-s", "--replace", type=Replace,
+                      help='replace value of the key specified by path. If path doesn\'t exist, no op.',
+                      metavar=('path', 'value'),
+                      nargs=2, dest='patch', action='append')
+  parser.add_argument("-r", "--remove", type=Remove,
+                      help='remove the key specified by path. If path doesn\'t exist, no op.',
+                      metavar='path',
+                      nargs=1, dest='patch', action='append')
+  parser.add_argument("-a", "--append_list", type=AppendList,
+                      help='append values to the list specified by path. If path doesn\'t exist, creates new list for it.',
+                      metavar=('path', 'value'),
+                      nargs='+', dest='patch', default=[], action='append')
+  args = parser.parse_args()
+
+  if args.input:
+    with open(args.input) as f:
+      obj = json.load(f, object_pairs_hook=collections.OrderedDict)
+  else:
+    obj = json.load(sys.stdin, object_pairs_hook=collections.OrderedDict)
+
+  for p in args.patch:
+    p[0].apply(obj, *p[1:])
+
+  if args.out:
+    with open(args.out, "w") as f:
+      json.dump(obj, f, indent=2)
+  else:
+    print(json.dumps(obj, indent=2))
+
+
+if __name__ == '__main__':
+  main()
diff --git a/scripts/manifest.py b/scripts/manifest.py
index 4c75f8b..04f7405 100755
--- a/scripts/manifest.py
+++ b/scripts/manifest.py
@@ -14,7 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
-"""A tool for inserting values from the build system into a manifest."""
+"""A tool for inserting values from the build system into a manifest or a test config."""
 
 from __future__ import print_function
 from xml.dom import minidom
@@ -65,6 +65,15 @@
                        ns.value)
 
 
+def parse_test_config(doc):
+  """ Get the configuration element. """
+
+  test_config = doc.documentElement
+  if test_config.tagName != 'configuration':
+    raise RuntimeError('expected configuration tag at root')
+  return test_config
+
+
 def as_int(s):
   try:
     i = int(s)
diff --git a/scripts/strip.sh b/scripts/strip.sh
index bd62619..40f0184 100755
--- a/scripts/strip.sh
+++ b/scripts/strip.sh
@@ -29,7 +29,6 @@
 #   --keep-mini-debug-info
 #   --keep-symbols
 #   --keep-symbols-and-debug-frame
-#   --use-gnu-strip
 #   --remove-build-id
 
 set -o pipefail
@@ -44,86 +43,55 @@
         --keep-mini-debug-info          Keep compressed debug info in out-file
         --keep-symbols                  Keep symbols in out-file
         --keep-symbols-and-debug-frame  Keep symbols and .debug_frame in out-file
-        --use-gnu-strip                 Use strip/objcopy instead of llvm-{strip,objcopy}
         --remove-build-id               Remove the gnu build-id section in out-file
 EOF
     exit 1
 }
 
-# Without --use-gnu-strip, GNU strip is replaced with llvm-strip to work around
-# old GNU strip bug on lld output files, b/80093681.
-# Similary, calls to objcopy are replaced with llvm-objcopy,
-# with some exceptions.
-
 do_strip() {
-    # ${CROSS_COMPILE}strip --strip-all does not strip .ARM.attributes,
+    # GNU strip --strip-all does not strip .ARM.attributes,
     # so we tell llvm-strip to keep it too.
-    if [ -z "${use_gnu_strip}" ]; then
-        "${CLANG_BIN}/llvm-strip" --strip-all -keep-section=.ARM.attributes "${infile}" -o "${outfile}.tmp"
-    else
-        "${CROSS_COMPILE}strip" --strip-all "${infile}" -o "${outfile}.tmp"
-    fi
+    "${CLANG_BIN}/llvm-strip" --strip-all --keep-section=.ARM.attributes "${infile}" -o "${outfile}.tmp"
 }
 
 do_strip_keep_symbols_and_debug_frame() {
-    REMOVE_SECTIONS=`"${CROSS_COMPILE}readelf" -S "${infile}" | awk '/.debug_/ {if ($2 != ".debug_frame") {print "--remove-section " $2}}' | xargs`
-    if [ -z "${use_gnu_strip}" ]; then
-        "${CLANG_BIN}/llvm-objcopy" "${infile}" "${outfile}.tmp" ${REMOVE_SECTIONS}
-    else
-        "${CROSS_COMPILE}objcopy" "${infile}" "${outfile}.tmp" ${REMOVE_SECTIONS}
-    fi
+    REMOVE_SECTIONS=`"${CLANG_BIN}/llvm-readelf" -S "${infile}" | awk '/.debug_/ {if ($2 != ".debug_frame") {print "--remove-section " $2}}' | xargs`
+    "${CLANG_BIN}/llvm-objcopy" "${infile}" "${outfile}.tmp" ${REMOVE_SECTIONS}
 }
 
 do_strip_keep_symbols() {
-    REMOVE_SECTIONS=`"${CROSS_COMPILE}readelf" -S "${infile}" | awk '/.debug_/ {print "--remove-section " $2}' | xargs`
-    if [ -z "${use_gnu_strip}" ]; then
-        "${CLANG_BIN}/llvm-objcopy" "${infile}" "${outfile}.tmp" ${REMOVE_SECTIONS}
-    else
-        "${CROSS_COMPILE}objcopy" "${infile}" "${outfile}.tmp" ${REMOVE_SECTIONS}
-    fi
+    REMOVE_SECTIONS=`"${CLANG_BIN}/llvm-readelf" -S "${infile}" | awk '/.debug_/ {print "--remove-section " $2}' | xargs`
+    "${CLANG_BIN}/llvm-objcopy" "${infile}" "${outfile}.tmp" ${REMOVE_SECTIONS}
 }
 
 do_strip_keep_symbol_list() {
-    if [ -z "${use_gnu_strip}" ]; then
-        echo "do_strip_keep_symbol_list does not work with llvm-objcopy"
-        echo "http://b/131631155"
-        usage
-    fi
-
     echo "${symbols_to_keep}" | tr ',' '\n' > "${outfile}.symbolList"
-    KEEP_SYMBOLS="-w --strip-unneeded-symbol=* --keep-symbols="
-    KEEP_SYMBOLS+="${outfile}.symbolList"
 
-    "${CROSS_COMPILE}objcopy" "${infile}" "${outfile}.tmp" ${KEEP_SYMBOLS}
+    KEEP_SYMBOLS="--strip-unneeded-symbol=* --keep-symbols="
+    KEEP_SYMBOLS+="${outfile}.symbolList"
+    "${CROSS_COMPILE}objcopy" -w "${infile}" "${outfile}.tmp" ${KEEP_SYMBOLS}
 }
 
 do_strip_keep_mini_debug_info() {
     rm -f "${outfile}.dynsyms" "${outfile}.funcsyms" "${outfile}.keep_symbols" "${outfile}.debug" "${outfile}.mini_debuginfo" "${outfile}.mini_debuginfo.xz"
     local fail=
-    if [ -z "${use_gnu_strip}" ]; then
-        "${CLANG_BIN}/llvm-strip" --strip-all -keep-section=.ARM.attributes -remove-section=.comment "${infile}" -o "${outfile}.tmp" || fail=true
-    else
-        "${CROSS_COMPILE}strip" --strip-all -R .comment "${infile}" -o "${outfile}.tmp" || fail=true
-    fi
+    "${CLANG_BIN}/llvm-strip" --strip-all --keep-section=.ARM.attributes --remove-section=.comment "${infile}" -o "${outfile}.tmp" || fail=true
+
     if [ -z $fail ]; then
-        # Current prebult llvm-objcopy does not support the following flags:
-        #    --only-keep-debug --rename-section --keep-symbols
-        # For the following use cases, ${CROSS_COMPILE}objcopy does fine with lld linked files,
-        # except the --add-section flag.
+        # Current prebult llvm-objcopy does not support --only-keep-debug flag,
+        # and cannot process object files that are produced with the flag. Use
+        # GNU objcopy instead for now. (b/141010852)
         "${CROSS_COMPILE}objcopy" --only-keep-debug "${infile}" "${outfile}.debug"
-        "${CROSS_COMPILE}nm" -D "${infile}" --format=posix --defined-only 2> /dev/null | awk '{ print $1 }' | sort >"${outfile}.dynsyms"
-        "${CROSS_COMPILE}nm" "${infile}" --format=posix --defined-only | awk '{ if ($2 == "T" || $2 == "t" || $2 == "D") print $1 }' | sort > "${outfile}.funcsyms"
+        "${CLANG_BIN}/llvm-nm" -D "${infile}" --format=posix --defined-only 2> /dev/null | awk '{ print $1 }' | sort >"${outfile}.dynsyms"
+        "${CLANG_BIN}/llvm-nm" "${infile}" --format=posix --defined-only | awk '{ if ($2 == "T" || $2 == "t" || $2 == "D") print $1 }' | sort > "${outfile}.funcsyms"
         comm -13 "${outfile}.dynsyms" "${outfile}.funcsyms" > "${outfile}.keep_symbols"
         echo >> "${outfile}.keep_symbols" # Ensure that the keep_symbols file is not empty.
         "${CROSS_COMPILE}objcopy" --rename-section .debug_frame=saved_debug_frame "${outfile}.debug" "${outfile}.mini_debuginfo"
         "${CROSS_COMPILE}objcopy" -S --remove-section .gdb_index --remove-section .comment --keep-symbols="${outfile}.keep_symbols" "${outfile}.mini_debuginfo"
         "${CROSS_COMPILE}objcopy" --rename-section saved_debug_frame=.debug_frame "${outfile}.mini_debuginfo"
         "${XZ}" "${outfile}.mini_debuginfo"
-        if [ -z "${use_gnu_strip}" ]; then
-            "${CLANG_BIN}/llvm-objcopy" --add-section .gnu_debugdata="${outfile}.mini_debuginfo.xz" "${outfile}.tmp"
-        else
-            "${CROSS_COMPILE}objcopy" --add-section .gnu_debugdata="${outfile}.mini_debuginfo.xz" "${outfile}.tmp"
-        fi
+
+        "${CLANG_BIN}/llvm-objcopy" --add-section .gnu_debugdata="${outfile}.mini_debuginfo.xz" "${outfile}.tmp"
         rm -f "${outfile}.dynsyms" "${outfile}.funcsyms" "${outfile}.keep_symbols" "${outfile}.debug" "${outfile}.mini_debuginfo" "${outfile}.mini_debuginfo.xz"
     else
         cp -f "${infile}" "${outfile}.tmp"
@@ -131,19 +99,11 @@
 }
 
 do_add_gnu_debuglink() {
-    if [ -z "${use_gnu_strip}" ]; then
-        "${CLANG_BIN}/llvm-objcopy" --add-gnu-debuglink="${infile}" "${outfile}.tmp"
-    else
-        "${CROSS_COMPILE}objcopy" --add-gnu-debuglink="${infile}" "${outfile}.tmp"
-    fi
+    "${CLANG_BIN}/llvm-objcopy" --add-gnu-debuglink="${infile}" "${outfile}.tmp"
 }
 
 do_remove_build_id() {
-    if [ -z "${use_gnu_strip}" ]; then
-        "${CLANG_BIN}/llvm-strip" -remove-section=.note.gnu.build-id "${outfile}.tmp" -o "${outfile}.tmp.no-build-id"
-    else
-        "${CROSS_COMPILE}strip" --remove-section=.note.gnu.build-id "${outfile}.tmp" -o "${outfile}.tmp.no-build-id"
-    fi
+    "${CLANG_BIN}/llvm-strip" --remove-section=.note.gnu.build-id "${outfile}.tmp" -o "${outfile}.tmp.no-build-id"
     rm -f "${outfile}.tmp"
     mv "${outfile}.tmp.no-build-id" "${outfile}.tmp"
 }
@@ -161,7 +121,6 @@
                 keep-symbols) keep_symbols=true ;;
                 keep-symbols-and-debug-frame) keep_symbols_and_debug_frame=true ;;
                 remove-build-id) remove_build_id=true ;;
-                use-gnu-strip) use_gnu_strip=true ;;
                 *) echo "Unknown option --${OPTARG}"; usage ;;
             esac;;
         ?) usage ;;
@@ -234,18 +193,13 @@
 rm -f "${outfile}"
 mv "${outfile}.tmp" "${outfile}"
 
-if [ -z "${use_gnu_strip}" ]; then
-  USED_STRIP_OBJCOPY="${CLANG_BIN}/llvm-strip ${CLANG_BIN}/llvm-objcopy"
-else
-  USED_STRIP_OBJCOPY="${CROSS_COMPILE}strip"
-fi
-
 cat <<EOF > "${depsfile}"
 ${outfile}: \
   ${infile} \
-  ${CROSS_COMPILE}nm \
   ${CROSS_COMPILE}objcopy \
-  ${CROSS_COMPILE}readelf \
-  ${USED_STRIP_OBJCOPY}
+  ${CLANG_BIN}/llvm-nm \
+  ${CLANG_BIN}/llvm-objcopy \
+  ${CLANG_BIN}/llvm-readelf \
+  ${CLANG_BIN}/llvm-strip
 
 EOF
diff --git a/scripts/system-clang-format b/scripts/system-clang-format
index 14abd93..a7614d2 100644
--- a/scripts/system-clang-format
+++ b/scripts/system-clang-format
@@ -1,4 +1,5 @@
 BasedOnStyle: Google
+Standard: Cpp11
 AccessModifierOffset: -2
 AllowShortFunctionsOnASingleLine: Inline
 ColumnLimit: 100
diff --git a/scripts/system-clang-format-2 b/scripts/system-clang-format-2
index e28b379..a4e23f8 100644
--- a/scripts/system-clang-format-2
+++ b/scripts/system-clang-format-2
@@ -1,4 +1,5 @@
 BasedOnStyle: Google
+Standard: Cpp11
 AllowShortFunctionsOnASingleLine: Inline
 ColumnLimit: 100
 CommentPragmas: NOLINT:.*
diff --git a/scripts/test_config_fixer.py b/scripts/test_config_fixer.py
new file mode 100644
index 0000000..7bb4b52
--- /dev/null
+++ b/scripts/test_config_fixer.py
@@ -0,0 +1,82 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+"""A tool for modifying values in a test config."""
+
+from __future__ import print_function
+
+import argparse
+import sys
+from xml.dom import minidom
+
+
+from manifest import get_children_with_tag
+from manifest import parse_manifest
+from manifest import parse_test_config
+from manifest import write_xml
+
+
+def parse_args():
+  """Parse commandline arguments."""
+
+  parser = argparse.ArgumentParser()
+  parser.add_argument('--manifest', default='', dest='manifest',
+                      help=('AndroidManifest.xml that contains the original package name'))
+  parser.add_argument('--package-name', default='', dest='package_name',
+                      help=('overwrite package fields in the test config'))
+  parser.add_argument('input', help='input test config file')
+  parser.add_argument('output', help='output test config file')
+  return parser.parse_args()
+
+
+def overwrite_package_name(test_config_doc, manifest_doc, package_name):
+
+  manifest = parse_manifest(manifest_doc)
+  original_package = manifest.getAttribute('package')
+  print('package: ' + original_package)
+
+  test_config = parse_test_config(test_config_doc)
+  tests = get_children_with_tag(test_config, 'test')
+
+  for test in tests:
+    options = get_children_with_tag(test, 'option')
+    for option in options:
+      if option.getAttribute('name') == "package" and option.getAttribute('value') == original_package:
+        option.setAttribute('value', package_name)
+
+def main():
+  """Program entry point."""
+  try:
+    args = parse_args()
+
+    doc = minidom.parse(args.input)
+
+    if args.package_name:
+      if not args.manifest:
+        raise RuntimeError('--manifest flag required for --package-name')
+      manifest_doc = minidom.parse(args.manifest)
+      overwrite_package_name(doc, manifest_doc, args.package_name)
+
+    with open(args.output, 'wb') as f:
+      write_xml(f, doc)
+
+  # pylint: disable=broad-except
+  except Exception as err:
+    print('error: ' + str(err), file=sys.stderr)
+    sys.exit(-1)
+
+if __name__ == '__main__':
+  main()
diff --git a/scripts/test_config_fixer_test.py b/scripts/test_config_fixer_test.py
new file mode 100644
index 0000000..b90582e
--- /dev/null
+++ b/scripts/test_config_fixer_test.py
@@ -0,0 +1,71 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+"""Unit tests for test_config_fixer.py."""
+
+import StringIO
+import sys
+import unittest
+from xml.dom import minidom
+
+import test_config_fixer
+
+sys.dont_write_bytecode = True
+
+
+class OverwritePackageNameTest(unittest.TestCase):
+  """ Unit tests for overwrite_package_name function """
+
+  manifest = (
+      '<?xml version="1.0" encoding="utf-8"?>\n'
+      '<manifest xmlns:android="http://schemas.android.com/apk/res/android"\n'
+      '    package="com.android.foo">\n'
+      '    <application>\n'
+      '    </application>\n'
+      '</manifest>\n')
+
+  test_config = (
+      '<?xml version="1.0" encoding="utf-8"?>\n'
+      '<configuration description="Runs some tests.">\n'
+      '    <option name="test-suite-tag" value="apct"/>\n'
+      '    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">\n'
+      '        <option name="package" value="%s"/>\n'
+      '    </target_preparer>\n'
+      '    <test class="com.android.tradefed.testtype.AndroidJUnitTest">\n'
+      '        <option name="package" value="%s"/>\n'
+      '        <option name="runtime-hint" value="20s"/>\n'
+      '    </test>\n'
+      '    <test class="com.android.tradefed.testtype.AndroidJUnitTest">\n'
+      '        <option name="package" value="%s"/>\n'
+      '        <option name="runtime-hint" value="15s"/>\n'
+      '    </test>\n'
+      '</configuration>\n')
+
+  def test_all(self):
+    doc = minidom.parseString(self.test_config % ("com.android.foo", "com.android.foo", "com.android.bar"))
+    manifest = minidom.parseString(self.manifest)
+
+    test_config_fixer.overwrite_package_name(doc, manifest, "com.soong.foo")
+    output = StringIO.StringIO()
+    test_config_fixer.write_xml(output, doc)
+
+    # Only the matching package name in a test node should be updated.
+    expected = self.test_config % ("com.android.foo", "com.soong.foo", "com.android.bar")
+    self.assertEqual(expected, output.getvalue())
+
+
+if __name__ == '__main__':
+  unittest.main(verbosity=2)
diff --git a/sdk/bp.go b/sdk/bp.go
new file mode 100644
index 0000000..19fb70d
--- /dev/null
+++ b/sdk/bp.go
@@ -0,0 +1,141 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package sdk
+
+import (
+	"fmt"
+
+	"android/soong/android"
+)
+
+type bpPropertySet struct {
+	properties map[string]interface{}
+	order      []string
+}
+
+var _ android.BpPropertySet = (*bpPropertySet)(nil)
+
+func (s *bpPropertySet) init() {
+	s.properties = make(map[string]interface{})
+}
+
+func (s *bpPropertySet) AddProperty(name string, value interface{}) {
+	if s.properties[name] != nil {
+		panic("Property %q already exists in property set")
+	}
+
+	s.properties[name] = value
+	s.order = append(s.order, name)
+}
+
+func (s *bpPropertySet) AddPropertySet(name string) android.BpPropertySet {
+	set := &bpPropertySet{}
+	set.init()
+	s.AddProperty(name, set)
+	return set
+}
+
+func (s *bpPropertySet) getValue(name string) interface{} {
+	return s.properties[name]
+}
+
+func (s *bpPropertySet) copy() bpPropertySet {
+	propertiesCopy := make(map[string]interface{})
+	for p, v := range s.properties {
+		propertiesCopy[p] = v
+	}
+
+	return bpPropertySet{
+		properties: propertiesCopy,
+		order:      append([]string(nil), s.order...),
+	}
+}
+
+func (s *bpPropertySet) setProperty(name string, value interface{}) {
+	if s.properties[name] == nil {
+		s.AddProperty(name, value)
+	} else {
+		s.properties[name] = value
+	}
+}
+
+func (s *bpPropertySet) insertAfter(position string, name string, value interface{}) {
+	if s.properties[name] != nil {
+		panic("Property %q already exists in property set")
+	}
+
+	// Add the name to the end of the order, to ensure it has necessary capacity
+	// and to handle the case when the position does not exist.
+	s.order = append(s.order, name)
+
+	// Search through the order for the item that matches supplied position. If
+	// found then insert the name of the new property after it.
+	for i, v := range s.order {
+		if v == position {
+			// Copy the items after the one where the new property should be inserted.
+			copy(s.order[i+2:], s.order[i+1:])
+			// Insert the item in the list.
+			s.order[i+1] = name
+		}
+	}
+
+	s.properties[name] = value
+}
+
+type bpModule struct {
+	bpPropertySet
+	moduleType string
+}
+
+var _ android.BpModule = (*bpModule)(nil)
+
+func (m *bpModule) copy() *bpModule {
+	return &bpModule{
+		bpPropertySet: m.bpPropertySet.copy(),
+		moduleType:    m.moduleType,
+	}
+}
+
+// A .bp file
+type bpFile struct {
+	modules map[string]*bpModule
+	order   []*bpModule
+}
+
+// Add a module.
+//
+// The module must have had its "name" property set to a string value that
+// is unique within this file.
+func (f *bpFile) AddModule(module android.BpModule) {
+	m := module.(*bpModule)
+	if name, ok := m.getValue("name").(string); ok {
+		if f.modules[name] != nil {
+			panic(fmt.Sprintf("Module %q already exists in bp file", name))
+		}
+
+		f.modules[name] = m
+		f.order = append(f.order, m)
+	} else {
+		panic("Module does not have a name property, or it is not a string")
+	}
+}
+
+func (f *bpFile) newModule(moduleType string) *bpModule {
+	module := &bpModule{
+		moduleType: moduleType,
+	}
+	(&module.bpPropertySet).init()
+	return module
+}
diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go
new file mode 100644
index 0000000..255ac08
--- /dev/null
+++ b/sdk/cc_sdk_test.go
@@ -0,0 +1,674 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package sdk
+
+import (
+	"testing"
+
+	"android/soong/cc"
+)
+
+func testSdkWithCc(t *testing.T, bp string) *testSdkResult {
+	t.Helper()
+
+	fs := map[string][]byte{
+		"Test.cpp":                  nil,
+		"include/Test.h":            nil,
+		"arm64/include/Arm64Test.h": nil,
+		"libfoo.so":                 nil,
+		"aidl/foo/bar/Test.aidl":    nil,
+	}
+	return testSdkWithFs(t, bp, fs)
+}
+
+// Contains tests for SDK members provided by the cc package.
+
+func TestSdkIsCompileMultilibBoth(t *testing.T) {
+	result := testSdkWithCc(t, `
+		sdk {
+			name: "mysdk",
+			native_shared_libs: ["sdkmember"],
+		}
+
+		cc_library_shared {
+			name: "sdkmember",
+			srcs: ["Test.cpp"],
+			system_shared_libs: [],
+			stl: "none",
+		}
+	`)
+
+	armOutput := result.Module("sdkmember", "android_arm_armv7-a-neon_shared").(*cc.Module).OutputFile()
+	arm64Output := result.Module("sdkmember", "android_arm64_armv8-a_shared").(*cc.Module).OutputFile()
+
+	var inputs []string
+	buildParams := result.Module("mysdk", "android_common").BuildParamsForTests()
+	for _, bp := range buildParams {
+		if bp.Input != nil {
+			inputs = append(inputs, bp.Input.String())
+		}
+	}
+
+	// ensure that both 32/64 outputs are inputs of the sdk snapshot
+	ensureListContains(t, inputs, armOutput.String())
+	ensureListContains(t, inputs, arm64Output.String())
+}
+
+func TestBasicSdkWithCc(t *testing.T) {
+	result := testSdkWithCc(t, `
+		sdk {
+			name: "mysdk",
+			native_shared_libs: ["sdkmember"],
+		}
+
+		cc_library_shared {
+			name: "sdkmember",
+		}
+
+		sdk_snapshot {
+			name: "mysdk@1",
+			native_shared_libs: ["sdkmember_mysdk_1"],
+		}
+
+		sdk_snapshot {
+			name: "mysdk@2",
+			native_shared_libs: ["sdkmember_mysdk_2"],
+		}
+
+		cc_prebuilt_library_shared {
+			name: "sdkmember",
+			srcs: ["libfoo.so"],
+			prefer: false,
+			system_shared_libs: [],
+			stl: "none",
+		}
+
+		cc_prebuilt_library_shared {
+			name: "sdkmember_mysdk_1",
+			sdk_member_name: "sdkmember",
+			srcs: ["libfoo.so"],
+			system_shared_libs: [],
+			stl: "none",
+		}
+
+		cc_prebuilt_library_shared {
+			name: "sdkmember_mysdk_2",
+			sdk_member_name: "sdkmember",
+			srcs: ["libfoo.so"],
+			system_shared_libs: [],
+			stl: "none",
+		}
+
+		cc_library_shared {
+			name: "mycpplib",
+			srcs: ["Test.cpp"],
+			shared_libs: ["sdkmember"],
+			system_shared_libs: [],
+			stl: "none",
+		}
+
+		apex {
+			name: "myapex",
+			native_shared_libs: ["mycpplib"],
+			uses_sdks: ["mysdk@1"],
+			key: "myapex.key",
+			certificate: ":myapex.cert",
+		}
+
+		apex {
+			name: "myapex2",
+			native_shared_libs: ["mycpplib"],
+			uses_sdks: ["mysdk@2"],
+			key: "myapex.key",
+			certificate: ":myapex.cert",
+		}
+	`)
+
+	sdkMemberV1 := result.ModuleForTests("sdkmember_mysdk_1", "android_arm64_armv8-a_shared_myapex").Rule("toc").Output
+	sdkMemberV2 := result.ModuleForTests("sdkmember_mysdk_2", "android_arm64_armv8-a_shared_myapex2").Rule("toc").Output
+
+	cpplibForMyApex := result.ModuleForTests("mycpplib", "android_arm64_armv8-a_shared_myapex")
+	cpplibForMyApex2 := result.ModuleForTests("mycpplib", "android_arm64_armv8-a_shared_myapex2")
+
+	// Depending on the uses_sdks value, different libs are linked
+	ensureListContains(t, pathsToStrings(cpplibForMyApex.Rule("ld").Implicits), sdkMemberV1.String())
+	ensureListContains(t, pathsToStrings(cpplibForMyApex2.Rule("ld").Implicits), sdkMemberV2.String())
+}
+
+// Make sure the sdk can use host specific cc libraries static/shared and both.
+func TestHostSdkWithCc(t *testing.T) {
+	testSdkWithCc(t, `
+		sdk {
+			name: "mysdk",
+			device_supported: false,
+			host_supported: true,
+			native_shared_libs: ["sdkshared"],
+			native_static_libs: ["sdkstatic"],
+		}
+
+		cc_library_host_shared {
+			name: "sdkshared",
+			system_shared_libs: [],
+			stl: "none",
+		}
+
+		cc_library_host_static {
+			name: "sdkstatic",
+			system_shared_libs: [],
+			stl: "none",
+		}
+	`)
+}
+
+// Make sure the sdk can use cc libraries static/shared and both.
+func TestSdkWithCc(t *testing.T) {
+	testSdkWithCc(t, `
+		sdk {
+			name: "mysdk",
+			native_shared_libs: ["sdkshared", "sdkboth1"],
+			native_static_libs: ["sdkstatic", "sdkboth2"],
+		}
+
+		cc_library_shared {
+			name: "sdkshared",
+			system_shared_libs: [],
+			stl: "none",
+		}
+
+		cc_library_static {
+			name: "sdkstatic",
+			system_shared_libs: [],
+			stl: "none",
+		}
+
+		cc_library {
+			name: "sdkboth1",
+			system_shared_libs: [],
+			stl: "none",
+		}
+
+		cc_library {
+			name: "sdkboth2",
+			system_shared_libs: [],
+			stl: "none",
+		}
+	`)
+}
+
+func TestSnapshotWithCcDuplicateHeaders(t *testing.T) {
+	result := testSdkWithCc(t, `
+		sdk {
+			name: "mysdk",
+			native_shared_libs: ["mynativelib1", "mynativelib2"],
+		}
+
+		cc_library_shared {
+			name: "mynativelib1",
+			srcs: [
+				"Test.cpp",
+			],
+			export_include_dirs: ["include"],
+			system_shared_libs: [],
+			stl: "none",
+		}
+
+		cc_library_shared {
+			name: "mynativelib2",
+			srcs: [
+				"Test.cpp",
+			],
+			export_include_dirs: ["include"],
+			system_shared_libs: [],
+			stl: "none",
+		}
+	`)
+
+	result.CheckSnapshot("mysdk", "android_common", "",
+		checkAllCopyRules(`
+include/Test.h -> include/include/Test.h
+.intermediates/mynativelib1/android_arm64_armv8-a_shared/mynativelib1.so -> arm64/lib/mynativelib1.so
+.intermediates/mynativelib1/android_arm_armv7-a-neon_shared/mynativelib1.so -> arm/lib/mynativelib1.so
+.intermediates/mynativelib2/android_arm64_armv8-a_shared/mynativelib2.so -> arm64/lib/mynativelib2.so
+.intermediates/mynativelib2/android_arm_armv7-a-neon_shared/mynativelib2.so -> arm/lib/mynativelib2.so
+`),
+	)
+}
+
+// Verify that when the shared library has some common and some arch specific properties that the generated
+// snapshot is optimized properly.
+func TestSnapshotWithCcSharedLibraryCommonProperties(t *testing.T) {
+	result := testSdkWithCc(t, `
+		sdk {
+			name: "mysdk",
+			native_shared_libs: ["mynativelib"],
+		}
+
+		cc_library_shared {
+			name: "mynativelib",
+			srcs: [
+				"Test.cpp",
+				"aidl/foo/bar/Test.aidl",
+			],
+			export_include_dirs: ["include"],
+			arch: {
+				arm64: {
+					export_system_include_dirs: ["arm64/include"],
+				},
+			},
+			system_shared_libs: [],
+			stl: "none",
+		}
+	`)
+
+	result.CheckSnapshot("mysdk", "android_common", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library_shared {
+    name: "mysdk_mynativelib@current",
+    sdk_member_name: "mynativelib",
+    export_include_dirs: ["include/include"],
+    arch: {
+        arm64: {
+            srcs: ["arm64/lib/mynativelib.so"],
+            export_system_include_dirs: ["arm64/include/arm64/include"],
+        },
+        arm: {
+            srcs: ["arm/lib/mynativelib.so"],
+        },
+    },
+    stl: "none",
+    system_shared_libs: [],
+}
+
+cc_prebuilt_library_shared {
+    name: "mynativelib",
+    prefer: false,
+    export_include_dirs: ["include/include"],
+    arch: {
+        arm64: {
+            srcs: ["arm64/lib/mynativelib.so"],
+            export_system_include_dirs: ["arm64/include/arm64/include"],
+        },
+        arm: {
+            srcs: ["arm/lib/mynativelib.so"],
+        },
+    },
+    stl: "none",
+    system_shared_libs: [],
+}
+
+sdk_snapshot {
+    name: "mysdk@current",
+    native_shared_libs: ["mysdk_mynativelib@current"],
+}
+`),
+		checkAllCopyRules(`
+include/Test.h -> include/include/Test.h
+.intermediates/mynativelib/android_arm64_armv8-a_shared/mynativelib.so -> arm64/lib/mynativelib.so
+arm64/include/Arm64Test.h -> arm64/include/arm64/include/Arm64Test.h
+.intermediates/mynativelib/android_arm_armv7-a-neon_shared/mynativelib.so -> arm/lib/mynativelib.so`),
+	)
+}
+
+func TestSnapshotWithCcSharedLibrary(t *testing.T) {
+	result := testSdkWithCc(t, `
+		sdk {
+			name: "mysdk",
+			native_shared_libs: ["mynativelib"],
+		}
+
+		cc_library_shared {
+			name: "mynativelib",
+			srcs: [
+				"Test.cpp",
+				"aidl/foo/bar/Test.aidl",
+			],
+			export_include_dirs: ["include"],
+			aidl: {
+				export_aidl_headers: true,
+			},
+			system_shared_libs: [],
+			stl: "none",
+		}
+	`)
+
+	result.CheckSnapshot("mysdk", "android_common", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library_shared {
+    name: "mysdk_mynativelib@current",
+    sdk_member_name: "mynativelib",
+    export_include_dirs: ["include/include"],
+    arch: {
+        arm64: {
+            srcs: ["arm64/lib/mynativelib.so"],
+            export_include_dirs: ["arm64/include_gen/mynativelib"],
+        },
+        arm: {
+            srcs: ["arm/lib/mynativelib.so"],
+            export_include_dirs: ["arm/include_gen/mynativelib"],
+        },
+    },
+    stl: "none",
+    system_shared_libs: [],
+}
+
+cc_prebuilt_library_shared {
+    name: "mynativelib",
+    prefer: false,
+    export_include_dirs: ["include/include"],
+    arch: {
+        arm64: {
+            srcs: ["arm64/lib/mynativelib.so"],
+            export_include_dirs: ["arm64/include_gen/mynativelib"],
+        },
+        arm: {
+            srcs: ["arm/lib/mynativelib.so"],
+            export_include_dirs: ["arm/include_gen/mynativelib"],
+        },
+    },
+    stl: "none",
+    system_shared_libs: [],
+}
+
+sdk_snapshot {
+    name: "mysdk@current",
+    native_shared_libs: ["mysdk_mynativelib@current"],
+}
+`),
+		checkAllCopyRules(`
+include/Test.h -> include/include/Test.h
+.intermediates/mynativelib/android_arm64_armv8-a_shared/mynativelib.so -> arm64/lib/mynativelib.so
+.intermediates/mynativelib/android_arm64_armv8-a_shared/gen/aidl/aidl/foo/bar/Test.h -> arm64/include_gen/mynativelib/aidl/foo/bar/Test.h
+.intermediates/mynativelib/android_arm64_armv8-a_shared/gen/aidl/aidl/foo/bar/BnTest.h -> arm64/include_gen/mynativelib/aidl/foo/bar/BnTest.h
+.intermediates/mynativelib/android_arm64_armv8-a_shared/gen/aidl/aidl/foo/bar/BpTest.h -> arm64/include_gen/mynativelib/aidl/foo/bar/BpTest.h
+.intermediates/mynativelib/android_arm_armv7-a-neon_shared/mynativelib.so -> arm/lib/mynativelib.so
+.intermediates/mynativelib/android_arm_armv7-a-neon_shared/gen/aidl/aidl/foo/bar/Test.h -> arm/include_gen/mynativelib/aidl/foo/bar/Test.h
+.intermediates/mynativelib/android_arm_armv7-a-neon_shared/gen/aidl/aidl/foo/bar/BnTest.h -> arm/include_gen/mynativelib/aidl/foo/bar/BnTest.h
+.intermediates/mynativelib/android_arm_armv7-a-neon_shared/gen/aidl/aidl/foo/bar/BpTest.h -> arm/include_gen/mynativelib/aidl/foo/bar/BpTest.h
+`),
+	)
+}
+
+func TestHostSnapshotWithCcSharedLibrary(t *testing.T) {
+	// b/145598135 - Generating host snapshots for anything other than linux is not supported.
+	SkipIfNotLinux(t)
+
+	result := testSdkWithCc(t, `
+		sdk {
+			name: "mysdk",
+			device_supported: false,
+			host_supported: true,
+			native_shared_libs: ["mynativelib"],
+		}
+
+		cc_library_shared {
+			name: "mynativelib",
+			device_supported: false,
+			host_supported: true,
+			srcs: [
+				"Test.cpp",
+				"aidl/foo/bar/Test.aidl",
+			],
+			export_include_dirs: ["include"],
+			aidl: {
+				export_aidl_headers: true,
+			},
+			system_shared_libs: [],
+			stl: "none",
+		}
+	`)
+
+	result.CheckSnapshot("mysdk", "linux_glibc_common", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library_shared {
+    name: "mysdk_mynativelib@current",
+    sdk_member_name: "mynativelib",
+    device_supported: false,
+    host_supported: true,
+    export_include_dirs: ["include/include"],
+    arch: {
+        x86_64: {
+            srcs: ["x86_64/lib/mynativelib.so"],
+            export_include_dirs: ["x86_64/include_gen/mynativelib"],
+        },
+        x86: {
+            srcs: ["x86/lib/mynativelib.so"],
+            export_include_dirs: ["x86/include_gen/mynativelib"],
+        },
+    },
+    stl: "none",
+    system_shared_libs: [],
+}
+
+cc_prebuilt_library_shared {
+    name: "mynativelib",
+    prefer: false,
+    device_supported: false,
+    host_supported: true,
+    export_include_dirs: ["include/include"],
+    arch: {
+        x86_64: {
+            srcs: ["x86_64/lib/mynativelib.so"],
+            export_include_dirs: ["x86_64/include_gen/mynativelib"],
+        },
+        x86: {
+            srcs: ["x86/lib/mynativelib.so"],
+            export_include_dirs: ["x86/include_gen/mynativelib"],
+        },
+    },
+    stl: "none",
+    system_shared_libs: [],
+}
+
+sdk_snapshot {
+    name: "mysdk@current",
+    device_supported: false,
+    host_supported: true,
+    native_shared_libs: ["mysdk_mynativelib@current"],
+}
+`),
+		checkAllCopyRules(`
+include/Test.h -> include/include/Test.h
+.intermediates/mynativelib/linux_glibc_x86_64_shared/mynativelib.so -> x86_64/lib/mynativelib.so
+.intermediates/mynativelib/linux_glibc_x86_64_shared/gen/aidl/aidl/foo/bar/Test.h -> x86_64/include_gen/mynativelib/aidl/foo/bar/Test.h
+.intermediates/mynativelib/linux_glibc_x86_64_shared/gen/aidl/aidl/foo/bar/BnTest.h -> x86_64/include_gen/mynativelib/aidl/foo/bar/BnTest.h
+.intermediates/mynativelib/linux_glibc_x86_64_shared/gen/aidl/aidl/foo/bar/BpTest.h -> x86_64/include_gen/mynativelib/aidl/foo/bar/BpTest.h
+.intermediates/mynativelib/linux_glibc_x86_shared/mynativelib.so -> x86/lib/mynativelib.so
+.intermediates/mynativelib/linux_glibc_x86_shared/gen/aidl/aidl/foo/bar/Test.h -> x86/include_gen/mynativelib/aidl/foo/bar/Test.h
+.intermediates/mynativelib/linux_glibc_x86_shared/gen/aidl/aidl/foo/bar/BnTest.h -> x86/include_gen/mynativelib/aidl/foo/bar/BnTest.h
+.intermediates/mynativelib/linux_glibc_x86_shared/gen/aidl/aidl/foo/bar/BpTest.h -> x86/include_gen/mynativelib/aidl/foo/bar/BpTest.h
+`),
+	)
+}
+
+func TestSnapshotWithCcStaticLibrary(t *testing.T) {
+	result := testSdkWithCc(t, `
+		module_exports {
+			name: "myexports",
+			native_static_libs: ["mynativelib"],
+		}
+
+		cc_library_static {
+			name: "mynativelib",
+			srcs: [
+				"Test.cpp",
+				"aidl/foo/bar/Test.aidl",
+			],
+			export_include_dirs: ["include"],
+			aidl: {
+				export_aidl_headers: true,
+			},
+			system_shared_libs: [],
+			stl: "none",
+		}
+	`)
+
+	result.CheckSnapshot("myexports", "android_common", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library_static {
+    name: "myexports_mynativelib@current",
+    sdk_member_name: "mynativelib",
+    export_include_dirs: ["include/include"],
+    arch: {
+        arm64: {
+            srcs: ["arm64/lib/mynativelib.a"],
+            export_include_dirs: ["arm64/include_gen/mynativelib"],
+        },
+        arm: {
+            srcs: ["arm/lib/mynativelib.a"],
+            export_include_dirs: ["arm/include_gen/mynativelib"],
+        },
+    },
+    stl: "none",
+    system_shared_libs: [],
+}
+
+cc_prebuilt_library_static {
+    name: "mynativelib",
+    prefer: false,
+    export_include_dirs: ["include/include"],
+    arch: {
+        arm64: {
+            srcs: ["arm64/lib/mynativelib.a"],
+            export_include_dirs: ["arm64/include_gen/mynativelib"],
+        },
+        arm: {
+            srcs: ["arm/lib/mynativelib.a"],
+            export_include_dirs: ["arm/include_gen/mynativelib"],
+        },
+    },
+    stl: "none",
+    system_shared_libs: [],
+}
+
+module_exports_snapshot {
+    name: "myexports@current",
+    native_static_libs: ["myexports_mynativelib@current"],
+}
+`),
+		checkAllCopyRules(`
+include/Test.h -> include/include/Test.h
+.intermediates/mynativelib/android_arm64_armv8-a_static/mynativelib.a -> arm64/lib/mynativelib.a
+.intermediates/mynativelib/android_arm64_armv8-a_static/gen/aidl/aidl/foo/bar/Test.h -> arm64/include_gen/mynativelib/aidl/foo/bar/Test.h
+.intermediates/mynativelib/android_arm64_armv8-a_static/gen/aidl/aidl/foo/bar/BnTest.h -> arm64/include_gen/mynativelib/aidl/foo/bar/BnTest.h
+.intermediates/mynativelib/android_arm64_armv8-a_static/gen/aidl/aidl/foo/bar/BpTest.h -> arm64/include_gen/mynativelib/aidl/foo/bar/BpTest.h
+.intermediates/mynativelib/android_arm_armv7-a-neon_static/mynativelib.a -> arm/lib/mynativelib.a
+.intermediates/mynativelib/android_arm_armv7-a-neon_static/gen/aidl/aidl/foo/bar/Test.h -> arm/include_gen/mynativelib/aidl/foo/bar/Test.h
+.intermediates/mynativelib/android_arm_armv7-a-neon_static/gen/aidl/aidl/foo/bar/BnTest.h -> arm/include_gen/mynativelib/aidl/foo/bar/BnTest.h
+.intermediates/mynativelib/android_arm_armv7-a-neon_static/gen/aidl/aidl/foo/bar/BpTest.h -> arm/include_gen/mynativelib/aidl/foo/bar/BpTest.h
+`),
+	)
+}
+
+func TestHostSnapshotWithCcStaticLibrary(t *testing.T) {
+	// b/145598135 - Generating host snapshots for anything other than linux is not supported.
+	SkipIfNotLinux(t)
+
+	result := testSdkWithCc(t, `
+		module_exports {
+			name: "myexports",
+			device_supported: false,
+			host_supported: true,
+			native_static_libs: ["mynativelib"],
+		}
+
+		cc_library_static {
+			name: "mynativelib",
+			device_supported: false,
+			host_supported: true,
+			srcs: [
+				"Test.cpp",
+				"aidl/foo/bar/Test.aidl",
+			],
+			export_include_dirs: ["include"],
+			aidl: {
+				export_aidl_headers: true,
+			},
+			system_shared_libs: [],
+			stl: "none",
+		}
+	`)
+
+	result.CheckSnapshot("myexports", "linux_glibc_common", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library_static {
+    name: "myexports_mynativelib@current",
+    sdk_member_name: "mynativelib",
+    device_supported: false,
+    host_supported: true,
+    export_include_dirs: ["include/include"],
+    arch: {
+        x86_64: {
+            srcs: ["x86_64/lib/mynativelib.a"],
+            export_include_dirs: ["x86_64/include_gen/mynativelib"],
+        },
+        x86: {
+            srcs: ["x86/lib/mynativelib.a"],
+            export_include_dirs: ["x86/include_gen/mynativelib"],
+        },
+    },
+    stl: "none",
+    system_shared_libs: [],
+}
+
+cc_prebuilt_library_static {
+    name: "mynativelib",
+    prefer: false,
+    device_supported: false,
+    host_supported: true,
+    export_include_dirs: ["include/include"],
+    arch: {
+        x86_64: {
+            srcs: ["x86_64/lib/mynativelib.a"],
+            export_include_dirs: ["x86_64/include_gen/mynativelib"],
+        },
+        x86: {
+            srcs: ["x86/lib/mynativelib.a"],
+            export_include_dirs: ["x86/include_gen/mynativelib"],
+        },
+    },
+    stl: "none",
+    system_shared_libs: [],
+}
+
+module_exports_snapshot {
+    name: "myexports@current",
+    device_supported: false,
+    host_supported: true,
+    native_static_libs: ["myexports_mynativelib@current"],
+}
+`),
+		checkAllCopyRules(`
+include/Test.h -> include/include/Test.h
+.intermediates/mynativelib/linux_glibc_x86_64_static/mynativelib.a -> x86_64/lib/mynativelib.a
+.intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/Test.h -> x86_64/include_gen/mynativelib/aidl/foo/bar/Test.h
+.intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/BnTest.h -> x86_64/include_gen/mynativelib/aidl/foo/bar/BnTest.h
+.intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/BpTest.h -> x86_64/include_gen/mynativelib/aidl/foo/bar/BpTest.h
+.intermediates/mynativelib/linux_glibc_x86_static/mynativelib.a -> x86/lib/mynativelib.a
+.intermediates/mynativelib/linux_glibc_x86_static/gen/aidl/aidl/foo/bar/Test.h -> x86/include_gen/mynativelib/aidl/foo/bar/Test.h
+.intermediates/mynativelib/linux_glibc_x86_static/gen/aidl/aidl/foo/bar/BnTest.h -> x86/include_gen/mynativelib/aidl/foo/bar/BnTest.h
+.intermediates/mynativelib/linux_glibc_x86_static/gen/aidl/aidl/foo/bar/BpTest.h -> x86/include_gen/mynativelib/aidl/foo/bar/BpTest.h
+`),
+	)
+}
diff --git a/sdk/exports.go b/sdk/exports.go
new file mode 100644
index 0000000..d313057
--- /dev/null
+++ b/sdk/exports.go
@@ -0,0 +1,36 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package sdk
+
+import "android/soong/android"
+
+func init() {
+	android.RegisterModuleType("module_exports", ModuleExportsFactory)
+	android.RegisterModuleType("module_exports_snapshot", ModuleExportsSnapshotsFactory)
+}
+
+// module_exports defines the exports of a mainline module. The exports are Soong modules
+// which are required by Soong modules that are not part of the mainline module.
+func ModuleExportsFactory() android.Module {
+	return newSdkModule(true)
+}
+
+// module_exports_snapshot is a versioned snapshot of prebuilt versions of all the exports
+// of a mainline module.
+func ModuleExportsSnapshotsFactory() android.Module {
+	s := newSdkModule(true)
+	s.properties.Snapshot = true
+	return s
+}
diff --git a/sdk/exports_test.go b/sdk/exports_test.go
new file mode 100644
index 0000000..b905d71
--- /dev/null
+++ b/sdk/exports_test.go
@@ -0,0 +1,66 @@
+// 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 sdk
+
+import (
+	"testing"
+)
+
+// Ensure that module_exports generates a module_exports_snapshot module.
+func TestModuleExportsSnapshot(t *testing.T) {
+	packageBp := `
+		module_exports {
+			name: "myexports",
+			java_libs: [
+				"myjavalib",
+			],
+		}
+
+		java_library {
+			name: "myjavalib",
+			srcs: ["Test.java"],
+			system_modules: "none",
+			sdk_version: "none",
+		}
+	`
+
+	result := testSdkWithFs(t, ``,
+		map[string][]byte{
+			"package/Test.java":  nil,
+			"package/Android.bp": []byte(packageBp),
+		})
+
+	result.CheckSnapshot("myexports", "android_common", "package",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+    name: "myexports_myjavalib@current",
+    sdk_member_name: "myjavalib",
+    jars: ["java/myjavalib.jar"],
+}
+
+java_import {
+    name: "myjavalib",
+    prefer: false,
+    jars: ["java/myjavalib.jar"],
+}
+
+module_exports_snapshot {
+    name: "myexports@current",
+    java_libs: ["myexports_myjavalib@current"],
+}
+`))
+}
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
new file mode 100644
index 0000000..218a16a
--- /dev/null
+++ b/sdk/java_sdk_test.go
@@ -0,0 +1,567 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package sdk
+
+import (
+	"testing"
+)
+
+func testSdkWithJava(t *testing.T, bp string) *testSdkResult {
+	t.Helper()
+
+	fs := map[string][]byte{
+		"Test.java":              nil,
+		"aidl/foo/bar/Test.aidl": nil,
+	}
+	return testSdkWithFs(t, bp, fs)
+}
+
+// Contains tests for SDK members provided by the java package.
+
+func TestBasicSdkWithJavaLibrary(t *testing.T) {
+	result := testSdkWithJava(t, `
+		sdk {
+			name: "mysdk",
+			java_header_libs: ["myjavalib"],
+		}
+
+		sdk_snapshot {
+			name: "mysdk@1",
+			java_header_libs: ["sdkmember_mysdk_1"],
+		}
+
+		sdk_snapshot {
+			name: "mysdk@2",
+			java_header_libs: ["sdkmember_mysdk_2"],
+		}
+
+		java_import {
+			name: "sdkmember",
+			prefer: false,
+			host_supported: true,
+		}
+
+		java_import {
+			name: "sdkmember_mysdk_1",
+			sdk_member_name: "sdkmember",
+			host_supported: true,
+		}
+
+		java_import {
+			name: "sdkmember_mysdk_2",
+			sdk_member_name: "sdkmember",
+			host_supported: true,
+		}
+
+		java_library {
+			name: "myjavalib",
+			srcs: ["Test.java"],
+			libs: ["sdkmember"],
+			system_modules: "none",
+			sdk_version: "none",
+			compile_dex: true,
+			host_supported: true,
+		}
+
+		apex {
+			name: "myapex",
+			java_libs: ["myjavalib"],
+			uses_sdks: ["mysdk@1"],
+			key: "myapex.key",
+			certificate: ":myapex.cert",
+		}
+
+		apex {
+			name: "myapex2",
+			java_libs: ["myjavalib"],
+			uses_sdks: ["mysdk@2"],
+			key: "myapex.key",
+			certificate: ":myapex.cert",
+		}
+	`)
+
+	sdkMemberV1 := result.ctx.ModuleForTests("sdkmember_mysdk_1", "android_common_myapex").Rule("combineJar").Output
+	sdkMemberV2 := result.ctx.ModuleForTests("sdkmember_mysdk_2", "android_common_myapex2").Rule("combineJar").Output
+
+	javalibForMyApex := result.ctx.ModuleForTests("myjavalib", "android_common_myapex")
+	javalibForMyApex2 := result.ctx.ModuleForTests("myjavalib", "android_common_myapex2")
+
+	// Depending on the uses_sdks value, different libs are linked
+	ensureListContains(t, pathsToStrings(javalibForMyApex.Rule("javac").Implicits), sdkMemberV1.String())
+	ensureListContains(t, pathsToStrings(javalibForMyApex2.Rule("javac").Implicits), sdkMemberV2.String())
+}
+
+func TestSnapshotWithJavaHeaderLibrary(t *testing.T) {
+	result := testSdkWithJava(t, `
+		sdk {
+			name: "mysdk",
+			java_header_libs: ["myjavalib"],
+		}
+
+		java_library {
+			name: "myjavalib",
+			srcs: ["Test.java"],
+			aidl: {
+				export_include_dirs: ["aidl"],
+			},
+			system_modules: "none",
+			sdk_version: "none",
+			compile_dex: true,
+			host_supported: true,
+		}
+	`)
+
+	result.CheckSnapshot("mysdk", "android_common", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+    name: "mysdk_myjavalib@current",
+    sdk_member_name: "myjavalib",
+    jars: ["java/myjavalib.jar"],
+}
+
+java_import {
+    name: "myjavalib",
+    prefer: false,
+    jars: ["java/myjavalib.jar"],
+}
+
+sdk_snapshot {
+    name: "mysdk@current",
+    java_header_libs: ["mysdk_myjavalib@current"],
+}
+
+`),
+		checkAllCopyRules(`
+.intermediates/myjavalib/android_common/turbine-combined/myjavalib.jar -> java/myjavalib.jar
+aidl/foo/bar/Test.aidl -> aidl/aidl/foo/bar/Test.aidl
+`),
+	)
+}
+
+func TestHostSnapshotWithJavaHeaderLibrary(t *testing.T) {
+	// b/145598135 - Generating host snapshots for anything other than linux is not supported.
+	SkipIfNotLinux(t)
+
+	result := testSdkWithJava(t, `
+		sdk {
+			name: "mysdk",
+			device_supported: false,
+			host_supported: true,
+			java_header_libs: ["myjavalib"],
+		}
+
+		java_library {
+			name: "myjavalib",
+			device_supported: false,
+			host_supported: true,
+			srcs: ["Test.java"],
+			aidl: {
+				export_include_dirs: ["aidl"],
+			},
+			system_modules: "none",
+			sdk_version: "none",
+			compile_dex: true,
+		}
+	`)
+
+	result.CheckSnapshot("mysdk", "linux_glibc_common", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+    name: "mysdk_myjavalib@current",
+    sdk_member_name: "myjavalib",
+    device_supported: false,
+    host_supported: true,
+    jars: ["java/myjavalib.jar"],
+}
+
+java_import {
+    name: "myjavalib",
+    prefer: false,
+    device_supported: false,
+    host_supported: true,
+    jars: ["java/myjavalib.jar"],
+}
+
+sdk_snapshot {
+    name: "mysdk@current",
+    device_supported: false,
+    host_supported: true,
+    java_header_libs: ["mysdk_myjavalib@current"],
+}
+`),
+		checkAllCopyRules(`
+.intermediates/myjavalib/linux_glibc_common/javac/myjavalib.jar -> java/myjavalib.jar
+aidl/foo/bar/Test.aidl -> aidl/aidl/foo/bar/Test.aidl
+`),
+	)
+}
+
+func TestSnapshotWithJavaImplLibrary(t *testing.T) {
+	result := testSdkWithJava(t, `
+		module_exports {
+			name: "myexports",
+			java_libs: ["myjavalib"],
+		}
+
+		java_library {
+			name: "myjavalib",
+			srcs: ["Test.java"],
+			aidl: {
+				export_include_dirs: ["aidl"],
+			},
+			system_modules: "none",
+			sdk_version: "none",
+			compile_dex: true,
+			host_supported: true,
+		}
+	`)
+
+	result.CheckSnapshot("myexports", "android_common", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+    name: "myexports_myjavalib@current",
+    sdk_member_name: "myjavalib",
+    jars: ["java/myjavalib.jar"],
+}
+
+java_import {
+    name: "myjavalib",
+    prefer: false,
+    jars: ["java/myjavalib.jar"],
+}
+
+module_exports_snapshot {
+    name: "myexports@current",
+    java_libs: ["myexports_myjavalib@current"],
+}
+
+`),
+		checkAllCopyRules(`
+.intermediates/myjavalib/android_common/javac/myjavalib.jar -> java/myjavalib.jar
+aidl/foo/bar/Test.aidl -> aidl/aidl/foo/bar/Test.aidl
+`),
+	)
+}
+
+func TestHostSnapshotWithJavaImplLibrary(t *testing.T) {
+	// b/145598135 - Generating host snapshots for anything other than linux is not supported.
+	SkipIfNotLinux(t)
+
+	result := testSdkWithJava(t, `
+		module_exports {
+			name: "myexports",
+			device_supported: false,
+			host_supported: true,
+			java_libs: ["myjavalib"],
+		}
+
+		java_library {
+			name: "myjavalib",
+			device_supported: false,
+			host_supported: true,
+			srcs: ["Test.java"],
+			aidl: {
+				export_include_dirs: ["aidl"],
+			},
+			system_modules: "none",
+			sdk_version: "none",
+			compile_dex: true,
+		}
+	`)
+
+	result.CheckSnapshot("myexports", "linux_glibc_common", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+    name: "myexports_myjavalib@current",
+    sdk_member_name: "myjavalib",
+    device_supported: false,
+    host_supported: true,
+    jars: ["java/myjavalib.jar"],
+}
+
+java_import {
+    name: "myjavalib",
+    prefer: false,
+    device_supported: false,
+    host_supported: true,
+    jars: ["java/myjavalib.jar"],
+}
+
+module_exports_snapshot {
+    name: "myexports@current",
+    device_supported: false,
+    host_supported: true,
+    java_libs: ["myexports_myjavalib@current"],
+}
+`),
+		checkAllCopyRules(`
+.intermediates/myjavalib/linux_glibc_common/javac/myjavalib.jar -> java/myjavalib.jar
+aidl/foo/bar/Test.aidl -> aidl/aidl/foo/bar/Test.aidl
+`),
+	)
+}
+
+func TestSnapshotWithJavaTest(t *testing.T) {
+	result := testSdkWithJava(t, `
+		module_exports {
+			name: "myexports",
+			java_tests: ["myjavatests"],
+		}
+
+		java_test {
+			name: "myjavatests",
+			srcs: ["Test.java"],
+			system_modules: "none",
+			sdk_version: "none",
+			compile_dex: true,
+			host_supported: true,
+		}
+	`)
+
+	result.CheckSnapshot("myexports", "android_common", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_test_import {
+    name: "myexports_myjavatests@current",
+    sdk_member_name: "myjavatests",
+    jars: ["java/myjavatests.jar"],
+    test_config: "java/myjavatests-AndroidTest.xml",
+}
+
+java_test_import {
+    name: "myjavatests",
+    prefer: false,
+    jars: ["java/myjavatests.jar"],
+    test_config: "java/myjavatests-AndroidTest.xml",
+}
+
+module_exports_snapshot {
+    name: "myexports@current",
+    java_tests: ["myexports_myjavatests@current"],
+}
+`),
+		checkAllCopyRules(`
+.intermediates/myjavatests/android_common/javac/myjavatests.jar -> java/myjavatests.jar
+.intermediates/myjavatests/android_common/myjavatests.config -> java/myjavatests-AndroidTest.xml
+`),
+	)
+}
+
+func TestHostSnapshotWithJavaTest(t *testing.T) {
+	// b/145598135 - Generating host snapshots for anything other than linux is not supported.
+	SkipIfNotLinux(t)
+
+	result := testSdkWithJava(t, `
+		module_exports {
+			name: "myexports",
+			device_supported: false,
+			host_supported: true,
+			java_tests: ["myjavatests"],
+		}
+
+		java_test {
+			name: "myjavatests",
+			device_supported: false,
+			host_supported: true,
+			srcs: ["Test.java"],
+			system_modules: "none",
+			sdk_version: "none",
+			compile_dex: true,
+		}
+	`)
+
+	result.CheckSnapshot("myexports", "linux_glibc_common", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_test_import {
+    name: "myexports_myjavatests@current",
+    sdk_member_name: "myjavatests",
+    device_supported: false,
+    host_supported: true,
+    jars: ["java/myjavatests.jar"],
+    test_config: "java/myjavatests-AndroidTest.xml",
+}
+
+java_test_import {
+    name: "myjavatests",
+    prefer: false,
+    device_supported: false,
+    host_supported: true,
+    jars: ["java/myjavatests.jar"],
+    test_config: "java/myjavatests-AndroidTest.xml",
+}
+
+module_exports_snapshot {
+    name: "myexports@current",
+    device_supported: false,
+    host_supported: true,
+    java_tests: ["myexports_myjavatests@current"],
+}
+`),
+		checkAllCopyRules(`
+.intermediates/myjavatests/linux_glibc_common/javac/myjavatests.jar -> java/myjavatests.jar
+.intermediates/myjavatests/linux_glibc_common/myjavatests.config -> java/myjavatests-AndroidTest.xml
+`),
+	)
+}
+
+func testSdkWithDroidstubs(t *testing.T, bp string) *testSdkResult {
+	t.Helper()
+
+	fs := map[string][]byte{
+		"foo/bar/Foo.java":               nil,
+		"stubs-sources/foo/bar/Foo.java": nil,
+	}
+	return testSdkWithFs(t, bp, fs)
+}
+
+// Note: This test does not verify that a droidstubs can be referenced, either
+// directly or indirectly from an APEX as droidstubs can never be a part of an
+// apex.
+func TestBasicSdkWithDroidstubs(t *testing.T) {
+	testSdkWithDroidstubs(t, `
+		sdk {
+				name: "mysdk",
+				stubs_sources: ["mystub"],
+		}
+		sdk_snapshot {
+				name: "mysdk@10",
+				stubs_sources: ["mystub_mysdk@10"],
+		}
+		prebuilt_stubs_sources {
+				name: "mystub_mysdk@10",
+				sdk_member_name: "mystub",
+				srcs: ["stubs-sources/foo/bar/Foo.java"],
+		}
+		droidstubs {
+				name: "mystub",
+				srcs: ["foo/bar/Foo.java"],
+				sdk_version: "none",
+				system_modules: "none",
+		}
+		java_library {
+				name: "myjavalib",
+				srcs: [":mystub"],
+				sdk_version: "none",
+				system_modules: "none",
+		}
+	`)
+}
+
+func TestSnapshotWithDroidstubs(t *testing.T) {
+	result := testSdkWithDroidstubs(t, `
+		module_exports {
+			name: "myexports",
+			stubs_sources: ["myjavaapistubs"],
+		}
+
+		droidstubs {
+			name: "myjavaapistubs",
+			srcs: ["foo/bar/Foo.java"],
+			system_modules: "none",
+			sdk_version: "none",
+		}
+	`)
+
+	result.CheckSnapshot("myexports", "android_common", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+prebuilt_stubs_sources {
+    name: "myexports_myjavaapistubs@current",
+    sdk_member_name: "myjavaapistubs",
+    srcs: ["java/myjavaapistubs_stubs_sources"],
+}
+
+prebuilt_stubs_sources {
+    name: "myjavaapistubs",
+    prefer: false,
+    srcs: ["java/myjavaapistubs_stubs_sources"],
+}
+
+module_exports_snapshot {
+    name: "myexports@current",
+    stubs_sources: ["myexports_myjavaapistubs@current"],
+}
+
+`),
+		checkAllCopyRules(""),
+		checkMergeZip(".intermediates/myexports/android_common/tmp/java/myjavaapistubs_stubs_sources.zip"),
+	)
+}
+
+func TestHostSnapshotWithDroidstubs(t *testing.T) {
+	// b/145598135 - Generating host snapshots for anything other than linux is not supported.
+	SkipIfNotLinux(t)
+
+	result := testSdkWithDroidstubs(t, `
+		module_exports {
+			name: "myexports",
+			device_supported: false,
+			host_supported: true,
+			stubs_sources: ["myjavaapistubs"],
+		}
+
+		droidstubs {
+			name: "myjavaapistubs",
+			device_supported: false,
+			host_supported: true,
+			srcs: ["foo/bar/Foo.java"],
+			system_modules: "none",
+			sdk_version: "none",
+		}
+	`)
+
+	result.CheckSnapshot("myexports", "linux_glibc_common", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+prebuilt_stubs_sources {
+    name: "myexports_myjavaapistubs@current",
+    sdk_member_name: "myjavaapistubs",
+    device_supported: false,
+    host_supported: true,
+    srcs: ["java/myjavaapistubs_stubs_sources"],
+}
+
+prebuilt_stubs_sources {
+    name: "myjavaapistubs",
+    prefer: false,
+    device_supported: false,
+    host_supported: true,
+    srcs: ["java/myjavaapistubs_stubs_sources"],
+}
+
+module_exports_snapshot {
+    name: "myexports@current",
+    device_supported: false,
+    host_supported: true,
+    stubs_sources: ["myexports_myjavaapistubs@current"],
+}
+`),
+		checkAllCopyRules(""),
+		checkMergeZip(".intermediates/myexports/linux_glibc_common/tmp/java/myjavaapistubs_stubs_sources.zip"),
+	)
+}
diff --git a/sdk/sdk.go b/sdk/sdk.go
new file mode 100644
index 0000000..44e5cbb
--- /dev/null
+++ b/sdk/sdk.go
@@ -0,0 +1,405 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package sdk
+
+import (
+	"fmt"
+	"io"
+	"reflect"
+	"strconv"
+
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+
+	"android/soong/android"
+	// This package doesn't depend on the apex package, but import it to make its mutators to be
+	// registered before mutators in this package. See RegisterPostDepsMutators for more details.
+	_ "android/soong/apex"
+)
+
+func init() {
+	pctx.Import("android/soong/android")
+	pctx.Import("android/soong/java/config")
+
+	android.RegisterModuleType("sdk", SdkModuleFactory)
+	android.RegisterModuleType("sdk_snapshot", SnapshotModuleFactory)
+	android.PreDepsMutators(RegisterPreDepsMutators)
+	android.PostDepsMutators(RegisterPostDepsMutators)
+}
+
+type sdk struct {
+	android.ModuleBase
+	android.DefaultableModuleBase
+
+	// The dynamically generated information about the registered SdkMemberType
+	dynamicSdkMemberTypes *dynamicSdkMemberTypes
+
+	// The dynamically created instance of the properties struct containing the sdk member
+	// list properties, e.g. java_libs.
+	dynamicMemberTypeListProperties interface{}
+
+	properties sdkProperties
+
+	snapshotFile android.OptionalPath
+
+	// The builder, preserved for testing.
+	builderForTests *snapshotBuilder
+}
+
+type sdkProperties struct {
+	Snapshot bool `blueprint:"mutated"`
+
+	// True if this is a module_exports (or module_exports_snapshot) module type.
+	Module_exports bool `blueprint:"mutated"`
+}
+
+type sdkMemberDependencyTag struct {
+	blueprint.BaseDependencyTag
+	memberType android.SdkMemberType
+}
+
+// Contains information about the sdk properties that list sdk members, e.g.
+// Java_header_libs.
+type sdkMemberListProperty struct {
+	// getter for the list of member names
+	getter func(properties interface{}) []string
+
+	// the type of member referenced in the list
+	memberType android.SdkMemberType
+
+	// the dependency tag used for items in this list that can be used to determine the memberType
+	// for a resolved dependency.
+	dependencyTag *sdkMemberDependencyTag
+}
+
+func (p *sdkMemberListProperty) propertyName() string {
+	return p.memberType.SdkPropertyName()
+}
+
+// Cache of dynamically generated dynamicSdkMemberTypes objects. The key is the pointer
+// to a slice of SdkMemberType instances held in android.SdkMemberTypes.
+var dynamicSdkMemberTypesMap android.OncePer
+
+// A dynamically generated set of member list properties and associated structure type.
+type dynamicSdkMemberTypes struct {
+	// The dynamically generated structure type.
+	//
+	// Contains one []string exported field for each android.SdkMemberTypes. The name of the field
+	// is the exported form of the value returned by SdkMemberType.SdkPropertyName().
+	propertiesStructType reflect.Type
+
+	// Information about each of the member type specific list properties.
+	memberListProperties []*sdkMemberListProperty
+}
+
+func (d *dynamicSdkMemberTypes) createMemberListProperties() interface{} {
+	return reflect.New(d.propertiesStructType).Interface()
+}
+
+func getDynamicSdkMemberTypes(registry *android.SdkMemberTypesRegistry) *dynamicSdkMemberTypes {
+
+	// Get a key that uniquely identifies the registry contents.
+	key := registry.UniqueOnceKey()
+
+	// Get the registered types.
+	registeredTypes := registry.RegisteredTypes()
+
+	// Get the cached value, creating new instance if necessary.
+	return dynamicSdkMemberTypesMap.Once(key, func() interface{} {
+		return createDynamicSdkMemberTypes(registeredTypes)
+	}).(*dynamicSdkMemberTypes)
+}
+
+// Create the dynamicSdkMemberTypes from the list of registered member types.
+//
+// A struct is created which contains one exported field per member type corresponding to
+// the SdkMemberType.SdkPropertyName() value.
+//
+// A list of sdkMemberListProperty instances is created, one per member type that provides:
+// * a reference to the member type.
+// * a getter for the corresponding field in the properties struct.
+// * a dependency tag that identifies the member type of a resolved dependency.
+//
+func createDynamicSdkMemberTypes(sdkMemberTypes []android.SdkMemberType) *dynamicSdkMemberTypes {
+
+	var listProperties []*sdkMemberListProperty
+	var fields []reflect.StructField
+
+	// Iterate over the member types creating StructField and sdkMemberListProperty objects.
+	for f, memberType := range sdkMemberTypes {
+		p := memberType.SdkPropertyName()
+
+		// Create a dynamic exported field for the member type's property.
+		fields = append(fields, reflect.StructField{
+			Name: proptools.FieldNameForProperty(p),
+			Type: reflect.TypeOf([]string{}),
+		})
+
+		// Copy the field index for use in the getter func as using the loop variable directly will
+		// cause all funcs to use the last value.
+		fieldIndex := f
+
+		// Create an sdkMemberListProperty for the member type.
+		memberListProperty := &sdkMemberListProperty{
+			getter: func(properties interface{}) []string {
+				// The properties is expected to be of the following form (where
+				// <Module_types> is the name of an SdkMemberType.SdkPropertyName().
+				//     properties *struct {<Module_types> []string, ....}
+				//
+				// Although it accesses the field by index the following reflection code is equivalent to:
+				//    *properties.<Module_types>
+				//
+				list := reflect.ValueOf(properties).Elem().Field(fieldIndex).Interface().([]string)
+				return list
+			},
+
+			memberType: memberType,
+
+			dependencyTag: &sdkMemberDependencyTag{
+				memberType: memberType,
+			},
+		}
+
+		listProperties = append(listProperties, memberListProperty)
+	}
+
+	// Create a dynamic struct from the collated fields.
+	propertiesStructType := reflect.StructOf(fields)
+
+	return &dynamicSdkMemberTypes{
+		memberListProperties: listProperties,
+		propertiesStructType: propertiesStructType,
+	}
+}
+
+// sdk defines an SDK which is a logical group of modules (e.g. native libs, headers, java libs, etc.)
+// which Mainline modules like APEX can choose to build with.
+func SdkModuleFactory() android.Module {
+	return newSdkModule(false)
+}
+
+func newSdkModule(moduleExports bool) *sdk {
+	s := &sdk{}
+	s.properties.Module_exports = moduleExports
+	// Get the dynamic sdk member type data for the currently registered sdk member types.
+	var registry *android.SdkMemberTypesRegistry
+	if moduleExports {
+		registry = android.ModuleExportsMemberTypes
+	} else {
+		registry = android.SdkMemberTypes
+	}
+	s.dynamicSdkMemberTypes = getDynamicSdkMemberTypes(registry)
+	// Create an instance of the dynamically created struct that contains all the
+	// properties for the member type specific list properties.
+	s.dynamicMemberTypeListProperties = s.dynamicSdkMemberTypes.createMemberListProperties()
+	s.AddProperties(&s.properties, s.dynamicMemberTypeListProperties)
+	android.InitAndroidMultiTargetsArchModule(s, android.HostAndDeviceSupported, android.MultilibCommon)
+	android.InitDefaultableModule(s)
+	android.AddLoadHook(s, func(ctx android.LoadHookContext) {
+		type props struct {
+			Compile_multilib *string
+		}
+		p := &props{Compile_multilib: proptools.StringPtr("both")}
+		ctx.AppendProperties(p)
+	})
+	return s
+}
+
+// sdk_snapshot is a versioned snapshot of an SDK. This is an auto-generated module.
+func SnapshotModuleFactory() android.Module {
+	s := newSdkModule(false)
+	s.properties.Snapshot = true
+	return s
+}
+
+func (s *sdk) snapshot() bool {
+	return s.properties.Snapshot
+}
+
+func (s *sdk) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	if !s.snapshot() {
+		// We don't need to create a snapshot out of sdk_snapshot.
+		// That doesn't make sense. We need a snapshot to create sdk_snapshot.
+		s.snapshotFile = android.OptionalPathForPath(s.buildSnapshot(ctx))
+	}
+}
+
+func (s *sdk) AndroidMkEntries() []android.AndroidMkEntries {
+	if !s.snapshotFile.Valid() {
+		return []android.AndroidMkEntries{}
+	}
+
+	return []android.AndroidMkEntries{android.AndroidMkEntries{
+		Class:      "FAKE",
+		OutputFile: s.snapshotFile,
+		DistFile:   s.snapshotFile,
+		Include:    "$(BUILD_PHONY_PACKAGE)",
+		ExtraFooters: []android.AndroidMkExtraFootersFunc{
+			func(w io.Writer, name, prefix, moduleDir string, entries *android.AndroidMkEntries) {
+				// Allow the sdk to be built by simply passing its name on the command line.
+				fmt.Fprintln(w, ".PHONY:", s.Name())
+				fmt.Fprintln(w, s.Name()+":", s.snapshotFile.String())
+			},
+		},
+	}}
+}
+
+// RegisterPreDepsMutators registers pre-deps mutators to support modules implementing SdkAware
+// interface and the sdk module type. This function has been made public to be called by tests
+// outside of the sdk package
+func RegisterPreDepsMutators(ctx android.RegisterMutatorsContext) {
+	ctx.BottomUp("SdkMember", memberMutator).Parallel()
+	ctx.TopDown("SdkMember_deps", memberDepsMutator).Parallel()
+	ctx.BottomUp("SdkMemberInterVersion", memberInterVersionMutator).Parallel()
+}
+
+// RegisterPostDepshMutators registers post-deps mutators to support modules implementing SdkAware
+// interface and the sdk module type. This function has been made public to be called by tests
+// outside of the sdk package
+func RegisterPostDepsMutators(ctx android.RegisterMutatorsContext) {
+	// These must run AFTER apexMutator. Note that the apex package is imported even though there is
+	// no direct dependency to the package here. sdkDepsMutator sets the SDK requirements from an
+	// APEX to its dependents. Since different versions of the same SDK can be used by different
+	// APEXes, the apex and its dependents (which includes the dependencies to the sdk members)
+	// should have been mutated for the apex before the SDK requirements are set.
+	ctx.TopDown("SdkDepsMutator", sdkDepsMutator).Parallel()
+	ctx.BottomUp("SdkDepsReplaceMutator", sdkDepsReplaceMutator).Parallel()
+	ctx.TopDown("SdkRequirementCheck", sdkRequirementsMutator).Parallel()
+}
+
+type dependencyTag struct {
+	blueprint.BaseDependencyTag
+}
+
+// For dependencies from an in-development version of an SDK member to frozen versions of the same member
+// e.g. libfoo -> libfoo.mysdk.11 and libfoo.mysdk.12
+type sdkMemberVesionedDepTag struct {
+	dependencyTag
+	member  string
+	version string
+}
+
+// Step 1: create dependencies from an SDK module to its members.
+func memberMutator(mctx android.BottomUpMutatorContext) {
+	if s, ok := mctx.Module().(*sdk); ok {
+		for _, memberListProperty := range s.dynamicSdkMemberTypes.memberListProperties {
+			names := memberListProperty.getter(s.dynamicMemberTypeListProperties)
+			tag := memberListProperty.dependencyTag
+			memberListProperty.memberType.AddDependencies(mctx, tag, names)
+		}
+	}
+}
+
+// Step 2: record that dependencies of SDK modules are members of the SDK modules
+func memberDepsMutator(mctx android.TopDownMutatorContext) {
+	if s, ok := mctx.Module().(*sdk); ok {
+		mySdkRef := android.ParseSdkRef(mctx, mctx.ModuleName(), "name")
+		if s.snapshot() && mySdkRef.Unversioned() {
+			mctx.PropertyErrorf("name", "sdk_snapshot should be named as <name>@<version>. "+
+				"Did you manually modify Android.bp?")
+		}
+		if !s.snapshot() && !mySdkRef.Unversioned() {
+			mctx.PropertyErrorf("name", "sdk shouldn't be named as <name>@<version>.")
+		}
+		if mySdkRef.Version != "" && mySdkRef.Version != "current" {
+			if _, err := strconv.Atoi(mySdkRef.Version); err != nil {
+				mctx.PropertyErrorf("name", "version %q is neither a number nor \"current\"", mySdkRef.Version)
+			}
+		}
+
+		mctx.VisitDirectDeps(func(child android.Module) {
+			if member, ok := child.(android.SdkAware); ok {
+				member.MakeMemberOf(mySdkRef)
+			}
+		})
+	}
+}
+
+// Step 3: create dependencies from the unversioned SDK member to snapshot versions
+// of the same member. By having these dependencies, they are mutated for multiple Mainline modules
+// (apex and apk), each of which might want different sdks to be built with. For example, if both
+// apex A and B are referencing libfoo which is a member of sdk 'mysdk', the two APEXes can be
+// built with libfoo.mysdk.11 and libfoo.mysdk.12, respectively depending on which sdk they are
+// using.
+func memberInterVersionMutator(mctx android.BottomUpMutatorContext) {
+	if m, ok := mctx.Module().(android.SdkAware); ok && m.IsInAnySdk() {
+		if !m.ContainingSdk().Unversioned() {
+			memberName := m.MemberName()
+			tag := sdkMemberVesionedDepTag{member: memberName, version: m.ContainingSdk().Version}
+			mctx.AddReverseDependency(mctx.Module(), tag, memberName)
+		}
+	}
+}
+
+// Step 4: transitively ripple down the SDK requirements from the root modules like APEX to its
+// descendants
+func sdkDepsMutator(mctx android.TopDownMutatorContext) {
+	if m, ok := mctx.Module().(android.SdkAware); ok {
+		// Module types for Mainline modules (e.g. APEX) are expected to implement RequiredSdks()
+		// by reading its own properties like `uses_sdks`.
+		requiredSdks := m.RequiredSdks()
+		if len(requiredSdks) > 0 {
+			mctx.VisitDirectDeps(func(m android.Module) {
+				if dep, ok := m.(android.SdkAware); ok {
+					dep.BuildWithSdks(requiredSdks)
+				}
+			})
+		}
+	}
+}
+
+// Step 5: if libfoo.mysdk.11 is in the context where version 11 of mysdk is requested, the
+// versioned module is used instead of the un-versioned (in-development) module libfoo
+func sdkDepsReplaceMutator(mctx android.BottomUpMutatorContext) {
+	if m, ok := mctx.Module().(android.SdkAware); ok && m.IsInAnySdk() {
+		if sdk := m.ContainingSdk(); !sdk.Unversioned() {
+			if m.RequiredSdks().Contains(sdk) {
+				// Note that this replacement is done only for the modules that have the same
+				// variations as the current module. Since current module is already mutated for
+				// apex references in other APEXes are not affected by this replacement.
+				memberName := m.MemberName()
+				mctx.ReplaceDependencies(memberName)
+			}
+		}
+	}
+}
+
+// Step 6: ensure that the dependencies from outside of the APEX are all from the required SDKs
+func sdkRequirementsMutator(mctx android.TopDownMutatorContext) {
+	if m, ok := mctx.Module().(interface {
+		DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool
+		RequiredSdks() android.SdkRefs
+	}); ok {
+		requiredSdks := m.RequiredSdks()
+		if len(requiredSdks) == 0 {
+			return
+		}
+		mctx.VisitDirectDeps(func(dep android.Module) {
+			if mctx.OtherModuleDependencyTag(dep) == android.DefaultsDepTag {
+				// dependency to defaults is always okay
+				return
+			}
+
+			// If the dep is from outside of the APEX, but is not in any of the
+			// required SDKs, we know that the dep is a violation.
+			if sa, ok := dep.(android.SdkAware); ok {
+				if !m.DepIsInSameApex(mctx, dep) && !requiredSdks.Contains(sa.ContainingSdk()) {
+					mctx.ModuleErrorf("depends on %q (in SDK %q) that isn't part of the required SDKs: %v",
+						sa.Name(), sa.ContainingSdk(), requiredSdks)
+				}
+			}
+		})
+	}
+}
diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go
new file mode 100644
index 0000000..d376e59
--- /dev/null
+++ b/sdk/sdk_test.go
@@ -0,0 +1,201 @@
+// 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 sdk
+
+import (
+	"testing"
+)
+
+// Needed in an _test.go file in this package to ensure tests run correctly, particularly in IDE.
+func TestMain(m *testing.M) {
+	runTestWithBuildDir(m)
+}
+
+func TestDepNotInRequiredSdks(t *testing.T) {
+	testSdkError(t, `module "myjavalib".*depends on "otherlib".*that isn't part of the required SDKs:.*`, `
+		sdk {
+			name: "mysdk",
+			java_header_libs: ["sdkmember"],
+		}
+
+		sdk_snapshot {
+			name: "mysdk@1",
+			java_header_libs: ["sdkmember_mysdk_1"],
+		}
+
+		java_import {
+			name: "sdkmember",
+			prefer: false,
+			host_supported: true,
+		}
+
+		java_import {
+			name: "sdkmember_mysdk_1",
+			sdk_member_name: "sdkmember",
+			host_supported: true,
+		}
+
+		java_library {
+			name: "myjavalib",
+			srcs: ["Test.java"],
+			libs: [
+				"sdkmember",
+				"otherlib",
+			],
+			system_modules: "none",
+			sdk_version: "none",
+			compile_dex: true,
+			host_supported: true,
+		}
+
+		// this lib is no in mysdk
+		java_library {
+			name: "otherlib",
+			srcs: ["Test.java"],
+			system_modules: "none",
+			sdk_version: "none",
+			compile_dex: true,
+			host_supported: true,
+		}
+
+		apex {
+			name: "myapex",
+			java_libs: ["myjavalib"],
+			uses_sdks: ["mysdk@1"],
+			key: "myapex.key",
+			certificate: ":myapex.cert",
+		}
+	`)
+}
+
+// Ensure that prebuilt modules have the same effective visibility as the source
+// modules.
+func TestSnapshotVisibility(t *testing.T) {
+	packageBp := `
+		package {
+			default_visibility: ["//other/foo"],
+		}
+
+		sdk {
+			name: "mysdk",
+			visibility: [
+				"//other/foo",
+				// This short form will be replaced with //package:__subpackages__ in the
+				// generated sdk_snapshot.
+				":__subpackages__",
+			],
+			java_header_libs: [
+				"myjavalib",
+				"mypublicjavalib",
+				"mydefaultedjavalib",
+			],
+		}
+
+		java_library {
+			name: "myjavalib",
+			// Uses package default visibility
+			srcs: ["Test.java"],
+			system_modules: "none",
+			sdk_version: "none",
+		}
+
+		java_library {
+			name: "mypublicjavalib",
+      visibility: ["//visibility:public"],
+			srcs: ["Test.java"],
+			system_modules: "none",
+			sdk_version: "none",
+		}
+
+		java_defaults {
+			name: "myjavadefaults",
+			visibility: ["//other/bar"],
+		}
+
+		java_library {
+			name: "mydefaultedjavalib",
+			defaults: ["myjavadefaults"],
+			srcs: ["Test.java"],
+			system_modules: "none",
+			sdk_version: "none",
+		}
+	`
+
+	result := testSdkWithFs(t, ``,
+		map[string][]byte{
+			"package/Test.java":  nil,
+			"package/Android.bp": []byte(packageBp),
+		})
+
+	result.CheckSnapshot("mysdk", "android_common", "package",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+    name: "mysdk_myjavalib@current",
+    sdk_member_name: "myjavalib",
+    visibility: ["//other/foo:__pkg__"],
+    jars: ["java/myjavalib.jar"],
+}
+
+java_import {
+    name: "myjavalib",
+    prefer: false,
+    visibility: ["//other/foo:__pkg__"],
+    jars: ["java/myjavalib.jar"],
+}
+
+java_import {
+    name: "mysdk_mypublicjavalib@current",
+    sdk_member_name: "mypublicjavalib",
+    visibility: ["//visibility:public"],
+    jars: ["java/mypublicjavalib.jar"],
+}
+
+java_import {
+    name: "mypublicjavalib",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    jars: ["java/mypublicjavalib.jar"],
+}
+
+java_import {
+    name: "mysdk_mydefaultedjavalib@current",
+    sdk_member_name: "mydefaultedjavalib",
+    visibility: ["//other/bar:__pkg__"],
+    jars: ["java/mydefaultedjavalib.jar"],
+}
+
+java_import {
+    name: "mydefaultedjavalib",
+    prefer: false,
+    visibility: ["//other/bar:__pkg__"],
+    jars: ["java/mydefaultedjavalib.jar"],
+}
+
+sdk_snapshot {
+    name: "mysdk@current",
+    visibility: [
+        "//other/foo:__pkg__",
+        "//package:__subpackages__",
+    ],
+    java_header_libs: [
+        "mysdk_myjavalib@current",
+        "mysdk_mypublicjavalib@current",
+        "mysdk_mydefaultedjavalib@current",
+    ],
+}
+`))
+}
diff --git a/sdk/testing.go b/sdk/testing.go
new file mode 100644
index 0000000..8097889
--- /dev/null
+++ b/sdk/testing.go
@@ -0,0 +1,370 @@
+// 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 sdk
+
+import (
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"strings"
+	"testing"
+
+	"android/soong/android"
+	"android/soong/apex"
+	"android/soong/cc"
+	"android/soong/java"
+)
+
+func testSdkContext(bp string, fs map[string][]byte) (*android.TestContext, android.Config) {
+	bp = bp + `
+		apex_key {
+			name: "myapex.key",
+			public_key: "myapex.avbpubkey",
+			private_key: "myapex.pem",
+		}
+
+		android_app_certificate {
+			name: "myapex.cert",
+			certificate: "myapex",
+		}
+	` + cc.GatherRequiredDepsForTest(android.Android)
+
+	mockFS := map[string][]byte{
+		"build/make/target/product/security":         nil,
+		"apex_manifest.json":                         nil,
+		"system/sepolicy/apex/myapex-file_contexts":  nil,
+		"system/sepolicy/apex/myapex2-file_contexts": nil,
+		"myapex.avbpubkey":                           nil,
+		"myapex.pem":                                 nil,
+		"myapex.x509.pem":                            nil,
+		"myapex.pk8":                                 nil,
+	}
+
+	for k, v := range fs {
+		mockFS[k] = v
+	}
+
+	config := android.TestArchConfig(buildDir, nil, bp, mockFS)
+
+	ctx := android.NewTestArchContext()
+
+	// from android package
+	ctx.PreArchMutators(android.RegisterPackageRenamer)
+	ctx.PreArchMutators(android.RegisterVisibilityRuleChecker)
+	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
+	ctx.PreArchMutators(android.RegisterVisibilityRuleGatherer)
+	ctx.PostDepsMutators(android.RegisterVisibilityRuleEnforcer)
+
+	ctx.RegisterModuleType("package", android.PackageFactory)
+
+	// from java package
+	java.RegisterJavaBuildComponents(ctx)
+	java.RegisterAppBuildComponents(ctx)
+	java.RegisterStubsBuildComponents(ctx)
+
+	// from cc package
+	cc.RegisterRequiredBuildComponentsForTest(ctx)
+
+	// from apex package
+	ctx.RegisterModuleType("apex", apex.BundleFactory)
+	ctx.RegisterModuleType("apex_key", apex.ApexKeyFactory)
+	ctx.PostDepsMutators(apex.RegisterPostDepsMutators)
+
+	// from this package
+	ctx.RegisterModuleType("sdk", SdkModuleFactory)
+	ctx.RegisterModuleType("sdk_snapshot", SnapshotModuleFactory)
+	ctx.RegisterModuleType("module_exports", ModuleExportsFactory)
+	ctx.RegisterModuleType("module_exports_snapshot", ModuleExportsSnapshotsFactory)
+	ctx.PreDepsMutators(RegisterPreDepsMutators)
+	ctx.PostDepsMutators(RegisterPostDepsMutators)
+
+	ctx.Register(config)
+
+	return ctx, config
+}
+
+func testSdkWithFs(t *testing.T, bp string, fs map[string][]byte) *testSdkResult {
+	t.Helper()
+	ctx, config := testSdkContext(bp, fs)
+	_, errs := ctx.ParseBlueprintsFiles(".")
+	android.FailIfErrored(t, errs)
+	_, errs = ctx.PrepareBuildActions(config)
+	android.FailIfErrored(t, errs)
+	return &testSdkResult{
+		TestHelper: TestHelper{t: t},
+		ctx:        ctx,
+		config:     config,
+	}
+}
+
+func testSdkError(t *testing.T, pattern, bp string) {
+	t.Helper()
+	ctx, config := testSdkContext(bp, nil)
+	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+	if len(errs) > 0 {
+		android.FailIfNoMatchingErrors(t, pattern, errs)
+		return
+	}
+	_, errs = ctx.PrepareBuildActions(config)
+	if len(errs) > 0 {
+		android.FailIfNoMatchingErrors(t, pattern, errs)
+		return
+	}
+
+	t.Fatalf("missing expected error %q (0 errors are returned)", pattern)
+}
+
+func ensureListContains(t *testing.T, result []string, expected string) {
+	t.Helper()
+	if !android.InList(expected, result) {
+		t.Errorf("%q is not found in %v", expected, result)
+	}
+}
+
+func pathsToStrings(paths android.Paths) []string {
+	var ret []string
+	for _, p := range paths {
+		ret = append(ret, p.String())
+	}
+	return ret
+}
+
+// Provides general test support.
+type TestHelper struct {
+	t *testing.T
+}
+
+func (h *TestHelper) AssertStringEquals(message string, expected string, actual string) {
+	h.t.Helper()
+	if actual != expected {
+		h.t.Errorf("%s: expected %s, actual %s", message, expected, actual)
+	}
+}
+
+func (h *TestHelper) AssertTrimmedStringEquals(message string, expected string, actual string) {
+	h.t.Helper()
+	h.AssertStringEquals(message, strings.TrimSpace(expected), strings.TrimSpace(actual))
+}
+
+// Encapsulates result of processing an SDK definition. Provides support for
+// checking the state of the build structures.
+type testSdkResult struct {
+	TestHelper
+	ctx    *android.TestContext
+	config android.Config
+}
+
+// Analyse the sdk build rules to extract information about what it is doing.
+
+// e.g. find the src/dest pairs from each cp command, the various zip files
+// generated, etc.
+func (r *testSdkResult) getSdkSnapshotBuildInfo(sdk *sdk) *snapshotBuildInfo {
+	androidBpContents := strings.NewReplacer("\\n", "\n").Replace(sdk.GetAndroidBpContentsForTests())
+
+	info := &snapshotBuildInfo{
+		r:                 r,
+		androidBpContents: androidBpContents,
+	}
+
+	buildParams := sdk.BuildParamsForTests()
+	copyRules := &strings.Builder{}
+	for _, bp := range buildParams {
+		switch bp.Rule.String() {
+		case android.Cp.String():
+			// Get source relative to build directory.
+			src := android.NormalizePathForTesting(bp.Input)
+			// Get destination relative to the snapshot root
+			dest := bp.Output.Rel()
+			_, _ = fmt.Fprintf(copyRules, "%s -> %s\n", src, dest)
+			info.snapshotContents = append(info.snapshotContents, dest)
+
+		case repackageZip.String():
+			// Add the destdir to the snapshot contents as that is effectively where
+			// the content of the repackaged zip is copied.
+			dest := bp.Args["destdir"]
+			info.snapshotContents = append(info.snapshotContents, dest)
+
+		case zipFiles.String():
+			// This could be an intermediate zip file and not the actual output zip.
+			// In that case this will be overridden when the rule to merge the zips
+			// is processed.
+			info.outputZip = android.NormalizePathForTesting(bp.Output)
+
+		case mergeZips.String():
+			// Copy the current outputZip to the intermediateZip.
+			info.intermediateZip = info.outputZip
+			mergeInput := android.NormalizePathForTesting(bp.Input)
+			if info.intermediateZip != mergeInput {
+				r.t.Errorf("Expected intermediate zip %s to be an input to merge zips but found %s instead",
+					info.intermediateZip, mergeInput)
+			}
+
+			// Override output zip (which was actually the intermediate zip file) with the actual
+			// output zip.
+			info.outputZip = android.NormalizePathForTesting(bp.Output)
+
+			// Save the zips to be merged into the intermediate zip.
+			info.mergeZips = android.NormalizePathsForTesting(bp.Inputs)
+		}
+	}
+
+	info.copyRules = copyRules.String()
+
+	return info
+}
+
+func (r *testSdkResult) Module(name string, variant string) android.Module {
+	return r.ctx.ModuleForTests(name, variant).Module()
+}
+
+func (r *testSdkResult) ModuleForTests(name string, variant string) android.TestingModule {
+	return r.ctx.ModuleForTests(name, variant)
+}
+
+// Check the snapshot build rules.
+//
+// Takes a list of functions which check different facets of the snapshot build rules.
+// Allows each test to customize what is checked without duplicating lots of code
+// or proliferating check methods of different flavors.
+func (r *testSdkResult) CheckSnapshot(name string, variant string, dir string, checkers ...snapshotBuildInfoChecker) {
+	r.t.Helper()
+
+	sdk := r.Module(name, variant).(*sdk)
+
+	snapshotBuildInfo := r.getSdkSnapshotBuildInfo(sdk)
+
+	// Check state of the snapshot build.
+	for _, checker := range checkers {
+		checker(snapshotBuildInfo)
+	}
+
+	// Make sure that the generated zip file is in the correct place.
+	actual := snapshotBuildInfo.outputZip
+	if dir != "" {
+		dir = filepath.Clean(dir) + "/"
+	}
+	r.AssertStringEquals("Snapshot zip file in wrong place",
+		fmt.Sprintf(".intermediates/%s%s/%s/%s-current.zip", dir, name, variant, name), actual)
+
+	// Populate a mock filesystem with the files that would have been copied by
+	// the rules.
+	fs := make(map[string][]byte)
+	for _, dest := range snapshotBuildInfo.snapshotContents {
+		fs[dest] = nil
+	}
+
+	// Process the generated bp file to make sure it is valid.
+	testSdkWithFs(r.t, snapshotBuildInfo.androidBpContents, fs)
+}
+
+type snapshotBuildInfoChecker func(info *snapshotBuildInfo)
+
+// Check that the snapshot's generated Android.bp is correct.
+//
+// Both the expected and actual string are both trimmed before comparing.
+func checkAndroidBpContents(expected string) snapshotBuildInfoChecker {
+	return func(info *snapshotBuildInfo) {
+		info.r.t.Helper()
+		info.r.AssertTrimmedStringEquals("Android.bp contents do not match", expected, info.androidBpContents)
+	}
+}
+
+// Check that the snapshot's copy rules are correct.
+//
+// The copy rules are formatted as <src> -> <dest>, one per line and then compared
+// to the supplied expected string. Both the expected and actual string are trimmed
+// before comparing.
+func checkAllCopyRules(expected string) snapshotBuildInfoChecker {
+	return func(info *snapshotBuildInfo) {
+		info.r.t.Helper()
+		info.r.AssertTrimmedStringEquals("Incorrect copy rules", expected, info.copyRules)
+	}
+}
+
+// Check that the specified path is in the list of zips to merge with the intermediate zip.
+func checkMergeZip(expected string) snapshotBuildInfoChecker {
+	return func(info *snapshotBuildInfo) {
+		info.r.t.Helper()
+		if info.intermediateZip == "" {
+			info.r.t.Errorf("No intermediate zip file was created")
+		}
+		ensureListContains(info.r.t, info.mergeZips, expected)
+	}
+}
+
+// Encapsulates information about the snapshot build structure in order to insulate tests from
+// knowing too much about internal structures.
+//
+// All source/input paths are relative either the build directory. All dest/output paths are
+// relative to the snapshot root directory.
+type snapshotBuildInfo struct {
+	r *testSdkResult
+
+	// The contents of the generated Android.bp file
+	androidBpContents string
+
+	// The paths, relative to the snapshot root, of all files and directories copied into the
+	// snapshot.
+	snapshotContents []string
+
+	// A formatted representation of the src/dest pairs, one pair per line, of the format
+	// src -> dest
+	copyRules string
+
+	// The path to the intermediate zip, which is a zip created from the source files copied
+	// into the snapshot directory and which will be merged with other zips to form the final output.
+	// Is am empty string if there is no intermediate zip because there are no zips to merge in.
+	intermediateZip string
+
+	// The paths to the zips to merge into the output zip, does not include the intermediate
+	// zip.
+	mergeZips []string
+
+	// The final output zip.
+	outputZip string
+}
+
+var buildDir string
+
+func setUp() {
+	var err error
+	buildDir, err = ioutil.TempDir("", "soong_sdk_test")
+	if err != nil {
+		panic(err)
+	}
+}
+
+func tearDown() {
+	_ = os.RemoveAll(buildDir)
+}
+
+func runTestWithBuildDir(m *testing.M) {
+	run := func() int {
+		setUp()
+		defer tearDown()
+
+		return m.Run()
+	}
+
+	os.Exit(run())
+}
+
+func SkipIfNotLinux(t *testing.T) {
+	t.Helper()
+	if android.BuildOs != android.Linux {
+		t.Skipf("Skipping as sdk snapshot generation is only supported on %s not %s", android.Linux, android.BuildOs)
+	}
+}
diff --git a/sdk/update.go b/sdk/update.go
new file mode 100644
index 0000000..5bc3b83
--- /dev/null
+++ b/sdk/update.go
@@ -0,0 +1,457 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package sdk
+
+import (
+	"fmt"
+	"reflect"
+	"strings"
+
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+
+	"android/soong/android"
+)
+
+var pctx = android.NewPackageContext("android/soong/sdk")
+
+var (
+	repackageZip = pctx.AndroidStaticRule("SnapshotRepackageZip",
+		blueprint.RuleParams{
+			Command: `${config.Zip2ZipCmd} -i $in -o $out -x META-INF/**/* "**/*:$destdir"`,
+			CommandDeps: []string{
+				"${config.Zip2ZipCmd}",
+			},
+		},
+		"destdir")
+
+	zipFiles = pctx.AndroidStaticRule("SnapshotZipFiles",
+		blueprint.RuleParams{
+			Command: `${config.SoongZipCmd} -C $basedir -l $out.rsp -o $out`,
+			CommandDeps: []string{
+				"${config.SoongZipCmd}",
+			},
+			Rspfile:        "$out.rsp",
+			RspfileContent: "$in",
+		},
+		"basedir")
+
+	mergeZips = pctx.AndroidStaticRule("SnapshotMergeZips",
+		blueprint.RuleParams{
+			Command: `${config.MergeZipsCmd} $out $in`,
+			CommandDeps: []string{
+				"${config.MergeZipsCmd}",
+			},
+		})
+)
+
+type generatedContents struct {
+	content     strings.Builder
+	indentLevel int
+}
+
+// generatedFile abstracts operations for writing contents into a file and emit a build rule
+// for the file.
+type generatedFile struct {
+	generatedContents
+	path android.OutputPath
+}
+
+func newGeneratedFile(ctx android.ModuleContext, path ...string) *generatedFile {
+	return &generatedFile{
+		path: android.PathForModuleOut(ctx, path...).OutputPath,
+	}
+}
+
+func (gc *generatedContents) Indent() {
+	gc.indentLevel++
+}
+
+func (gc *generatedContents) Dedent() {
+	gc.indentLevel--
+}
+
+func (gc *generatedContents) Printfln(format string, args ...interface{}) {
+	// ninja consumes newline characters in rspfile_content. Prevent it by
+	// escaping the backslash in the newline character. The extra backslash
+	// is removed when the rspfile is written to the actual script file
+	fmt.Fprintf(&(gc.content), strings.Repeat("    ", gc.indentLevel)+format+"\\n", args...)
+}
+
+func (gf *generatedFile) build(pctx android.PackageContext, ctx android.BuilderContext, implicits android.Paths) {
+	rb := android.NewRuleBuilder()
+	// convert \\n to \n
+	rb.Command().
+		Implicits(implicits).
+		Text("echo").Text(proptools.ShellEscape(gf.content.String())).
+		Text("| sed 's/\\\\n/\\n/g' >").Output(gf.path)
+	rb.Command().
+		Text("chmod a+x").Output(gf.path)
+	rb.Build(pctx, ctx, gf.path.Base(), "Build "+gf.path.Base())
+}
+
+// Collect all the members.
+//
+// The members are first grouped by type and then grouped by name. The order of
+// the types is the order they are referenced in android.SdkMemberTypes. The
+// names are in order in which the dependencies were added.
+func (s *sdk) collectMembers(ctx android.ModuleContext) []*sdkMember {
+	byType := make(map[android.SdkMemberType][]*sdkMember)
+	byName := make(map[string]*sdkMember)
+
+	ctx.VisitDirectDeps(func(m android.Module) {
+		tag := ctx.OtherModuleDependencyTag(m)
+		if memberTag, ok := tag.(*sdkMemberDependencyTag); ok {
+			memberType := memberTag.memberType
+
+			// Make sure that the resolved module is allowed in the member list property.
+			if !memberType.IsInstance(m) {
+				ctx.ModuleErrorf("module %q is not valid in property %s", ctx.OtherModuleName(m), memberType.SdkPropertyName())
+			}
+
+			name := ctx.OtherModuleName(m)
+
+			member := byName[name]
+			if member == nil {
+				member = &sdkMember{memberType: memberType, name: name}
+				byName[name] = member
+				byType[memberType] = append(byType[memberType], member)
+			}
+
+			member.variants = append(member.variants, m.(android.SdkAware))
+		}
+	})
+
+	var members []*sdkMember
+	for _, memberListProperty := range s.dynamicSdkMemberTypes.memberListProperties {
+		membersOfType := byType[memberListProperty.memberType]
+		members = append(members, membersOfType...)
+	}
+
+	return members
+}
+
+// SDK directory structure
+// <sdk_root>/
+//     Android.bp   : definition of a 'sdk' module is here. This is a hand-made one.
+//     <api_ver>/   : below this directory are all auto-generated
+//         Android.bp   : definition of 'sdk_snapshot' module is here
+//         aidl/
+//            frameworks/base/core/..../IFoo.aidl   : an exported AIDL file
+//         java/
+//            <module_name>.jar    : the stub jar for a java library 'module_name'
+//         include/
+//            bionic/libc/include/stdlib.h   : an exported header file
+//         include_gen/
+//            <module_name>/com/android/.../IFoo.h : a generated header file
+//         <arch>/include/   : arch-specific exported headers
+//         <arch>/include_gen/   : arch-specific generated headers
+//         <arch>/lib/
+//            libFoo.so   : a stub library
+
+// A name that uniquely identifies a prebuilt SDK member for a version of SDK snapshot
+// This isn't visible to users, so could be changed in future.
+func versionedSdkMemberName(ctx android.ModuleContext, memberName string, version string) string {
+	return ctx.ModuleName() + "_" + memberName + string(android.SdkVersionSeparator) + version
+}
+
+// buildSnapshot is the main function in this source file. It creates rules to copy
+// the contents (header files, stub libraries, etc) into the zip file.
+func (s *sdk) buildSnapshot(ctx android.ModuleContext) android.OutputPath {
+	snapshotDir := android.PathForModuleOut(ctx, "snapshot")
+
+	bp := newGeneratedFile(ctx, "snapshot", "Android.bp")
+
+	bpFile := &bpFile{
+		modules: make(map[string]*bpModule),
+	}
+
+	builder := &snapshotBuilder{
+		ctx:             ctx,
+		sdk:             s,
+		version:         "current",
+		snapshotDir:     snapshotDir.OutputPath,
+		copies:          make(map[string]string),
+		filesToZip:      []android.Path{bp.path},
+		bpFile:          bpFile,
+		prebuiltModules: make(map[string]*bpModule),
+	}
+	s.builderForTests = builder
+
+	for _, member := range s.collectMembers(ctx) {
+		member.memberType.BuildSnapshot(ctx, builder, member)
+	}
+
+	for _, unversioned := range builder.prebuiltOrder {
+		// Copy the unversioned module so it can be modified to make it versioned.
+		versioned := unversioned.copy()
+		name := versioned.properties["name"].(string)
+		versioned.setProperty("name", builder.versionedSdkMemberName(name))
+		versioned.insertAfter("name", "sdk_member_name", name)
+		bpFile.AddModule(versioned)
+
+		// Set prefer: false - this is not strictly required as that is the default.
+		unversioned.insertAfter("name", "prefer", false)
+		bpFile.AddModule(unversioned)
+	}
+
+	// Create the snapshot module.
+	snapshotName := ctx.ModuleName() + string(android.SdkVersionSeparator) + builder.version
+	var snapshotModuleType string
+	if s.properties.Module_exports {
+		snapshotModuleType = "module_exports_snapshot"
+	} else {
+		snapshotModuleType = "sdk_snapshot"
+	}
+	snapshotModule := bpFile.newModule(snapshotModuleType)
+	snapshotModule.AddProperty("name", snapshotName)
+
+	// Make sure that the snapshot has the same visibility as the sdk.
+	visibility := android.EffectiveVisibilityRules(ctx, s)
+	if len(visibility) != 0 {
+		snapshotModule.AddProperty("visibility", visibility)
+	}
+
+	addHostDeviceSupportedProperties(&s.ModuleBase, snapshotModule)
+	for _, memberListProperty := range s.dynamicSdkMemberTypes.memberListProperties {
+		names := memberListProperty.getter(s.dynamicMemberTypeListProperties)
+		if len(names) > 0 {
+			snapshotModule.AddProperty(memberListProperty.propertyName(), builder.versionedSdkMemberNames(names))
+		}
+	}
+	bpFile.AddModule(snapshotModule)
+
+	// generate Android.bp
+	bp = newGeneratedFile(ctx, "snapshot", "Android.bp")
+	generateBpContents(&bp.generatedContents, bpFile)
+
+	bp.build(pctx, ctx, nil)
+
+	filesToZip := builder.filesToZip
+
+	// zip them all
+	outputZipFile := android.PathForModuleOut(ctx, ctx.ModuleName()+"-current.zip").OutputPath
+	outputDesc := "Building snapshot for " + ctx.ModuleName()
+
+	// If there are no zips to merge then generate the output zip directly.
+	// Otherwise, generate an intermediate zip file into which other zips can be
+	// merged.
+	var zipFile android.OutputPath
+	var desc string
+	if len(builder.zipsToMerge) == 0 {
+		zipFile = outputZipFile
+		desc = outputDesc
+	} else {
+		zipFile = android.PathForModuleOut(ctx, ctx.ModuleName()+"-current.unmerged.zip").OutputPath
+		desc = "Building intermediate snapshot for " + ctx.ModuleName()
+	}
+
+	ctx.Build(pctx, android.BuildParams{
+		Description: desc,
+		Rule:        zipFiles,
+		Inputs:      filesToZip,
+		Output:      zipFile,
+		Args: map[string]string{
+			"basedir": builder.snapshotDir.String(),
+		},
+	})
+
+	if len(builder.zipsToMerge) != 0 {
+		ctx.Build(pctx, android.BuildParams{
+			Description: outputDesc,
+			Rule:        mergeZips,
+			Input:       zipFile,
+			Inputs:      builder.zipsToMerge,
+			Output:      outputZipFile,
+		})
+	}
+
+	return outputZipFile
+}
+
+func generateBpContents(contents *generatedContents, bpFile *bpFile) {
+	contents.Printfln("// This is auto-generated. DO NOT EDIT.")
+	for _, bpModule := range bpFile.order {
+		contents.Printfln("")
+		contents.Printfln("%s {", bpModule.moduleType)
+		outputPropertySet(contents, &bpModule.bpPropertySet)
+		contents.Printfln("}")
+	}
+}
+
+func outputPropertySet(contents *generatedContents, set *bpPropertySet) {
+	contents.Indent()
+	for _, name := range set.order {
+		value := set.properties[name]
+
+		reflectedValue := reflect.ValueOf(value)
+		t := reflectedValue.Type()
+
+		kind := t.Kind()
+		switch kind {
+		case reflect.Slice:
+			length := reflectedValue.Len()
+			if length > 1 {
+				contents.Printfln("%s: [", name)
+				contents.Indent()
+				for i := 0; i < length; i = i + 1 {
+					contents.Printfln("%q,", reflectedValue.Index(i).Interface())
+				}
+				contents.Dedent()
+				contents.Printfln("],")
+			} else if length == 0 {
+				contents.Printfln("%s: [],", name)
+			} else {
+				contents.Printfln("%s: [%q],", name, reflectedValue.Index(0).Interface())
+			}
+		case reflect.Bool:
+			contents.Printfln("%s: %t,", name, reflectedValue.Bool())
+
+		case reflect.Ptr:
+			contents.Printfln("%s: {", name)
+			outputPropertySet(contents, reflectedValue.Interface().(*bpPropertySet))
+			contents.Printfln("},")
+
+		default:
+			contents.Printfln("%s: %q,", name, value)
+		}
+	}
+	contents.Dedent()
+}
+
+func (s *sdk) GetAndroidBpContentsForTests() string {
+	contents := &generatedContents{}
+	generateBpContents(contents, s.builderForTests.bpFile)
+	return contents.content.String()
+}
+
+type snapshotBuilder struct {
+	ctx         android.ModuleContext
+	sdk         *sdk
+	version     string
+	snapshotDir android.OutputPath
+	bpFile      *bpFile
+
+	// Map from destination to source of each copy - used to eliminate duplicates and
+	// detect conflicts.
+	copies map[string]string
+
+	filesToZip  android.Paths
+	zipsToMerge android.Paths
+
+	prebuiltModules map[string]*bpModule
+	prebuiltOrder   []*bpModule
+}
+
+func (s *snapshotBuilder) CopyToSnapshot(src android.Path, dest string) {
+	if existing, ok := s.copies[dest]; ok {
+		if existing != src.String() {
+			s.ctx.ModuleErrorf("conflicting copy, %s copied from both %s and %s", dest, existing, src)
+			return
+		}
+	} else {
+		path := s.snapshotDir.Join(s.ctx, dest)
+		s.ctx.Build(pctx, android.BuildParams{
+			Rule:   android.Cp,
+			Input:  src,
+			Output: path,
+		})
+		s.filesToZip = append(s.filesToZip, path)
+
+		s.copies[dest] = src.String()
+	}
+}
+
+func (s *snapshotBuilder) UnzipToSnapshot(zipPath android.Path, destDir string) {
+	ctx := s.ctx
+
+	// Repackage the zip file so that the entries are in the destDir directory.
+	// This will allow the zip file to be merged into the snapshot.
+	tmpZipPath := android.PathForModuleOut(ctx, "tmp", destDir+".zip").OutputPath
+
+	ctx.Build(pctx, android.BuildParams{
+		Description: "Repackaging zip file " + destDir + " for snapshot " + ctx.ModuleName(),
+		Rule:        repackageZip,
+		Input:       zipPath,
+		Output:      tmpZipPath,
+		Args: map[string]string{
+			"destdir": destDir,
+		},
+	})
+
+	// Add the repackaged zip file to the files to merge.
+	s.zipsToMerge = append(s.zipsToMerge, tmpZipPath)
+}
+
+func (s *snapshotBuilder) AddPrebuiltModule(member android.SdkMember, moduleType string) android.BpModule {
+	name := member.Name()
+	if s.prebuiltModules[name] != nil {
+		panic(fmt.Sprintf("Duplicate module detected, module %s has already been added", name))
+	}
+
+	m := s.bpFile.newModule(moduleType)
+	m.AddProperty("name", name)
+
+	// Extract visibility information from a member variant. All variants have the same
+	// visibility so it doesn't matter which one is used.
+	visibility := android.EffectiveVisibilityRules(s.ctx, member.Variants()[0])
+	if len(visibility) != 0 {
+		m.AddProperty("visibility", visibility)
+	}
+
+	addHostDeviceSupportedProperties(&s.sdk.ModuleBase, m)
+
+	s.prebuiltModules[name] = m
+	s.prebuiltOrder = append(s.prebuiltOrder, m)
+	return m
+}
+
+func addHostDeviceSupportedProperties(module *android.ModuleBase, bpModule *bpModule) {
+	if !module.DeviceSupported() {
+		bpModule.AddProperty("device_supported", false)
+	}
+	if module.HostSupported() {
+		bpModule.AddProperty("host_supported", true)
+	}
+}
+
+// Get a versioned name appropriate for the SDK snapshot version being taken.
+func (s *snapshotBuilder) versionedSdkMemberName(unversionedName string) string {
+	return versionedSdkMemberName(s.ctx, unversionedName, s.version)
+}
+
+func (s *snapshotBuilder) versionedSdkMemberNames(members []string) []string {
+	var references []string = nil
+	for _, m := range members {
+		references = append(references, s.versionedSdkMemberName(m))
+	}
+	return references
+}
+
+var _ android.SdkMember = (*sdkMember)(nil)
+
+type sdkMember struct {
+	memberType android.SdkMemberType
+	name       string
+	variants   []android.SdkAware
+}
+
+func (m *sdkMember) Name() string {
+	return m.name
+}
+
+func (m *sdkMember) Variants() []android.SdkAware {
+	return m.variants
+}
diff --git a/sysprop/sysprop_library.go b/sysprop/sysprop_library.go
index 86061c6..ce404f8 100644
--- a/sysprop/sysprop_library.go
+++ b/sysprop/sysprop_library.go
@@ -15,12 +15,16 @@
 package sysprop
 
 import (
-	"android/soong/android"
-	"android/soong/cc"
-	"android/soong/java"
+	"fmt"
+	"io"
+	"path"
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
+
+	"android/soong/android"
+	"android/soong/cc"
+	"android/soong/java"
 )
 
 type dependencyTag struct {
@@ -28,11 +32,96 @@
 	name string
 }
 
-type syspropLibrary struct {
-	java.SdkLibrary
+type syspropGenProperties struct {
+	Srcs  []string `android:"path"`
+	Scope string
+	Name  *string
+}
 
-	commonProperties         commonProperties
-	syspropLibraryProperties syspropLibraryProperties
+type syspropJavaGenRule struct {
+	android.ModuleBase
+
+	properties syspropGenProperties
+
+	genSrcjars android.Paths
+}
+
+var _ android.OutputFileProducer = (*syspropJavaGenRule)(nil)
+
+var (
+	syspropJava = pctx.AndroidStaticRule("syspropJava",
+		blueprint.RuleParams{
+			Command: `rm -rf $out.tmp && mkdir -p $out.tmp && ` +
+				`$syspropJavaCmd --scope $scope --java-output-dir $out.tmp $in && ` +
+				`$soongZipCmd -jar -o $out -C $out.tmp -D $out.tmp && rm -rf $out.tmp`,
+			CommandDeps: []string{
+				"$syspropJavaCmd",
+				"$soongZipCmd",
+			},
+		}, "scope")
+)
+
+func init() {
+	pctx.HostBinToolVariable("soongZipCmd", "soong_zip")
+	pctx.HostBinToolVariable("syspropJavaCmd", "sysprop_java")
+
+	android.PreArchMutators(func(ctx android.RegisterMutatorsContext) {
+		ctx.BottomUp("sysprop_deps", syspropDepsMutator).Parallel()
+	})
+}
+
+func (g *syspropJavaGenRule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	var checkApiFileTimeStamp android.WritablePath
+
+	ctx.VisitDirectDeps(func(dep android.Module) {
+		if m, ok := dep.(*syspropLibrary); ok {
+			checkApiFileTimeStamp = m.checkApiFileTimeStamp
+		}
+	})
+
+	for _, syspropFile := range android.PathsForModuleSrc(ctx, g.properties.Srcs) {
+		srcJarFile := android.GenPathWithExt(ctx, "sysprop", syspropFile, "srcjar")
+
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        syspropJava,
+			Description: "sysprop_java " + syspropFile.Rel(),
+			Output:      srcJarFile,
+			Input:       syspropFile,
+			Implicit:    checkApiFileTimeStamp,
+			Args: map[string]string{
+				"scope": g.properties.Scope,
+			},
+		})
+
+		g.genSrcjars = append(g.genSrcjars, srcJarFile)
+	}
+}
+
+func (g *syspropJavaGenRule) OutputFiles(tag string) (android.Paths, error) {
+	switch tag {
+	case "":
+		return g.genSrcjars, nil
+	default:
+		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+	}
+}
+
+func syspropJavaGenFactory() android.Module {
+	g := &syspropJavaGenRule{}
+	g.AddProperties(&g.properties)
+	android.InitAndroidModule(g)
+	return g
+}
+
+type syspropLibrary struct {
+	android.ModuleBase
+
+	properties syspropLibraryProperties
+
+	checkApiFileTimeStamp android.WritablePath
+	latestApiFile         android.Path
+	currentApiFile        android.Path
+	dumpedApiFile         android.WritablePath
 }
 
 type syspropLibraryProperties struct {
@@ -42,17 +131,25 @@
 
 	// list of package names that will be documented and publicized as API
 	Api_packages []string
-}
 
-type commonProperties struct {
-	Srcs               []string
-	Recovery           *bool
+	// If set to true, allow this module to be dexed and installed on devices.
+	Installable *bool
+
+	// Make this module available when building for recovery
 	Recovery_available *bool
-	Vendor_available   *bool
+
+	// Make this module available when building for vendor
+	Vendor_available *bool
+
+	// list of .sysprop files which defines the properties.
+	Srcs []string `android:"path"`
+
+	// Whether public stub exists or not.
+	Public_stub *bool `blueprint:"mutated"`
 }
 
 var (
-	Bool         = proptools.Bool
+	pctx         = android.NewPackageContext("android/soong/sysprop")
 	syspropCcTag = dependencyTag{name: "syspropCc"}
 )
 
@@ -60,80 +157,307 @@
 	android.RegisterModuleType("sysprop_library", syspropLibraryFactory)
 }
 
-func (m *syspropLibrary) CcModuleName() string {
-	return "lib" + m.Name()
+func (m *syspropLibrary) Name() string {
+	return m.BaseModuleName() + "_sysprop_library"
 }
 
+func (m *syspropLibrary) Owner() string {
+	return m.properties.Property_owner
+}
+
+func (m *syspropLibrary) CcModuleName() string {
+	return "lib" + m.BaseModuleName()
+}
+
+func (m *syspropLibrary) JavaPublicStubName() string {
+	if proptools.Bool(m.properties.Public_stub) {
+		return m.BaseModuleName() + "_public"
+	}
+	return ""
+}
+
+func (m *syspropLibrary) javaGenModuleName() string {
+	return m.BaseModuleName() + "_java_gen"
+}
+
+func (m *syspropLibrary) javaGenPublicStubName() string {
+	return m.BaseModuleName() + "_java_gen_public"
+}
+
+func (m *syspropLibrary) BaseModuleName() string {
+	return m.ModuleBase.Name()
+}
+
+func (m *syspropLibrary) HasPublicStub() bool {
+	return proptools.Bool(m.properties.Public_stub)
+}
+
+func (m *syspropLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	baseModuleName := m.BaseModuleName()
+
+	for _, syspropFile := range android.PathsForModuleSrc(ctx, m.properties.Srcs) {
+		if syspropFile.Ext() != ".sysprop" {
+			ctx.PropertyErrorf("srcs", "srcs contains non-sysprop file %q", syspropFile.String())
+		}
+	}
+
+	if ctx.Failed() {
+		return
+	}
+
+	m.currentApiFile = android.PathForSource(ctx, ctx.ModuleDir(), "api", baseModuleName+"-current.txt")
+	m.latestApiFile = android.PathForSource(ctx, ctx.ModuleDir(), "api", baseModuleName+"-latest.txt")
+
+	// dump API rule
+	rule := android.NewRuleBuilder()
+	m.dumpedApiFile = android.PathForModuleOut(ctx, "api-dump.txt")
+	rule.Command().
+		BuiltTool(ctx, "sysprop_api_dump").
+		Output(m.dumpedApiFile).
+		Inputs(android.PathsForModuleSrc(ctx, m.properties.Srcs))
+	rule.Build(pctx, ctx, baseModuleName+"_api_dump", baseModuleName+" api dump")
+
+	// check API rule
+	rule = android.NewRuleBuilder()
+
+	// 1. current.txt <-> api_dump.txt
+	msg := fmt.Sprintf(`\n******************************\n`+
+		`API of sysprop_library %s doesn't match with current.txt\n`+
+		`Please update current.txt by:\n`+
+		`m %s-dump-api && rm -rf %q && cp -f %q %q\n`+
+		`******************************\n`, baseModuleName, baseModuleName,
+		m.currentApiFile.String(), m.dumpedApiFile.String(), m.currentApiFile.String())
+
+	rule.Command().
+		Text("( cmp").Flag("-s").
+		Input(m.dumpedApiFile).
+		Input(m.currentApiFile).
+		Text("|| ( echo").Flag("-e").
+		Flag(`"` + msg + `"`).
+		Text("; exit 38) )")
+
+	// 2. current.txt <-> latest.txt
+	msg = fmt.Sprintf(`\n******************************\n`+
+		`API of sysprop_library %s doesn't match with latest version\n`+
+		`Please fix the breakage and rebuild.\n`+
+		`******************************\n`, baseModuleName)
+
+	rule.Command().
+		Text("( ").
+		BuiltTool(ctx, "sysprop_api_checker").
+		Input(m.latestApiFile).
+		Input(m.currentApiFile).
+		Text(" || ( echo").Flag("-e").
+		Flag(`"` + msg + `"`).
+		Text("; exit 38) )")
+
+	m.checkApiFileTimeStamp = android.PathForModuleOut(ctx, "check_api.timestamp")
+
+	rule.Command().
+		Text("touch").
+		Output(m.checkApiFileTimeStamp)
+
+	rule.Build(pctx, ctx, baseModuleName+"_check_api", baseModuleName+" check api")
+}
+
+func (m *syspropLibrary) AndroidMk() android.AndroidMkData {
+	return android.AndroidMkData{
+		Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
+			// sysprop_library module itself is defined as a FAKE module to perform API check.
+			// Actual implementation libraries are created on LoadHookMutator
+			fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
+			fmt.Fprintf(w, "LOCAL_MODULE := %s\n", m.Name())
+			fmt.Fprintf(w, "LOCAL_MODULE_CLASS := FAKE\n")
+			fmt.Fprintf(w, "LOCAL_MODULE_TAGS := optional\n")
+			fmt.Fprintf(w, "include $(BUILD_SYSTEM)/base_rules.mk\n\n")
+			fmt.Fprintf(w, "$(LOCAL_BUILT_MODULE): %s\n", m.checkApiFileTimeStamp.String())
+			fmt.Fprintf(w, "\ttouch $@\n\n")
+			fmt.Fprintf(w, ".PHONY: %s-check-api %s-dump-api\n\n", name, name)
+
+			// dump API rule
+			fmt.Fprintf(w, "%s-dump-api: %s\n\n", name, m.dumpedApiFile.String())
+
+			// check API rule
+			fmt.Fprintf(w, "%s-check-api: %s\n\n", name, m.checkApiFileTimeStamp.String())
+		}}
+}
+
+// sysprop_library creates schematized APIs from sysprop description files (.sysprop).
+// Both Java and C++ modules can link against sysprop_library, and API stability check
+// against latest APIs (see build/soong/scripts/freeze-sysprop-api-files.sh)
+// is performed.
 func syspropLibraryFactory() android.Module {
 	m := &syspropLibrary{}
 
 	m.AddProperties(
-		&m.commonProperties,
-		&m.syspropLibraryProperties,
+		&m.properties,
 	)
-	m.InitSdkLibraryProperties()
-	m.SetNoDist()
-	android.InitAndroidMultiTargetsArchModule(m, android.DeviceSupported, "common")
+	android.InitAndroidModule(m)
 	android.AddLoadHook(m, func(ctx android.LoadHookContext) { syspropLibraryHook(ctx, m) })
-	android.AddLoadHook(m, func(ctx android.LoadHookContext) { m.SdkLibrary.CreateInternalModules(ctx) })
 	return m
 }
 
+type ccLibraryProperties struct {
+	Name             *string
+	Srcs             []string
+	Soc_specific     *bool
+	Device_specific  *bool
+	Product_specific *bool
+	Sysprop          struct {
+		Platform *bool
+	}
+	Header_libs        []string
+	Shared_libs        []string
+	Required           []string
+	Recovery           *bool
+	Recovery_available *bool
+	Vendor_available   *bool
+}
+
+type javaLibraryProperties struct {
+	Name             *string
+	Srcs             []string
+	Soc_specific     *bool
+	Device_specific  *bool
+	Product_specific *bool
+	Required         []string
+	Sdk_version      *string
+	Installable      *bool
+	Libs             []string
+	Stem             *string
+}
+
 func syspropLibraryHook(ctx android.LoadHookContext, m *syspropLibrary) {
-	if len(m.commonProperties.Srcs) == 0 {
+	if len(m.properties.Srcs) == 0 {
 		ctx.PropertyErrorf("srcs", "sysprop_library must specify srcs")
 	}
 
-	if len(m.syspropLibraryProperties.Api_packages) == 0 {
-		ctx.PropertyErrorf("api_packages", "sysprop_library must specify api_packages")
+	missing_api := false
+
+	for _, txt := range []string{"-current.txt", "-latest.txt"} {
+		path := path.Join(ctx.ModuleDir(), "api", m.BaseModuleName()+txt)
+		file := android.ExistentPathForSource(ctx, path)
+		if !file.Valid() {
+			ctx.ModuleErrorf("API file %#v doesn't exist", path)
+			missing_api = true
+		}
 	}
 
-	socSpecific := ctx.SocSpecific()
-	deviceSpecific := ctx.DeviceSpecific()
-	productSpecific := ctx.ProductSpecific()
+	if missing_api {
+		script := "build/soong/scripts/gen-sysprop-api-files.sh"
+		p := android.ExistentPathForSource(ctx, script)
 
-	owner := m.syspropLibraryProperties.Property_owner
+		if !p.Valid() {
+			panic(fmt.Sprintf("script file %s doesn't exist", script))
+		}
 
-	switch owner {
+		ctx.ModuleErrorf("One or more api files are missing. "+
+			"You can create them by:\n"+
+			"%s %q %q", script, ctx.ModuleDir(), m.BaseModuleName())
+		return
+	}
+
+	// ctx's Platform or Specific functions represent where this sysprop_library installed.
+	installedInSystem := ctx.Platform() || ctx.SystemExtSpecific()
+	installedInVendorOrOdm := ctx.SocSpecific() || ctx.DeviceSpecific()
+	isOwnerPlatform := false
+	stub := "sysprop-library-stub-"
+
+	switch m.Owner() {
 	case "Platform":
 		// Every partition can access platform-defined properties
-		break
+		stub += "platform"
+		isOwnerPlatform = true
 	case "Vendor":
 		// System can't access vendor's properties
-		if !socSpecific && !deviceSpecific && !productSpecific {
+		if installedInSystem {
 			ctx.ModuleErrorf("None of soc_specific, device_specific, product_specific is true. " +
 				"System can't access sysprop_library owned by Vendor")
 		}
+		stub += "vendor"
 	case "Odm":
 		// Only vendor can access Odm-defined properties
-		if !socSpecific && !deviceSpecific {
+		if !installedInVendorOrOdm {
 			ctx.ModuleErrorf("Neither soc_speicifc nor device_specific is true. " +
 				"Odm-defined properties should be accessed only in Vendor or Odm")
 		}
+		stub += "vendor"
 	default:
 		ctx.PropertyErrorf("property_owner",
-			"Unknown value %s: must be one of Platform, Vendor or Odm", owner)
+			"Unknown value %s: must be one of Platform, Vendor or Odm", m.Owner())
 	}
 
-	ccProps := struct {
-		Name             *string
-		Soc_specific     *bool
-		Device_specific  *bool
-		Product_specific *bool
-		Sysprop          struct {
-			Platform *bool
-		}
-		Header_libs []string
-		Shared_libs []string
-	}{}
-
+	ccProps := ccLibraryProperties{}
 	ccProps.Name = proptools.StringPtr(m.CcModuleName())
-	ccProps.Soc_specific = proptools.BoolPtr(socSpecific)
-	ccProps.Device_specific = proptools.BoolPtr(deviceSpecific)
-	ccProps.Product_specific = proptools.BoolPtr(productSpecific)
-	ccProps.Sysprop.Platform = proptools.BoolPtr(owner == "Platform")
+	ccProps.Srcs = m.properties.Srcs
+	ccProps.Soc_specific = proptools.BoolPtr(ctx.SocSpecific())
+	ccProps.Device_specific = proptools.BoolPtr(ctx.DeviceSpecific())
+	ccProps.Product_specific = proptools.BoolPtr(ctx.ProductSpecific())
+	ccProps.Sysprop.Platform = proptools.BoolPtr(isOwnerPlatform)
 	ccProps.Header_libs = []string{"libbase_headers"}
 	ccProps.Shared_libs = []string{"liblog"}
+	ccProps.Recovery_available = m.properties.Recovery_available
+	ccProps.Vendor_available = m.properties.Vendor_available
+	ctx.CreateModule(cc.LibraryFactory, &ccProps)
 
-	ctx.CreateModule(android.ModuleFactoryAdaptor(cc.LibraryFactory), &m.commonProperties, &ccProps)
+	scope := "internal"
+
+	// We need to only use public version, if the partition where sysprop_library will be installed
+	// is different from owner.
+
+	if ctx.ProductSpecific() {
+		// Currently product partition can't own any sysprop_library.
+		scope = "public"
+	} else if isOwnerPlatform && installedInVendorOrOdm {
+		// Vendor or Odm should use public version of Platform's sysprop_library.
+		scope = "public"
+	}
+
+	ctx.CreateModule(syspropJavaGenFactory, &syspropGenProperties{
+		Srcs:  m.properties.Srcs,
+		Scope: scope,
+		Name:  proptools.StringPtr(m.javaGenModuleName()),
+	})
+
+	ctx.CreateModule(java.LibraryFactory, &javaLibraryProperties{
+		Name:             proptools.StringPtr(m.BaseModuleName()),
+		Srcs:             []string{":" + m.javaGenModuleName()},
+		Soc_specific:     proptools.BoolPtr(ctx.SocSpecific()),
+		Device_specific:  proptools.BoolPtr(ctx.DeviceSpecific()),
+		Product_specific: proptools.BoolPtr(ctx.ProductSpecific()),
+		Installable:      m.properties.Installable,
+		Sdk_version:      proptools.StringPtr("core_current"),
+		Libs:             []string{stub},
+	})
+
+	// if platform sysprop_library is installed in /system or /system-ext, we regard it as an API
+	// and allow any modules (even from different partition) to link against the sysprop_library.
+	// To do that, we create a public stub and expose it to modules with sdk_version: system_*.
+	if isOwnerPlatform && installedInSystem {
+		m.properties.Public_stub = proptools.BoolPtr(true)
+		ctx.CreateModule(syspropJavaGenFactory, &syspropGenProperties{
+			Srcs:  m.properties.Srcs,
+			Scope: "public",
+			Name:  proptools.StringPtr(m.javaGenPublicStubName()),
+		})
+
+		ctx.CreateModule(java.LibraryFactory, &javaLibraryProperties{
+			Name:        proptools.StringPtr(m.JavaPublicStubName()),
+			Srcs:        []string{":" + m.javaGenPublicStubName()},
+			Installable: proptools.BoolPtr(false),
+			Sdk_version: proptools.StringPtr("core_current"),
+			Libs:        []string{stub},
+			Stem:        proptools.StringPtr(m.BaseModuleName()),
+		})
+	}
+}
+
+func syspropDepsMutator(ctx android.BottomUpMutatorContext) {
+	if m, ok := ctx.Module().(*syspropLibrary); ok {
+		ctx.AddReverseDependency(m, nil, m.javaGenModuleName())
+
+		if proptools.Bool(m.properties.Public_stub) {
+			ctx.AddReverseDependency(m, nil, m.javaGenPublicStubName())
+		}
+	}
 }
diff --git a/sysprop/sysprop_test.go b/sysprop/sysprop_test.go
index 0566036..7cad3da 100644
--- a/sysprop/sysprop_test.go
+++ b/sysprop/sysprop_test.go
@@ -24,6 +24,7 @@
 	"strings"
 	"testing"
 
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
 
@@ -52,84 +53,55 @@
 	os.Exit(run())
 }
 
-func testContext(config android.Config, bp string,
-	fs map[string][]byte) *android.TestContext {
+func testContext(config android.Config) *android.TestContext {
 
 	ctx := android.NewTestArchContext()
-	ctx.RegisterModuleType("android_app", android.ModuleFactoryAdaptor(java.AndroidAppFactory))
-	ctx.RegisterModuleType("droiddoc_template", android.ModuleFactoryAdaptor(java.ExportedDroiddocDirFactory))
-	ctx.RegisterModuleType("java_library", android.ModuleFactoryAdaptor(java.LibraryFactory))
-	ctx.RegisterModuleType("java_system_modules", android.ModuleFactoryAdaptor(java.SystemModulesFactory))
-	ctx.RegisterModuleType("prebuilt_apis", android.ModuleFactoryAdaptor(java.PrebuiltApisFactory))
-	ctx.PreArchMutators(android.RegisterPrebuiltsPreArchMutators)
-	ctx.PreArchMutators(android.RegisterPrebuiltsPostDepsMutators)
+	java.RegisterJavaBuildComponents(ctx)
+	java.RegisterAppBuildComponents(ctx)
+	java.RegisterSystemModulesBuildComponents(ctx)
+
 	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
 	ctx.PreArchMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.TopDown("prebuilt_apis", java.PrebuiltApisMutator).Parallel()
+		ctx.BottomUp("sysprop_deps", syspropDepsMutator).Parallel()
 	})
 
-	ctx.RegisterModuleType("cc_library", android.ModuleFactoryAdaptor(cc.LibraryFactory))
-	ctx.RegisterModuleType("cc_library_headers", android.ModuleFactoryAdaptor(cc.LibraryHeaderFactory))
-	ctx.RegisterModuleType("cc_library_static", android.ModuleFactoryAdaptor(cc.LibraryFactory))
-	ctx.RegisterModuleType("cc_object", android.ModuleFactoryAdaptor(cc.ObjectFactory))
-	ctx.RegisterModuleType("llndk_library", android.ModuleFactoryAdaptor(cc.LlndkLibraryFactory))
-	ctx.RegisterModuleType("toolchain_library", android.ModuleFactoryAdaptor(cc.ToolchainLibraryFactory))
+	cc.RegisterRequiredBuildComponentsForTest(ctx)
 	ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.BottomUp("image", cc.ImageMutator).Parallel()
-		ctx.BottomUp("link", cc.LinkageMutator).Parallel()
-		ctx.BottomUp("vndk", cc.VndkMutator).Parallel()
-		ctx.BottomUp("version", cc.VersionMutator).Parallel()
-		ctx.BottomUp("begin", cc.BeginMutator).Parallel()
-		ctx.BottomUp("sysprop", cc.SyspropMutator).Parallel()
+		ctx.BottomUp("sysprop_java", java.SyspropMutator).Parallel()
 	})
 
-	ctx.RegisterModuleType("sysprop_library", android.ModuleFactoryAdaptor(syspropLibraryFactory))
+	ctx.RegisterModuleType("sysprop_library", syspropLibraryFactory)
 
-	ctx.Register()
+	ctx.Register(config)
 
-	bp += java.GatherRequiredDepsForTest()
+	return ctx
+}
+
+func run(t *testing.T, ctx *android.TestContext, config android.Config) {
+	t.Helper()
+	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+	android.FailIfErrored(t, errs)
+	_, errs = ctx.PrepareBuildActions(config)
+	android.FailIfErrored(t, errs)
+}
+
+func testConfig(env map[string]string, bp string, fs map[string][]byte) android.Config {
 	bp += cc.GatherRequiredDepsForTest(android.Android)
 
 	mockFS := map[string][]byte{
-		"Android.bp":             []byte(bp),
-		"a.java":                 nil,
-		"b.java":                 nil,
-		"c.java":                 nil,
-		"d.cpp":                  nil,
-		"api/current.txt":        nil,
-		"api/removed.txt":        nil,
-		"api/system-current.txt": nil,
-		"api/system-removed.txt": nil,
-		"api/test-current.txt":   nil,
-		"api/test-removed.txt":   nil,
-		"framework/aidl/a.aidl":  nil,
-
-		"prebuilts/sdk/current/core/android.jar":                              nil,
-		"prebuilts/sdk/current/public/android.jar":                            nil,
-		"prebuilts/sdk/current/public/framework.aidl":                         nil,
-		"prebuilts/sdk/current/public/core.jar":                               nil,
-		"prebuilts/sdk/current/system/android.jar":                            nil,
-		"prebuilts/sdk/current/test/android.jar":                              nil,
-		"prebuilts/sdk/28/public/api/sysprop-platform.txt":                    nil,
-		"prebuilts/sdk/28/system/api/sysprop-platform.txt":                    nil,
-		"prebuilts/sdk/28/test/api/sysprop-platform.txt":                      nil,
-		"prebuilts/sdk/28/public/api/sysprop-platform-removed.txt":            nil,
-		"prebuilts/sdk/28/system/api/sysprop-platform-removed.txt":            nil,
-		"prebuilts/sdk/28/test/api/sysprop-platform-removed.txt":              nil,
-		"prebuilts/sdk/28/public/api/sysprop-platform-on-product.txt":         nil,
-		"prebuilts/sdk/28/system/api/sysprop-platform-on-product.txt":         nil,
-		"prebuilts/sdk/28/test/api/sysprop-platform-on-product.txt":           nil,
-		"prebuilts/sdk/28/public/api/sysprop-platform-on-product-removed.txt": nil,
-		"prebuilts/sdk/28/system/api/sysprop-platform-on-product-removed.txt": nil,
-		"prebuilts/sdk/28/test/api/sysprop-platform-on-product-removed.txt":   nil,
-		"prebuilts/sdk/28/public/api/sysprop-vendor.txt":                      nil,
-		"prebuilts/sdk/28/system/api/sysprop-vendor.txt":                      nil,
-		"prebuilts/sdk/28/test/api/sysprop-vendor.txt":                        nil,
-		"prebuilts/sdk/28/public/api/sysprop-vendor-removed.txt":              nil,
-		"prebuilts/sdk/28/system/api/sysprop-vendor-removed.txt":              nil,
-		"prebuilts/sdk/28/test/api/sysprop-vendor-removed.txt":                nil,
-		"prebuilts/sdk/tools/core-lambda-stubs.jar":                           nil,
-		"prebuilts/sdk/Android.bp":                                            []byte(`prebuilt_apis { name: "sdk", api_dirs: ["28", "current"],}`),
+		"a.java":                           nil,
+		"b.java":                           nil,
+		"c.java":                           nil,
+		"d.cpp":                            nil,
+		"api/sysprop-platform-current.txt": nil,
+		"api/sysprop-platform-latest.txt":  nil,
+		"api/sysprop-platform-on-product-current.txt": nil,
+		"api/sysprop-platform-on-product-latest.txt":  nil,
+		"api/sysprop-vendor-current.txt":              nil,
+		"api/sysprop-vendor-latest.txt":               nil,
+		"api/sysprop-odm-current.txt":                 nil,
+		"api/sysprop-odm-latest.txt":                  nil,
+		"framework/aidl/a.aidl":                       nil,
 
 		// For framework-res, which is an implicit dependency for framework
 		"AndroidManifest.xml":                        nil,
@@ -155,27 +127,14 @@
 
 		"android/sysprop/PlatformProperties.sysprop": nil,
 		"com/android/VendorProperties.sysprop":       nil,
+		"com/android2/OdmProperties.sysprop":         nil,
 	}
 
 	for k, v := range fs {
 		mockFS[k] = v
 	}
 
-	ctx.MockFileSystem(mockFS)
-
-	return ctx
-}
-
-func run(t *testing.T, ctx *android.TestContext, config android.Config) {
-	t.Helper()
-	_, errs := ctx.ParseFileList(".", []string{"Android.bp", "prebuilts/sdk/Android.bp"})
-	android.FailIfErrored(t, errs)
-	_, errs = ctx.PrepareBuildActions(config)
-	android.FailIfErrored(t, errs)
-}
-
-func testConfig(env map[string]string) android.Config {
-	config := java.TestConfig(buildDir, env)
+	config := java.TestConfig(buildDir, env, bp, mockFS)
 
 	config.TestProductVariables.DeviceSystemSdkVersions = []string{"28"}
 	config.TestProductVariables.DeviceVndkVersion = proptools.StringPtr("current")
@@ -187,8 +146,8 @@
 
 func test(t *testing.T, bp string) *android.TestContext {
 	t.Helper()
-	config := testConfig(nil)
-	ctx := testContext(config, bp, nil)
+	config := testConfig(nil, bp, nil)
+	ctx := testContext(config)
 	run(t, ctx, config)
 
 	return ctx
@@ -221,6 +180,14 @@
 			vendor_available: true,
 		}
 
+		sysprop_library {
+			name: "sysprop-odm",
+			srcs: ["com/android2/OdmProperties.sysprop"],
+			api_packages: ["com.android2"],
+			property_owner: "Odm",
+			device_specific: true,
+		}
+
 		java_library {
 			name: "java-platform",
 			srcs: ["c.java"],
@@ -229,6 +196,13 @@
 		}
 
 		java_library {
+			name: "java-platform-private",
+			srcs: ["c.java"],
+			platform_apis: true,
+			libs: ["sysprop-platform"],
+		}
+
+		java_library {
 			name: "java-product",
 			srcs: ["c.java"],
 			sdk_version: "system_current",
@@ -288,38 +262,59 @@
 			name: "liblog",
 			symbol_file: "",
 		}
+
+		java_library {
+			name: "sysprop-library-stub-platform",
+			sdk_version: "core_current",
+		}
+
+		java_library {
+			name: "sysprop-library-stub-vendor",
+			soc_specific: true,
+			sdk_version: "core_current",
+		}
 		`)
 
+	// Check for generated cc_library
 	for _, variant := range []string{
-		"android_arm_armv7-a-neon_core_shared",
-		"android_arm_armv7-a-neon_core_static",
-		"android_arm_armv7-a-neon_vendor_shared",
-		"android_arm_armv7-a-neon_vendor_static",
-		"android_arm64_armv8-a_core_shared",
-		"android_arm64_armv8-a_core_static",
-		"android_arm64_armv8-a_vendor_shared",
-		"android_arm64_armv8-a_vendor_static",
+		"android_vendor.VER_arm_armv7-a-neon_shared",
+		"android_vendor.VER_arm_armv7-a-neon_static",
+		"android_vendor.VER_arm64_armv8-a_shared",
+		"android_vendor.VER_arm64_armv8-a_static",
 	} {
-		// Check for generated cc_library
 		ctx.ModuleForTests("libsysprop-platform", variant)
 		ctx.ModuleForTests("libsysprop-vendor", variant)
+		ctx.ModuleForTests("libsysprop-odm", variant)
+	}
+
+	for _, variant := range []string{
+		"android_arm_armv7-a-neon_shared",
+		"android_arm_armv7-a-neon_static",
+		"android_arm64_armv8-a_shared",
+		"android_arm64_armv8-a_static",
+	} {
+		ctx.ModuleForTests("libsysprop-platform", variant)
+
+		// core variant of vendor-owned sysprop_library is for product
+		ctx.ModuleForTests("libsysprop-vendor", variant)
 	}
 
 	ctx.ModuleForTests("sysprop-platform", "android_common")
+	ctx.ModuleForTests("sysprop-platform_public", "android_common")
 	ctx.ModuleForTests("sysprop-vendor", "android_common")
 
 	// Check for exported includes
-	coreVariant := "android_arm64_armv8-a_core_static"
-	vendorVariant := "android_arm64_armv8-a_vendor_static"
+	coreVariant := "android_arm64_armv8-a_static"
+	vendorVariant := "android_vendor.VER_arm64_armv8-a_static"
 
-	platformInternalPath := "libsysprop-platform/android_arm64_armv8-a_core_static/gen/sysprop/include"
-	platformPublicCorePath := "libsysprop-platform/android_arm64_armv8-a_core_static/gen/sysprop/public/include"
-	platformPublicVendorPath := "libsysprop-platform/android_arm64_armv8-a_vendor_static/gen/sysprop/public/include"
+	platformInternalPath := "libsysprop-platform/android_arm64_armv8-a_static/gen/sysprop/include"
+	platformPublicCorePath := "libsysprop-platform/android_arm64_armv8-a_static/gen/sysprop/public/include"
+	platformPublicVendorPath := "libsysprop-platform/android_vendor.VER_arm64_armv8-a_static/gen/sysprop/public/include"
 
-	platformOnProductPath := "libsysprop-platform-on-product/android_arm64_armv8-a_core_static/gen/sysprop/public/include"
+	platformOnProductPath := "libsysprop-platform-on-product/android_arm64_armv8-a_static/gen/sysprop/public/include"
 
-	vendorInternalPath := "libsysprop-vendor/android_arm64_armv8-a_vendor_static/gen/sysprop/include"
-	vendorPublicPath := "libsysprop-vendor/android_arm64_armv8-a_core_static/gen/sysprop/public/include"
+	vendorInternalPath := "libsysprop-vendor/android_vendor.VER_arm64_armv8-a_static/gen/sysprop/include"
+	vendorPublicPath := "libsysprop-vendor/android_arm64_armv8-a_static/gen/sysprop/public/include"
 
 	platformClient := ctx.ModuleForTests("cc-client-platform", coreVariant)
 	platformFlags := platformClient.Rule("cc").Args["cFlags"]
@@ -358,4 +353,17 @@
 		t.Errorf("flags for vendor must contain %#v and %#v, but was %#v.",
 			platformPublicVendorPath, vendorInternalPath, vendorFlags)
 	}
+
+	// Java modules linking against system API should use public stub
+	javaSystemApiClient := ctx.ModuleForTests("java-platform", "android_common")
+	publicStubFound := false
+	ctx.VisitDirectDeps(javaSystemApiClient.Module(), func(dep blueprint.Module) {
+		if dep.Name() == "sysprop-platform_public" {
+			publicStubFound = true
+		}
+	})
+	if !publicStubFound {
+		t.Errorf("system api client should use public stub")
+	}
+
 }
diff --git a/tradefed/autogen.go b/tradefed/autogen.go
index 952b022..c35d8b9 100644
--- a/tradefed/autogen.go
+++ b/tradefed/autogen.go
@@ -24,6 +24,8 @@
 	"android/soong/android"
 )
 
+const test_xml_indent = "    "
+
 func getTestConfigTemplate(ctx android.ModuleContext, prop *string) android.OptionalPath {
 	return ctx.ExpandOptionalSource(prop, "test_config_template")
 }
@@ -42,10 +44,11 @@
 	CommandDeps: []string{"$template"},
 }, "name", "template", "extraConfigs")
 
-func testConfigPath(ctx android.ModuleContext, prop *string, testSuites []string) (path android.Path, autogenPath android.WritablePath) {
-	if p := getTestConfig(ctx, prop); p != nil {
+func testConfigPath(ctx android.ModuleContext, prop *string, testSuites []string, autoGenConfig *bool) (path android.Path, autogenPath android.WritablePath) {
+	p := getTestConfig(ctx, prop)
+	if !Bool(autoGenConfig) && p != nil {
 		return p, nil
-	} else if !android.InList("cts", testSuites) {
+	} else if !android.InList("cts", testSuites) && BoolDefault(autoGenConfig, true) {
 		outputFile := android.PathForModuleOut(ctx, ctx.ModuleName()+".config")
 		return nil, outputFile
 	} else {
@@ -71,22 +74,46 @@
 	return fmt.Sprintf(`<option name="%s" value="%s" />`, o.Name, o.Value)
 }
 
-type Preparer struct {
-	Class string
+// It can be a template of object or target_preparer.
+type Object struct {
+	// Set it as a target_preparer if object type == "target_preparer".
+	Type    string
+	Class   string
+	Options []Option
 }
 
-var _ Config = Preparer{}
+var _ Config = Object{}
 
-func (p Preparer) Config() string {
-	return fmt.Sprintf(`<target_preparer class="%s" />`, p.Class)
+func (ob Object) Config() string {
+	var optionStrings []string
+	for _, option := range ob.Options {
+		optionStrings = append(optionStrings, option.Config())
+	}
+	var options string
+	if len(ob.Options) == 0 {
+		options = ""
+	} else {
+		optionDelimiter := fmt.Sprintf("\\n%s%s", test_xml_indent, test_xml_indent)
+		options = optionDelimiter + strings.Join(optionStrings, optionDelimiter)
+	}
+	if ob.Type == "target_preparer" {
+		return fmt.Sprintf(`<target_preparer class="%s">%s\n%s</target_preparer>`, ob.Class, options, test_xml_indent)
+	} else {
+		return fmt.Sprintf(`<object type="%s" class="%s">%s\n%s</object>`, ob.Type, ob.Class, options, test_xml_indent)
+	}
+
 }
 
 func autogenTemplate(ctx android.ModuleContext, output android.WritablePath, template string, configs []Config) {
+	autogenTemplateWithName(ctx, ctx.ModuleName(), output, template, configs)
+}
+
+func autogenTemplateWithName(ctx android.ModuleContext, name string, output android.WritablePath, template string, configs []Config) {
 	var configStrings []string
 	for _, config := range configs {
 		configStrings = append(configStrings, config.Config())
 	}
-	extraConfigs := strings.Join(configStrings, "\n        ")
+	extraConfigs := strings.Join(configStrings, fmt.Sprintf("\\n%s", test_xml_indent))
 	extraConfigs = proptools.NinjaAndShellEscape(extraConfigs)
 
 	ctx.Build(pctx, android.BuildParams{
@@ -94,7 +121,7 @@
 		Description: "test config",
 		Output:      output,
 		Args: map[string]string{
-			"name":         ctx.ModuleName(),
+			"name":         name,
 			"template":     template,
 			"extraConfigs": extraConfigs,
 		},
@@ -102,8 +129,8 @@
 }
 
 func AutoGenNativeTestConfig(ctx android.ModuleContext, testConfigProp *string,
-	testConfigTemplateProp *string, testSuites []string, config []Config) android.Path {
-	path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites)
+	testConfigTemplateProp *string, testSuites []string, config []Config, autoGenConfig *bool) android.Path {
+	path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig)
 	if autogenPath != nil {
 		templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp)
 		if templatePath.Valid() {
@@ -121,8 +148,8 @@
 }
 
 func AutoGenNativeBenchmarkTestConfig(ctx android.ModuleContext, testConfigProp *string,
-	testConfigTemplateProp *string, testSuites []string, configs []Config) android.Path {
-	path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites)
+	testConfigTemplateProp *string, testSuites []string, configs []Config, autoGenConfig *bool) android.Path {
+	path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig)
 	if autogenPath != nil {
 		templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp)
 		if templatePath.Valid() {
@@ -135,8 +162,9 @@
 	return path
 }
 
-func AutoGenJavaTestConfig(ctx android.ModuleContext, testConfigProp *string, testConfigTemplateProp *string, testSuites []string) android.Path {
-	path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites)
+func AutoGenJavaTestConfig(ctx android.ModuleContext, testConfigProp *string, testConfigTemplateProp *string,
+	testSuites []string, autoGenConfig *bool) android.Path {
+	path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig)
 	if autogenPath != nil {
 		templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp)
 		if templatePath.Valid() {
@@ -154,9 +182,9 @@
 }
 
 func AutoGenPythonBinaryHostTestConfig(ctx android.ModuleContext, testConfigProp *string,
-	testConfigTemplateProp *string, testSuites []string) android.Path {
+	testConfigTemplateProp *string, testSuites []string, autoGenConfig *bool) android.Path {
 
-	path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites)
+	path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig)
 	if autogenPath != nil {
 		templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp)
 		if templatePath.Valid() {
@@ -169,6 +197,24 @@
 	return path
 }
 
+func AutoGenRustTestConfig(ctx android.ModuleContext, name string, testConfigProp *string,
+	testConfigTemplateProp *string, testSuites []string, autoGenConfig *bool) android.Path {
+	path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig)
+	if autogenPath != nil {
+		templatePathString := "${RustHostTestConfigTemplate}"
+		if ctx.Device() {
+			templatePathString = "${RustDeviceTestConfigTemplate}"
+		}
+		templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp)
+		if templatePath.Valid() {
+			templatePathString = templatePath.String()
+		}
+		autogenTemplateWithName(ctx, name, autogenPath, templatePathString, nil)
+		return autogenPath
+	}
+	return path
+}
+
 var autogenInstrumentationTest = pctx.StaticRule("autogenInstrumentationTest", blueprint.RuleParams{
 	Command: "${AutoGenTestConfigScript} $out $in ${EmptyTestConfig} $template",
 	CommandDeps: []string{
@@ -178,8 +224,9 @@
 	},
 }, "name", "template")
 
-func AutoGenInstrumentationTestConfig(ctx android.ModuleContext, testConfigProp *string, testConfigTemplateProp *string, manifest android.Path, testSuites []string) android.Path {
-	path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites)
+func AutoGenInstrumentationTestConfig(ctx android.ModuleContext, testConfigProp *string,
+	testConfigTemplateProp *string, manifest android.Path, testSuites []string, autoGenConfig *bool) android.Path {
+	path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig)
 	if autogenPath != nil {
 		template := "${InstrumentationTestConfigTemplate}"
 		moduleTemplate := getTestConfigTemplate(ctx, testConfigTemplateProp)
@@ -200,3 +247,6 @@
 	}
 	return path
 }
+
+var Bool = proptools.Bool
+var BoolDefault = proptools.BoolDefault
diff --git a/tradefed/config.go b/tradefed/config.go
index 141e0c5..a289073 100644
--- a/tradefed/config.go
+++ b/tradefed/config.go
@@ -31,6 +31,8 @@
 	pctx.SourcePathVariable("NativeHostTestConfigTemplate", "build/make/core/native_host_test_config_template.xml")
 	pctx.SourcePathVariable("NativeTestConfigTemplate", "build/make/core/native_test_config_template.xml")
 	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("EmptyTestConfig", "build/make/core/empty_test_config.xml")
 }
diff --git a/tradefed/makevars.go b/tradefed/makevars.go
index aad7273..d4cf7a8 100644
--- a/tradefed/makevars.go
+++ b/tradefed/makevars.go
@@ -31,6 +31,8 @@
 	ctx.Strict("NATIVE_HOST_TEST_CONFIG_TEMPLATE", "${NativeHostTestConfigTemplate}")
 	ctx.Strict("NATIVE_TEST_CONFIG_TEMPLATE", "${NativeTestConfigTemplate}")
 	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("EMPTY_TEST_CONFIG", "${EmptyTestConfig}")
 }
diff --git a/ui/build/Android.bp b/ui/build/Android.bp
index 1ddaf68..2a5a51a 100644
--- a/ui/build/Android.bp
+++ b/ui/build/Android.bp
@@ -52,12 +52,14 @@
         "ninja.go",
         "path.go",
         "proc_sync.go",
+        "rbe.go",
         "signal.go",
         "soong.go",
         "test_build.go",
         "util.go",
     ],
     testSrcs: [
+        "cleanbuild_test.go",
         "config_test.go",
         "environment_test.go",
         "util_test.go",
@@ -65,11 +67,13 @@
     ],
     darwin: {
         srcs: [
+            "config_darwin.go",
             "sandbox_darwin.go",
         ],
     },
     linux: {
         srcs: [
+            "config_linux.go",
             "sandbox_linux.go",
         ],
     },
diff --git a/ui/build/build.go b/ui/build/build.go
index 59d1474..1c2d864 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -33,6 +33,15 @@
 	// can be parsed as ninja output.
 	ensureEmptyFileExists(ctx, filepath.Join(config.OutDir(), "ninja_build"))
 	ensureEmptyFileExists(ctx, filepath.Join(config.OutDir(), ".out-dir"))
+
+	if buildDateTimeFile, ok := config.environ.Get("BUILD_DATETIME_FILE"); ok {
+		err := ioutil.WriteFile(buildDateTimeFile, []byte(config.buildDateTime), 0777)
+		if err != nil {
+			ctx.Fatalln("Failed to write BUILD_DATETIME to file:", err)
+		}
+	} else {
+		ctx.Fatalln("Missing BUILD_DATETIME_FILE")
+	}
 }
 
 var combinedBuildNinjaTemplate = template.Must(template.New("combined").Parse(`
@@ -128,6 +137,7 @@
 func Build(ctx Context, config Config, what int) {
 	ctx.Verboseln("Starting build with args:", config.Arguments())
 	ctx.Verboseln("Environment:", config.Environment().Environ())
+	ctx.Verbosef("Total RAM: %dGB", config.TotalRAM()/1024/1024/1024)
 
 	if config.SkipMake() {
 		ctx.Verboseln("Skipping Make/Kati as requested")
@@ -161,6 +171,11 @@
 		startGoma(ctx, config)
 	}
 
+	if config.StartRBE() {
+		// Ensure RBE proxy is started
+		startRBE(ctx, config)
+	}
+
 	if what&BuildProductConfig != 0 {
 		// Run make for product config
 		runMakeProductConfig(ctx, config)
diff --git a/ui/build/cleanbuild.go b/ui/build/cleanbuild.go
index c47f614..1c4f574 100644
--- a/ui/build/cleanbuild.go
+++ b/ui/build/cleanbuild.go
@@ -15,10 +15,12 @@
 package build
 
 import (
+	"bytes"
 	"fmt"
 	"io/ioutil"
 	"os"
 	"path/filepath"
+	"sort"
 	"strings"
 
 	"android/soong/ui/metrics"
@@ -87,6 +89,7 @@
 	// otherwise we'd have to rebuild any generated files created with
 	// those tools.
 	removeGlobs(ctx,
+		hostOut("apex"),
 		hostOut("obj/NOTICE_FILES"),
 		hostOut("obj/PACKAGING"),
 		hostOut("coverage"),
@@ -96,22 +99,26 @@
 		hostOut("sdk_addon"),
 		hostOut("testcases"),
 		hostOut("vts"),
+		hostOut("vts-core"),
 		productOut("*.img"),
 		productOut("*.zip"),
 		productOut("android-info.txt"),
+		productOut("apex"),
 		productOut("kernel"),
 		productOut("data"),
 		productOut("skin"),
 		productOut("obj/NOTICE_FILES"),
 		productOut("obj/PACKAGING"),
 		productOut("ramdisk"),
+		productOut("debug_ramdisk"),
+		productOut("test_harness_ramdisk"),
 		productOut("recovery"),
 		productOut("root"),
 		productOut("system"),
 		productOut("system_other"),
 		productOut("vendor"),
 		productOut("product"),
-		productOut("product_services"),
+		productOut("system_ext"),
 		productOut("oem"),
 		productOut("obj/FAKE"),
 		productOut("breakpad"),
@@ -172,3 +179,78 @@
 
 	writeConfig()
 }
+
+// cleanOldFiles takes an input file (with all paths relative to basePath), and removes files from
+// the filesystem if they were removed from the input file since the last execution.
+func cleanOldFiles(ctx Context, basePath, file string) {
+	file = filepath.Join(basePath, file)
+	oldFile := file + ".previous"
+
+	if _, err := os.Stat(file); err != nil {
+		ctx.Fatalf("Expected %q to be readable", file)
+	}
+
+	if _, err := os.Stat(oldFile); os.IsNotExist(err) {
+		if err := os.Rename(file, oldFile); err != nil {
+			ctx.Fatalf("Failed to rename file list (%q->%q): %v", file, oldFile, err)
+		}
+		return
+	}
+
+	var newPaths, oldPaths []string
+	if newData, err := ioutil.ReadFile(file); err == nil {
+		if oldData, err := ioutil.ReadFile(oldFile); err == nil {
+			// Common case: nothing has changed
+			if bytes.Equal(newData, oldData) {
+				return
+			}
+			newPaths = strings.Fields(string(newData))
+			oldPaths = strings.Fields(string(oldData))
+		} else {
+			ctx.Fatalf("Failed to read list of installable files (%q): %v", oldFile, err)
+		}
+	} else {
+		ctx.Fatalf("Failed to read list of installable files (%q): %v", file, err)
+	}
+
+	// These should be mostly sorted by make already, but better make sure Go concurs
+	sort.Strings(newPaths)
+	sort.Strings(oldPaths)
+
+	for len(oldPaths) > 0 {
+		if len(newPaths) > 0 {
+			if oldPaths[0] == newPaths[0] {
+				// Same file; continue
+				newPaths = newPaths[1:]
+				oldPaths = oldPaths[1:]
+				continue
+			} else if oldPaths[0] > newPaths[0] {
+				// New file; ignore
+				newPaths = newPaths[1:]
+				continue
+			}
+		}
+		// File only exists in the old list; remove if it exists
+		old := filepath.Join(basePath, oldPaths[0])
+		oldPaths = oldPaths[1:]
+		if fi, err := os.Stat(old); err == nil {
+			if fi.IsDir() {
+				if err := os.Remove(old); err == nil {
+					ctx.Println("Removed directory that is no longer installed: ", old)
+				} else {
+					ctx.Println("Failed to remove directory that is no longer installed (%q): %v", old, err)
+					ctx.Println("It's recommended to run `m installclean`")
+				}
+			} else {
+				if err := os.Remove(old); err == nil {
+					ctx.Println("Removed file that is no longer installed: ", old)
+				} else if !os.IsNotExist(err) {
+					ctx.Fatalf("Failed to remove file that is no longer installed (%q): %v", old, err)
+				}
+			}
+		}
+	}
+
+	// Use the new list as the base for the next build
+	os.Rename(file, oldFile)
+}
diff --git a/ui/build/cleanbuild_test.go b/ui/build/cleanbuild_test.go
new file mode 100644
index 0000000..89f4ad9
--- /dev/null
+++ b/ui/build/cleanbuild_test.go
@@ -0,0 +1,100 @@
+// 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 (
+	"android/soong/ui/logger"
+	"bytes"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"reflect"
+	"sort"
+	"strings"
+	"testing"
+)
+
+func TestCleanOldFiles(t *testing.T) {
+	dir, err := ioutil.TempDir("", "testcleanoldfiles")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer os.RemoveAll(dir)
+
+	ctx := testContext()
+	logBuf := &bytes.Buffer{}
+	ctx.Logger = logger.New(logBuf)
+
+	touch := func(names ...string) {
+		for _, name := range names {
+			if f, err := os.Create(filepath.Join(dir, name)); err != nil {
+				t.Fatal(err)
+			} else {
+				f.Close()
+			}
+		}
+	}
+	runCleanOldFiles := func(names ...string) {
+		data := []byte(strings.Join(names, " "))
+		if err := ioutil.WriteFile(filepath.Join(dir, ".installed"), data, 0666); err != nil {
+			t.Fatal(err)
+		}
+
+		cleanOldFiles(ctx, dir, ".installed")
+	}
+
+	assertFileList := func(names ...string) {
+		t.Helper()
+
+		sort.Strings(names)
+
+		var foundNames []string
+		if foundFiles, err := ioutil.ReadDir(dir); err == nil {
+			for _, fi := range foundFiles {
+				foundNames = append(foundNames, fi.Name())
+			}
+		} else {
+			t.Fatal(err)
+		}
+
+		if !reflect.DeepEqual(names, foundNames) {
+			t.Errorf("Expected a different list of files:\nwant: %v\n got: %v", names, foundNames)
+			t.Error("Log: ", logBuf.String())
+			logBuf.Reset()
+		}
+	}
+
+	// Initial list of potential files
+	runCleanOldFiles("foo", "bar")
+	touch("foo", "bar", "baz")
+	assertFileList("foo", "bar", "baz", ".installed.previous")
+
+	// This should be a no-op, as the list hasn't changed
+	runCleanOldFiles("foo", "bar")
+	assertFileList("foo", "bar", "baz", ".installed", ".installed.previous")
+
+	// This should be a no-op, as only a file was added
+	runCleanOldFiles("foo", "bar", "foo2")
+	assertFileList("foo", "bar", "baz", ".installed.previous")
+
+	// "bar" should be removed, foo2 should be ignored as it was never there
+	runCleanOldFiles("foo")
+	assertFileList("foo", "baz", ".installed.previous")
+
+	// Recreate bar, and create foo2. Ensure that they aren't removed
+	touch("bar", "foo2")
+	runCleanOldFiles("foo", "baz")
+	assertFileList("foo", "bar", "baz", "foo2", ".installed.previous")
+}
diff --git a/ui/build/config.go b/ui/build/config.go
index 6df9529..c084171 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -15,8 +15,6 @@
 package build
 
 import (
-	"io/ioutil"
-	"log"
 	"os"
 	"path/filepath"
 	"runtime"
@@ -31,10 +29,11 @@
 
 type configImpl struct {
 	// From the environment
-	arguments []string
-	goma      bool
-	environ   *Environment
-	distDir   string
+	arguments     []string
+	goma          bool
+	environ       *Environment
+	distDir       string
+	buildDateTime string
 
 	// From the arguments
 	parallel   int
@@ -51,16 +50,22 @@
 	targetDevice    string
 	targetDeviceDir string
 
+	// Autodetected
+	totalRAM uint64
+
 	pdkBuild bool
 
-	brokenDupRules    bool
-	brokenUsesNetwork bool
+	brokenDupRules     bool
+	brokenUsesNetwork  bool
+	brokenNinjaEnvVars []string
 
 	pathReplaced bool
 }
 
 const srcDirFileCheck = "build/soong/root.bp"
 
+var buildFiles = []string{"Android.mk", "Android.bp"}
+
 type BuildAction uint
 
 const (
@@ -71,6 +76,9 @@
 	// Builds all of the modules and their dependencies of a list of specified directories. All specified
 	// directories are relative to the root directory of the source tree.
 	BUILD_MODULES_IN_DIRECTORIES
+
+	// Build a list of specified modules. If none was specified, simply build the whole source tree.
+	BUILD_MODULES
 )
 
 // checkTopDir validates that the current directory is at the root directory of the source tree.
@@ -92,6 +100,8 @@
 	ret.parallel = runtime.NumCPU() + 2
 	ret.keepGoing = 1
 
+	ret.totalRAM = detectTotalRAM(ctx)
+
 	ret.parseArgs(ctx, args)
 
 	// Make sure OUT_DIR is set appropriately
@@ -140,6 +150,7 @@
 		"DIST_DIR",
 
 		// Variables that have caused problems in the past
+		"BASH_ENV",
 		"CDPATH",
 		"DISPLAY",
 		"GREP_OPTIONS",
@@ -175,41 +186,50 @@
 
 	ret.environ.Set("TMPDIR", absPath(ctx, ret.TempDir()))
 
+	// Always set ASAN_SYMBOLIZER_PATH so that ASAN-based tools can symbolize any crashes
+	symbolizerPath := filepath.Join("prebuilts/clang/host", ret.HostPrebuiltTag(),
+		"llvm-binutils-stable/llvm-symbolizer")
+	ret.environ.Set("ASAN_SYMBOLIZER_PATH", absPath(ctx, symbolizerPath))
+
 	// Precondition: the current directory is the top of the source tree
 	checkTopDir(ctx)
 
 	if srcDir := absPath(ctx, "."); strings.ContainsRune(srcDir, ' ') {
-		log.Println("You are building in a directory whose absolute path contains a space character:")
-		log.Println()
-		log.Printf("%q\n", srcDir)
-		log.Println()
-		log.Fatalln("Directory names containing spaces are not supported")
+		ctx.Println("You are building in a directory whose absolute path contains a space character:")
+		ctx.Println()
+		ctx.Printf("%q\n", srcDir)
+		ctx.Println()
+		ctx.Fatalln("Directory names containing spaces are not supported")
 	}
 
 	if outDir := ret.OutDir(); strings.ContainsRune(outDir, ' ') {
-		log.Println("The absolute path of your output directory ($OUT_DIR) contains a space character:")
-		log.Println()
-		log.Printf("%q\n", outDir)
-		log.Println()
-		log.Fatalln("Directory names containing spaces are not supported")
+		ctx.Println("The absolute path of your output directory ($OUT_DIR) contains a space character:")
+		ctx.Println()
+		ctx.Printf("%q\n", outDir)
+		ctx.Println()
+		ctx.Fatalln("Directory names containing spaces are not supported")
 	}
 
 	if distDir := ret.DistDir(); strings.ContainsRune(distDir, ' ') {
-		log.Println("The absolute path of your dist directory ($DIST_DIR) contains a space character:")
-		log.Println()
-		log.Printf("%q\n", distDir)
-		log.Println()
-		log.Fatalln("Directory names containing spaces are not supported")
+		ctx.Println("The absolute path of your dist directory ($DIST_DIR) contains a space character:")
+		ctx.Println()
+		ctx.Printf("%q\n", distDir)
+		ctx.Println()
+		ctx.Fatalln("Directory names containing spaces are not supported")
 	}
 
 	// Configure Java-related variables, including adding it to $PATH
 	java8Home := filepath.Join("prebuilts/jdk/jdk8", ret.HostPrebuiltTag())
 	java9Home := filepath.Join("prebuilts/jdk/jdk9", ret.HostPrebuiltTag())
+	java11Home := filepath.Join("prebuilts/jdk/jdk11", ret.HostPrebuiltTag())
 	javaHome := func() string {
 		if override, ok := ret.environ.Get("OVERRIDE_ANDROID_JAVA_HOME"); ok {
 			return override
 		}
-		return java9Home
+		if toolchain11, ok := ret.environ.Get("EXPERIMENTAL_USE_OPENJDK11_TOOLCHAIN"); ok && toolchain11 != "true" {
+			ctx.Fatalln("The environment variable EXPERIMENTAL_USE_OPENJDK11_TOOLCHAIN is no longer supported. An OpenJDK 11 toolchain is now the global default.")
+		}
+		return java11Home
 	}()
 	absJavaHome := absPath(ctx, javaHome)
 
@@ -219,27 +239,25 @@
 	if path, ok := ret.environ.Get("PATH"); ok && path != "" {
 		newPath = append(newPath, path)
 	}
+
 	ret.environ.Unset("OVERRIDE_ANDROID_JAVA_HOME")
 	ret.environ.Set("JAVA_HOME", absJavaHome)
 	ret.environ.Set("ANDROID_JAVA_HOME", javaHome)
 	ret.environ.Set("ANDROID_JAVA8_HOME", java8Home)
 	ret.environ.Set("ANDROID_JAVA9_HOME", java9Home)
+	ret.environ.Set("ANDROID_JAVA11_HOME", java11Home)
 	ret.environ.Set("PATH", strings.Join(newPath, string(filepath.ListSeparator)))
 
 	outDir := ret.OutDir()
 	buildDateTimeFile := filepath.Join(outDir, "build_date.txt")
-	var content string
 	if buildDateTime, ok := ret.environ.Get("BUILD_DATETIME"); ok && buildDateTime != "" {
-		content = buildDateTime
+		ret.buildDateTime = buildDateTime
 	} else {
-		content = strconv.FormatInt(time.Now().Unix(), 10)
+		ret.buildDateTime = strconv.FormatInt(time.Now().Unix(), 10)
 	}
+
 	if ctx.Metrics != nil {
-		ctx.Metrics.SetBuildDateTime(content)
-	}
-	err := ioutil.WriteFile(buildDateTimeFile, []byte(content), 0777)
-	if err != nil {
-		ctx.Fatalln("Failed to write BUILD_DATETIME to file:", err)
+		ctx.Metrics.SetBuildDateTime(ret.buildDateTime)
 	}
 	ret.environ.Set("BUILD_DATETIME_FILE", buildDateTimeFile)
 
@@ -248,13 +266,13 @@
 
 // NewBuildActionConfig returns a build configuration based on the build action. The arguments are
 // processed based on the build action and extracts any arguments that belongs to the build action.
-func NewBuildActionConfig(action BuildAction, dir string, buildDependencies bool, ctx Context, args ...string) Config {
-	return NewConfig(ctx, getConfigArgs(action, dir, buildDependencies, ctx, args)...)
+func NewBuildActionConfig(action BuildAction, dir string, ctx Context, args ...string) Config {
+	return NewConfig(ctx, getConfigArgs(action, dir, ctx, args)...)
 }
 
 // getConfigArgs processes the command arguments based on the build action and creates a set of new
 // arguments to be accepted by Config.
-func getConfigArgs(action BuildAction, dir string, buildDependencies bool, ctx Context, args []string) []string {
+func getConfigArgs(action BuildAction, dir string, ctx Context, args []string) []string {
 	// The next block of code verifies that the current directory is the root directory of the source
 	// tree. It then finds the relative path of dir based on the root directory of the source tree
 	// and verify that dir is inside of the source tree.
@@ -263,6 +281,10 @@
 	if err != nil {
 		ctx.Fatalf("Error retrieving top directory: %v", err)
 	}
+	dir, err = filepath.EvalSymlinks(dir)
+	if err != nil {
+		ctx.Fatalf("Unable to evaluate symlink of %s: %v", dir, err)
+	}
 	dir, err = filepath.Abs(dir)
 	if err != nil {
 		ctx.Fatalf("Unable to find absolute path %s: %v", dir, err)
@@ -286,37 +308,27 @@
 		configArgs = removeFromList("GET-INSTALL-PATH", configArgs)
 	}
 
-	var buildFiles []string
 	var targets []string
 
 	switch action {
+	case BUILD_MODULES:
+		// No additional processing is required when building a list of specific modules or all modules.
 	case BUILD_MODULES_IN_A_DIRECTORY:
 		// If dir is the root source tree, all the modules are built of the source tree are built so
 		// no need to find the build file.
 		if topDir == dir {
 			break
 		}
-		// Find the build file from the directory where the build action was triggered by traversing up
-		// the source tree. If a blank build filename is returned, simply use the directory where the build
-		// action was invoked.
+
 		buildFile := findBuildFile(ctx, relDir)
 		if buildFile == "" {
-			buildFile = filepath.Join(relDir, "Android.mk")
+			ctx.Fatalf("Build file not found for %s directory", relDir)
 		}
-		buildFiles = []string{buildFile}
 		targets = []string{convertToTarget(filepath.Dir(buildFile), targetNamePrefix)}
 	case BUILD_MODULES_IN_DIRECTORIES:
 		newConfigArgs, dirs := splitArgs(configArgs)
 		configArgs = newConfigArgs
-		targets, buildFiles = getTargetsFromDirs(ctx, relDir, dirs, targetNamePrefix)
-	}
-
-	// This is to support building modules without building their dependencies. Soon, this will be
-	// deprecated.
-	if !buildDependencies && len(buildFiles) > 0 {
-		if err := os.Setenv("ONE_SHOT_MAKEFILE", strings.Join(buildFiles, " ")); err != nil {
-			ctx.Fatalf("Unable to set ONE_SHOT_MAKEFILE environment variable: %v", err)
-		}
+		targets = getTargetsFromDirs(ctx, relDir, dirs, targetNamePrefix)
 	}
 
 	// Tidy only override all other specified targets.
@@ -335,26 +347,57 @@
 	return targetNamePrefix + strings.ReplaceAll(dir, "/", "-")
 }
 
-// findBuildFile finds a build file (makefile or blueprint file) by looking at dir first. If not
-// found, go up one level and repeat again until one is found and the path of that build file
-// relative to the root directory of the source tree is returned. The returned filename of build
-// file is "Android.mk". If one was not found, a blank string is returned.
+// hasBuildFile returns true if dir contains an Android build file.
+func hasBuildFile(ctx Context, dir string) bool {
+	for _, buildFile := range buildFiles {
+		_, err := os.Stat(filepath.Join(dir, buildFile))
+		if err == nil {
+			return true
+		}
+		if !os.IsNotExist(err) {
+			ctx.Fatalf("Error retrieving the build file stats: %v", err)
+		}
+	}
+	return false
+}
+
+// findBuildFile finds a build file (makefile or blueprint file) by looking if there is a build file
+// in the current and any sub directory of dir. If a build file is not found, traverse the path
+// up by one directory and repeat again until either a build file is found or reached to the root
+// source tree. The returned filename of build file is "Android.mk". If one was not found, a blank
+// string is returned.
 func findBuildFile(ctx Context, dir string) string {
-	// If the string is empty, assume it is top directory of the source tree.
-	if dir == "" {
+	// If the string is empty or ".", assume it is top directory of the source tree.
+	if dir == "" || dir == "." {
 		return ""
 	}
 
-	for ; dir != "."; dir = filepath.Dir(dir) {
-		for _, buildFile := range []string{"Android.bp", "Android.mk"} {
-			_, err := os.Stat(filepath.Join(dir, buildFile))
-			if err == nil {
-				// Returning the filename Android.mk as it might be used for ONE_SHOT_MAKEFILE variable.
-				return filepath.Join(dir, "Android.mk")
+	found := false
+	for buildDir := dir; buildDir != "."; buildDir = filepath.Dir(buildDir) {
+		err := filepath.Walk(buildDir, func(path string, info os.FileInfo, err error) error {
+			if err != nil {
+				return err
 			}
-			if !os.IsNotExist(err) {
-				ctx.Fatalf("Error retrieving the build file stats: %v", err)
+			if found {
+				return filepath.SkipDir
 			}
+			if info.IsDir() {
+				return nil
+			}
+			for _, buildFile := range buildFiles {
+				if info.Name() == buildFile {
+					found = true
+					return filepath.SkipDir
+				}
+			}
+			return nil
+		})
+		if err != nil {
+			ctx.Fatalf("Error finding Android build file: %v", err)
+		}
+
+		if found {
+			return filepath.Join(buildDir, "Android.mk")
 		}
 	}
 
@@ -395,7 +438,7 @@
 // directory from the dirs list does not exist, a fatal error is raised. relDir is related to the
 // source root tree where the build action command was invoked. Each directory is validated if the
 // build file can be found and follows the format "dir1:target1,target2,...". Target is optional.
-func getTargetsFromDirs(ctx Context, relDir string, dirs []string, targetNamePrefix string) (targets []string, buildFiles []string) {
+func getTargetsFromDirs(ctx Context, relDir string, dirs []string, targetNamePrefix string) (targets []string) {
 	for _, dir := range dirs {
 		// The directory may have specified specific modules to build. ":" is the separator to separate
 		// the directory and the list of modules.
@@ -419,28 +462,25 @@
 			}
 		}
 
-		buildFile := findBuildFile(ctx, dir)
-		if buildFile == "" {
-			ctx.Fatalf("Build file not found for %s directory", dir)
-		}
-		buildFileDir := filepath.Dir(buildFile)
-
-		// If there are specified targets, find the build file in the directory. If dir does not
-		// contain the build file, bail out as it is required for one shot build. If there are no
-		// target specified, build all the modules in dir (or the closest one in the dir path).
+		// If there are specified targets to build in dir, an android build file must exist for the one
+		// shot build. For the non-targets case, find the appropriate build file and build all the
+		// modules in dir (or the closest one in the dir path).
 		if len(newTargets) > 0 {
-			if buildFileDir != dir {
+			if !hasBuildFile(ctx, dir) {
 				ctx.Fatalf("Couldn't locate a build file from %s directory", dir)
 			}
 		} else {
-			newTargets = []string{convertToTarget(buildFileDir, targetNamePrefix)}
+			buildFile := findBuildFile(ctx, dir)
+			if buildFile == "" {
+				ctx.Fatalf("Build file not found for %s directory", dir)
+			}
+			newTargets = []string{convertToTarget(filepath.Dir(buildFile), targetNamePrefix)}
 		}
 
-		buildFiles = append(buildFiles, buildFile)
 		targets = append(targets, newTargets...)
 	}
 
-	return targets, buildFiles
+	return targets
 }
 
 func (c *configImpl) parseArgs(ctx Context, args []string) {
@@ -599,7 +639,7 @@
 
 func (c *configImpl) OutDir() string {
 	if outDir, ok := c.environ.Get("OUT_DIR"); ok {
-		return filepath.Clean(outDir)
+		return outDir
 	}
 	return "out"
 }
@@ -682,6 +722,10 @@
 	return c.parallel
 }
 
+func (c *configImpl) TotalRAM() uint64 {
+	return c.totalRAM
+}
+
 func (c *configImpl) UseGoma() bool {
 	if v, ok := c.environ.Get("USE_GOMA"); ok {
 		v = strings.TrimSpace(v)
@@ -706,6 +750,34 @@
 	return true
 }
 
+func (c *configImpl) UseRBE() bool {
+	if v, ok := c.environ.Get("USE_RBE"); ok {
+		v = strings.TrimSpace(v)
+		if v != "" && v != "false" {
+			return true
+		}
+	}
+	return false
+}
+
+func (c *configImpl) StartRBE() bool {
+	if !c.UseRBE() {
+		return false
+	}
+
+	if v, ok := c.environ.Get("NOSTART_RBE"); ok {
+		v = strings.TrimSpace(v)
+		if v != "" && v != "false" {
+			return false
+		}
+	}
+	return true
+}
+
+func (c *configImpl) UseRemoteBuild() bool {
+	return c.UseGoma() || c.UseRBE()
+}
+
 // RemoteParallel controls how many remote jobs (i.e., commands which contain
 // gomacc) are run in parallel.  Note the parallelism of all other jobs is
 // still limited by Parallel()
@@ -836,6 +908,14 @@
 	return c.brokenUsesNetwork
 }
 
+func (c *configImpl) SetBuildBrokenNinjaUsesEnvVars(val []string) {
+	c.brokenNinjaEnvVars = val
+}
+
+func (c *configImpl) BuildBrokenNinjaUsesEnvVars() []string {
+	return c.brokenNinjaEnvVars
+}
+
 func (c *configImpl) SetTargetDeviceDir(dir string) {
 	c.targetDeviceDir = dir
 }
diff --git a/ui/build/config_darwin.go b/ui/build/config_darwin.go
new file mode 100644
index 0000000..480d8d1
--- /dev/null
+++ b/ui/build/config_darwin.go
@@ -0,0 +1,40 @@
+// 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 build
+
+import (
+	"encoding/binary"
+	"syscall"
+)
+
+func detectTotalRAM(ctx Context) uint64 {
+	s, err := syscall.Sysctl("hw.memsize")
+	if err != nil {
+		ctx.Printf("Failed to get system memory size: %s")
+		return 0
+	}
+
+	// syscall.Sysctl assumes that the return value is a string and trims the last byte if it is 0.
+	if len(s) == 7 {
+		s += "\x00"
+	}
+
+	if len(s) != 8 {
+		ctx.Printf("Failed to get system memory size, returned %d bytes, 8", len(s))
+		return 0
+	}
+
+	return binary.LittleEndian.Uint64([]byte(s))
+}
diff --git a/ui/build/config_linux.go b/ui/build/config_linux.go
new file mode 100644
index 0000000..9e1bdc7
--- /dev/null
+++ b/ui/build/config_linux.go
@@ -0,0 +1,28 @@
+// 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 build
+
+import "syscall"
+
+func detectTotalRAM(ctx Context) uint64 {
+	var info syscall.Sysinfo_t
+	err := syscall.Sysinfo(&info)
+	if err != nil {
+		ctx.Printf("Failed to get system memory size: %s")
+		return 0
+	}
+	memBytes := uint64(info.Totalram) * uint64(info.Unit)
+	return memBytes
+}
diff --git a/ui/build/config_test.go b/ui/build/config_test.go
index 1ef5456..df618c4 100644
--- a/ui/build/config_test.go
+++ b/ui/build/config_test.go
@@ -363,19 +363,15 @@
 		// Expected targets from the function.
 		expectedTargets []string
 
-		// Expected build from the build system.
-		expectedBuildFiles []string
-
 		// Expecting error from running test case.
 		errStr string
 	}{{
-		description:        "one target dir specified",
-		dirsInTrees:        []string{"0/1/2/3"},
-		buildFiles:         []string{"0/1/2/3/Android.bp"},
-		dirs:               []string{"1/2/3"},
-		curDir:             "0",
-		expectedTargets:    []string{"MODULES-IN-0-1-2-3"},
-		expectedBuildFiles: []string{"0/1/2/3/Android.mk"},
+		description:     "one target dir specified",
+		dirsInTrees:     []string{"0/1/2/3"},
+		buildFiles:      []string{"0/1/2/3/Android.bp"},
+		dirs:            []string{"1/2/3"},
+		curDir:          "0",
+		expectedTargets: []string{"MODULES-IN-0-1-2-3"},
 	}, {
 		description: "one target dir specified, build file does not exist",
 		dirsInTrees: []string{"0/1/2/3"},
@@ -391,21 +387,19 @@
 		curDir:      "0",
 		errStr:      "1/2/3:t1:t2 not in proper directory:target1,target2,... format (\":\" was specified more than once)",
 	}, {
-		description:        "one target dir specified, no targets specified but has colon",
-		dirsInTrees:        []string{"0/1/2/3"},
-		buildFiles:         []string{"0/1/2/3/Android.bp"},
-		dirs:               []string{"1/2/3:"},
-		curDir:             "0",
-		expectedTargets:    []string{"MODULES-IN-0-1-2-3"},
-		expectedBuildFiles: []string{"0/1/2/3/Android.mk"},
+		description:     "one target dir specified, no targets specified but has colon",
+		dirsInTrees:     []string{"0/1/2/3"},
+		buildFiles:      []string{"0/1/2/3/Android.bp"},
+		dirs:            []string{"1/2/3:"},
+		curDir:          "0",
+		expectedTargets: []string{"MODULES-IN-0-1-2-3"},
 	}, {
-		description:        "one target dir specified, two targets specified",
-		dirsInTrees:        []string{"0/1/2/3"},
-		buildFiles:         []string{"0/1/2/3/Android.bp"},
-		dirs:               []string{"1/2/3:t1,t2"},
-		curDir:             "0",
-		expectedTargets:    []string{"t1", "t2"},
-		expectedBuildFiles: []string{"0/1/2/3/Android.mk"},
+		description:     "one target dir specified, two targets specified",
+		dirsInTrees:     []string{"0/1/2/3"},
+		buildFiles:      []string{"0/1/2/3/Android.bp"},
+		dirs:            []string{"1/2/3:t1,t2"},
+		curDir:          "0",
+		expectedTargets: []string{"t1", "t2"},
 	}, {
 		description: "one target dir specified, no targets and has a comma",
 		dirsInTrees: []string{"0/1/2/3"},
@@ -428,20 +422,19 @@
 		curDir:      "0",
 		errStr:      "0/1/2/3 not in proper directory:target1,target2,... format",
 	}, {
-		description:        "one target dir specified, many targets specified",
-		dirsInTrees:        []string{"0/1/2/3"},
-		buildFiles:         []string{"0/1/2/3/Android.bp"},
-		dirs:               []string{"1/2/3:t1,t2,t3,t4,t5,t6,t7,t8,t9,t10"},
-		curDir:             "0",
-		expectedTargets:    []string{"t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9", "t10"},
-		expectedBuildFiles: []string{"0/1/2/3/Android.mk"},
+		description:     "one target dir specified, many targets specified",
+		dirsInTrees:     []string{"0/1/2/3"},
+		buildFiles:      []string{"0/1/2/3/Android.bp"},
+		dirs:            []string{"1/2/3:t1,t2,t3,t4,t5,t6,t7,t8,t9,t10"},
+		curDir:          "0",
+		expectedTargets: []string{"t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9", "t10"},
 	}, {
 		description: "one target dir specified, one target specified, build file does not exist",
 		dirsInTrees: []string{"0/1/2/3"},
 		buildFiles:  []string{},
 		dirs:        []string{"1/2/3:t1"},
 		curDir:      "0",
-		errStr:      "Build file not found for 0/1/2/3 directory",
+		errStr:      "Couldn't locate a build file from 0/1/2/3 directory",
 	}, {
 		description: "one target dir specified, one target specified, build file not in target dir",
 		dirsInTrees: []string{"0/1/2/3"},
@@ -450,29 +443,26 @@
 		curDir:      "0",
 		errStr:      "Couldn't locate a build file from 0/1/2/3 directory",
 	}, {
-		description:        "one target dir specified, build file not in target dir",
-		dirsInTrees:        []string{"0/1/2/3"},
-		buildFiles:         []string{"0/1/2/Android.mk"},
-		dirs:               []string{"1/2/3"},
-		curDir:             "0",
-		expectedTargets:    []string{"MODULES-IN-0-1-2"},
-		expectedBuildFiles: []string{"0/1/2/Android.mk"},
+		description:     "one target dir specified, build file not in target dir",
+		dirsInTrees:     []string{"0/1/2/3"},
+		buildFiles:      []string{"0/1/2/Android.mk"},
+		dirs:            []string{"1/2/3"},
+		curDir:          "0",
+		expectedTargets: []string{"MODULES-IN-0-1-2"},
 	}, {
-		description:        "multiple targets dir specified, targets specified",
-		dirsInTrees:        []string{"0/1/2/3", "0/3/4"},
-		buildFiles:         []string{"0/1/2/3/Android.bp", "0/3/4/Android.mk"},
-		dirs:               []string{"1/2/3:t1,t2", "3/4:t3,t4,t5"},
-		curDir:             "0",
-		expectedTargets:    []string{"t1", "t2", "t3", "t4", "t5"},
-		expectedBuildFiles: []string{"0/1/2/3/Android.mk", "0/3/4/Android.mk"},
+		description:     "multiple targets dir specified, targets specified",
+		dirsInTrees:     []string{"0/1/2/3", "0/3/4"},
+		buildFiles:      []string{"0/1/2/3/Android.bp", "0/3/4/Android.mk"},
+		dirs:            []string{"1/2/3:t1,t2", "3/4:t3,t4,t5"},
+		curDir:          "0",
+		expectedTargets: []string{"t1", "t2", "t3", "t4", "t5"},
 	}, {
-		description:        "multiple targets dir specified, one directory has targets specified",
-		dirsInTrees:        []string{"0/1/2/3", "0/3/4"},
-		buildFiles:         []string{"0/1/2/3/Android.bp", "0/3/4/Android.mk"},
-		dirs:               []string{"1/2/3:t1,t2", "3/4"},
-		curDir:             "0",
-		expectedTargets:    []string{"t1", "t2", "MODULES-IN-0-3-4"},
-		expectedBuildFiles: []string{"0/1/2/3/Android.mk", "0/3/4/Android.mk"},
+		description:     "multiple targets dir specified, one directory has targets specified",
+		dirsInTrees:     []string{"0/1/2/3", "0/3/4"},
+		buildFiles:      []string{"0/1/2/3/Android.bp", "0/3/4/Android.mk"},
+		dirs:            []string{"1/2/3:t1,t2", "3/4"},
+		curDir:          "0",
+		expectedTargets: []string{"t1", "t2", "MODULES-IN-0-3-4"},
 	}, {
 		description: "two dirs specified, only one dir exist",
 		dirsInTrees: []string{"0/1/2/3"},
@@ -481,13 +471,12 @@
 		curDir:      "0",
 		errStr:      "couldn't find directory 0/3/4",
 	}, {
-		description:        "multiple targets dirs specified at root source tree",
-		dirsInTrees:        []string{"0/1/2/3", "0/3/4"},
-		buildFiles:         []string{"0/1/2/3/Android.bp", "0/3/4/Android.mk"},
-		dirs:               []string{"0/1/2/3:t1,t2", "0/3/4"},
-		curDir:             ".",
-		expectedTargets:    []string{"t1", "t2", "MODULES-IN-0-3-4"},
-		expectedBuildFiles: []string{"0/1/2/3/Android.mk", "0/3/4/Android.mk"},
+		description:     "multiple targets dirs specified at root source tree",
+		dirsInTrees:     []string{"0/1/2/3", "0/3/4"},
+		buildFiles:      []string{"0/1/2/3/Android.bp", "0/3/4/Android.mk"},
+		dirs:            []string{"0/1/2/3:t1,t2", "0/3/4"},
+		curDir:          ".",
+		expectedTargets: []string{"t1", "t2", "MODULES-IN-0-3-4"},
 	}, {
 		description: "no directories specified",
 		dirsInTrees: []string{"0/1/2/3", "0/3/4"},
@@ -518,13 +507,10 @@
 			r := setTop(t, topDir)
 			defer r()
 
-			targets, buildFiles := getTargetsFromDirs(ctx, tt.curDir, tt.dirs, "MODULES-IN-")
+			targets := getTargetsFromDirs(ctx, tt.curDir, tt.dirs, "MODULES-IN-")
 			if !reflect.DeepEqual(targets, tt.expectedTargets) {
 				t.Errorf("expected %v, got %v for targets", tt.expectedTargets, targets)
 			}
-			if !reflect.DeepEqual(buildFiles, tt.expectedBuildFiles) {
-				t.Errorf("expected %v, got %v for build files", tt.expectedBuildFiles, buildFiles)
-			}
 
 			// If the execution reached here and there was an expected error code, the unit test case failed.
 			if tt.errStr != "" {
@@ -545,8 +531,11 @@
 		// Array of build files to create in dir.
 		buildFiles []string
 
+		// Directories that exist in the source tree.
+		dirsInTrees []string
+
 		// ********* Action *********
-		// Directory to create, also the base directory is where findBuildFile is invoked.
+		// The base directory is where findBuildFile is invoked.
 		dir string
 
 		// ********* Validation *********
@@ -555,38 +544,63 @@
 	}{{
 		description:       "build file exists at leaf directory",
 		buildFiles:        []string{"1/2/3/Android.bp"},
+		dirsInTrees:       []string{"1/2/3"},
 		dir:               "1/2/3",
 		expectedBuildFile: "1/2/3/Android.mk",
 	}, {
 		description:       "build file exists in all directory paths",
 		buildFiles:        []string{"1/Android.mk", "1/2/Android.mk", "1/2/3/Android.mk"},
+		dirsInTrees:       []string{"1/2/3"},
 		dir:               "1/2/3",
 		expectedBuildFile: "1/2/3/Android.mk",
 	}, {
 		description:       "build file does not exist in all directory paths",
 		buildFiles:        []string{},
+		dirsInTrees:       []string{"1/2/3"},
 		dir:               "1/2/3",
 		expectedBuildFile: "",
 	}, {
 		description:       "build file exists only at top directory",
 		buildFiles:        []string{"Android.bp"},
+		dirsInTrees:       []string{"1/2/3"},
 		dir:               "1/2/3",
 		expectedBuildFile: "",
 	}, {
 		description:       "build file exist in a subdirectory",
 		buildFiles:        []string{"1/2/Android.bp"},
+		dirsInTrees:       []string{"1/2/3"},
 		dir:               "1/2/3",
 		expectedBuildFile: "1/2/Android.mk",
 	}, {
 		description:       "build file exists in a subdirectory",
 		buildFiles:        []string{"1/Android.mk"},
+		dirsInTrees:       []string{"1/2/3"},
 		dir:               "1/2/3",
 		expectedBuildFile: "1/Android.mk",
 	}, {
 		description:       "top directory",
 		buildFiles:        []string{"Android.bp"},
+		dirsInTrees:       []string{},
 		dir:               ".",
 		expectedBuildFile: "",
+	}, {
+		description:       "build file exists in subdirectory",
+		buildFiles:        []string{"1/2/3/Android.bp", "1/2/4/Android.bp"},
+		dirsInTrees:       []string{"1/2/3", "1/2/4"},
+		dir:               "1/2",
+		expectedBuildFile: "1/2/Android.mk",
+	}, {
+		description:       "build file exists in parent subdirectory",
+		buildFiles:        []string{"1/5/Android.bp"},
+		dirsInTrees:       []string{"1/2/3", "1/2/4", "1/5"},
+		dir:               "1/2",
+		expectedBuildFile: "1/Android.mk",
+	}, {
+		description:       "build file exists in deep parent's subdirectory.",
+		buildFiles:        []string{"1/5/6/Android.bp"},
+		dirsInTrees:       []string{"1/2/3", "1/2/4", "1/5/6", "1/5/7"},
+		dir:               "1/2",
+		expectedBuildFile: "1/Android.mk",
 	}}
 
 	for _, tt := range tests {
@@ -601,10 +615,7 @@
 			}
 			defer os.RemoveAll(topDir)
 
-			if tt.dir != "" {
-				createDirectories(t, topDir, []string{tt.dir})
-			}
-
+			createDirectories(t, topDir, tt.dirsInTrees)
 			createBuildFiles(t, topDir, tt.buildFiles)
 
 			curDir, err := os.Getwd()
@@ -690,6 +701,9 @@
 	// Build files that exists in the source tree.
 	buildFiles []string
 
+	// Create root symlink that points to topDir.
+	rootSymlink bool
+
 	// ********* Action *********
 	// Arguments passed in to soong_ui.
 	args []string
@@ -704,16 +718,24 @@
 	// Expected arguments to be in Config instance.
 	expectedArgs []string
 
-	// Expected environment variables to be set.
-	expectedEnvVars []envVar
+	// Expecting error from running test case.
+	expectedErrStr string
 }
 
-func testGetConfigArgs(t *testing.T, tt buildActionTestCase, action BuildAction, buildDependencies bool) {
+func testGetConfigArgs(t *testing.T, tt buildActionTestCase, action BuildAction) {
 	ctx := testContext()
 
+	defer logger.Recover(func(err error) {
+		if tt.expectedErrStr == "" {
+			t.Fatalf("Got unexpected error: %v", err)
+		}
+		if tt.expectedErrStr != err.Error() {
+			t.Errorf("expected %s, got %s", tt.expectedErrStr, err.Error())
+		}
+	})
+
 	// Environment variables to set it to blank on every test case run.
 	resetEnvVars := []string{
-		"ONE_SHOT_MAKEFILE",
 		"WITH_TIDY_ONLY",
 	}
 
@@ -738,6 +760,22 @@
 	createDirectories(t, topDir, tt.dirsInTrees)
 	createBuildFiles(t, topDir, tt.buildFiles)
 
+	if tt.rootSymlink {
+		// Create a secondary root source tree which points to the true root source tree.
+		symlinkTopDir, err := ioutil.TempDir("", "")
+		if err != nil {
+			t.Fatalf("failed to create symlink temp dir: %v", err)
+		}
+		defer os.RemoveAll(symlinkTopDir)
+
+		symlinkTopDir = filepath.Join(symlinkTopDir, "root")
+		err = os.Symlink(topDir, symlinkTopDir)
+		if err != nil {
+			t.Fatalf("failed to create symlink: %v", err)
+		}
+		topDir = symlinkTopDir
+	}
+
 	r := setTop(t, topDir)
 	defer r()
 
@@ -751,245 +789,156 @@
 		t.Fatalf("failed to create %s file: %v", srcDirFileCheck, err)
 	}
 
-	args := getConfigArgs(action, tt.curDir, buildDependencies, ctx, tt.args)
+	args := getConfigArgs(action, tt.curDir, ctx, tt.args)
 	if !reflect.DeepEqual(tt.expectedArgs, args) {
 		t.Fatalf("expected %v, got %v for config arguments", tt.expectedArgs, args)
 	}
 
-	for _, env := range tt.expectedEnvVars {
-		if val := os.Getenv(env.name); val != env.value {
-			t.Errorf("expecting %s, got %s for environment variable %s", env.value, val, env.name)
-		}
+	// If the execution reached here and there was an expected error code, the unit test case failed.
+	if tt.expectedErrStr != "" {
+		t.Errorf("expecting error %s", tt.expectedErrStr)
 	}
 }
 
-// TODO: Remove this test case once mm shell build command has been deprecated.
-func TestGetConfigArgsBuildModulesInDirecotoryNoDeps(t *testing.T) {
+func TestGetConfigArgsBuildModules(t *testing.T) {
 	tests := []buildActionTestCase{{
-		description:  "normal execution in a directory",
-		dirsInTrees:  []string{"0/1/2"},
-		buildFiles:   []string{"0/1/2/Android.mk"},
-		args:         []string{"-j", "-k", "showcommands", "fake-module"},
-		curDir:       "0/1/2",
-		tidyOnly:     "",
-		expectedArgs: []string{"-j", "-k", "showcommands", "fake-module", "MODULES-IN-0-1-2"},
-		expectedEnvVars: []envVar{
-			envVar{
-				name:  "ONE_SHOT_MAKEFILE",
-				value: "0/1/2/Android.mk"}},
-	}, {
-		description:  "makefile in parent directory",
-		dirsInTrees:  []string{"0/1/2"},
-		buildFiles:   []string{"0/1/Android.mk"},
-		args:         []string{},
-		curDir:       "0/1/2",
-		tidyOnly:     "",
-		expectedArgs: []string{"MODULES-IN-0-1"},
-		expectedEnvVars: []envVar{
-			envVar{
-				name:  "ONE_SHOT_MAKEFILE",
-				value: "0/1/Android.mk"}},
-	}, {
-		description:  "build file not found",
-		dirsInTrees:  []string{"0/1/2"},
-		buildFiles:   []string{},
-		args:         []string{},
-		curDir:       "0/1/2",
-		tidyOnly:     "",
-		expectedArgs: []string{"MODULES-IN-0-1-2"},
-		expectedEnvVars: []envVar{
-			envVar{
-				name:  "ONE_SHOT_MAKEFILE",
-				value: "0/1/2/Android.mk"}},
-	}, {
-		description:  "build action executed at root directory",
-		dirsInTrees:  []string{},
-		buildFiles:   []string{},
-		args:         []string{},
+		description:  "normal execution from the root source tree directory",
+		dirsInTrees:  []string{"0/1/2", "0/2", "0/3"},
+		buildFiles:   []string{"0/1/2/Android.mk", "0/2/Android.bp", "0/3/Android.mk"},
+		args:         []string{"-j", "fake_module", "fake_module2"},
 		curDir:       ".",
 		tidyOnly:     "",
-		expectedArgs: []string{},
-		expectedEnvVars: []envVar{
-			envVar{
-				name:  "ONE_SHOT_MAKEFILE",
-				value: ""}},
+		expectedArgs: []string{"-j", "fake_module", "fake_module2"},
 	}, {
-		description:  "GET-INSTALL-PATH specified,",
-		dirsInTrees:  []string{"0/1/2"},
-		buildFiles:   []string{"0/1/Android.mk"},
-		args:         []string{"GET-INSTALL-PATH"},
-		curDir:       "0/1/2",
+		description:  "normal execution in deep directory",
+		dirsInTrees:  []string{"0/1/2", "0/2", "0/3", "1/2/3/4/5/6/7/8/9/1/2/3/4/5/6"},
+		buildFiles:   []string{"0/1/2/Android.mk", "0/2/Android.bp", "1/2/3/4/5/6/7/8/9/1/2/3/4/5/6/Android.mk"},
+		args:         []string{"-j", "fake_module", "fake_module2", "-k"},
+		curDir:       "1/2/3/4/5/6/7/8/9",
 		tidyOnly:     "",
-		expectedArgs: []string{"GET-INSTALL-PATH-IN-0-1"},
-		expectedEnvVars: []envVar{
-			envVar{
-				name:  "ONE_SHOT_MAKEFILE",
-				value: "0/1/Android.mk"}},
+		expectedArgs: []string{"-j", "fake_module", "fake_module2", "-k"},
 	}, {
-		description:  "tidy only environment variable specified,",
-		dirsInTrees:  []string{"0/1/2"},
-		buildFiles:   []string{"0/1/Android.mk"},
-		args:         []string{"GET-INSTALL-PATH"},
-		curDir:       "0/1/2",
-		tidyOnly:     "true",
-		expectedArgs: []string{"tidy_only"},
-		expectedEnvVars: []envVar{
-			envVar{
-				name:  "ONE_SHOT_MAKEFILE",
-				value: "0/1/Android.mk"}},
+		description:  "normal execution in deep directory, no targets",
+		dirsInTrees:  []string{"0/1/2", "0/2", "0/3", "1/2/3/4/5/6/7/8/9/1/2/3/4/5/6"},
+		buildFiles:   []string{"0/1/2/Android.mk", "0/2/Android.bp", "1/2/3/4/5/6/7/8/9/1/2/3/4/5/6/Android.mk"},
+		args:         []string{"-j", "-k"},
+		curDir:       "1/2/3/4/5/6/7/8/9",
+		tidyOnly:     "",
+		expectedArgs: []string{"-j", "-k"},
+	}, {
+		description:  "normal execution in root source tree, no args",
+		dirsInTrees:  []string{"0/1/2", "0/2", "0/3"},
+		buildFiles:   []string{"0/1/2/Android.mk", "0/2/Android.bp"},
+		args:         []string{},
+		curDir:       "0/2",
+		tidyOnly:     "",
+		expectedArgs: []string{},
+	}, {
+		description:  "normal execution in symlink root source tree, no args",
+		dirsInTrees:  []string{"0/1/2", "0/2", "0/3"},
+		buildFiles:   []string{"0/1/2/Android.mk", "0/2/Android.bp"},
+		rootSymlink:  true,
+		args:         []string{},
+		curDir:       "0/2",
+		tidyOnly:     "",
+		expectedArgs: []string{},
 	}}
 	for _, tt := range tests {
-		t.Run("build action BUILD_MODULES_IN_DIR without their dependencies, "+tt.description, func(t *testing.T) {
-			testGetConfigArgs(t, tt, BUILD_MODULES_IN_A_DIRECTORY, false)
+		t.Run("build action BUILD_MODULES with dependencies, "+tt.description, func(t *testing.T) {
+			testGetConfigArgs(t, tt, BUILD_MODULES)
 		})
 	}
 }
 
 func TestGetConfigArgsBuildModulesInDirectory(t *testing.T) {
 	tests := []buildActionTestCase{{
-		description:     "normal execution in a directory",
-		dirsInTrees:     []string{"0/1/2"},
-		buildFiles:      []string{"0/1/2/Android.mk"},
-		args:            []string{"fake-module"},
-		curDir:          "0/1/2",
-		tidyOnly:        "",
-		expectedArgs:    []string{"fake-module", "MODULES-IN-0-1-2"},
-		expectedEnvVars: []envVar{},
+		description:  "normal execution in a directory",
+		dirsInTrees:  []string{"0/1/2"},
+		buildFiles:   []string{"0/1/2/Android.mk"},
+		args:         []string{"fake-module"},
+		curDir:       "0/1/2",
+		tidyOnly:     "",
+		expectedArgs: []string{"fake-module", "MODULES-IN-0-1-2"},
 	}, {
-		description:     "build file in parent directory",
-		dirsInTrees:     []string{"0/1/2"},
-		buildFiles:      []string{"0/1/Android.mk"},
-		args:            []string{},
-		curDir:          "0/1/2",
-		tidyOnly:        "",
-		expectedArgs:    []string{"MODULES-IN-0-1"},
-		expectedEnvVars: []envVar{},
+		description:  "build file in parent directory",
+		dirsInTrees:  []string{"0/1/2"},
+		buildFiles:   []string{"0/1/Android.mk"},
+		args:         []string{},
+		curDir:       "0/1/2",
+		tidyOnly:     "",
+		expectedArgs: []string{"MODULES-IN-0-1"},
 	},
 		{
-			description:     "build file in parent directory, multiple module names passed in",
-			dirsInTrees:     []string{"0/1/2"},
-			buildFiles:      []string{"0/1/Android.mk"},
-			args:            []string{"fake-module1", "fake-module2", "fake-module3"},
-			curDir:          "0/1/2",
-			tidyOnly:        "",
-			expectedArgs:    []string{"fake-module1", "fake-module2", "fake-module3", "MODULES-IN-0-1"},
-			expectedEnvVars: []envVar{},
+			description:  "build file in parent directory, multiple module names passed in",
+			dirsInTrees:  []string{"0/1/2"},
+			buildFiles:   []string{"0/1/Android.mk"},
+			args:         []string{"fake-module1", "fake-module2", "fake-module3"},
+			curDir:       "0/1/2",
+			tidyOnly:     "",
+			expectedArgs: []string{"fake-module1", "fake-module2", "fake-module3", "MODULES-IN-0-1"},
 		}, {
-			description:     "build file in 2nd level parent directory",
-			dirsInTrees:     []string{"0/1/2"},
-			buildFiles:      []string{"0/Android.bp"},
-			args:            []string{},
-			curDir:          "0/1/2",
-			tidyOnly:        "",
-			expectedArgs:    []string{"MODULES-IN-0"},
-			expectedEnvVars: []envVar{},
+			description:  "build file in 2nd level parent directory",
+			dirsInTrees:  []string{"0/1/2"},
+			buildFiles:   []string{"0/Android.bp"},
+			args:         []string{},
+			curDir:       "0/1/2",
+			tidyOnly:     "",
+			expectedArgs: []string{"MODULES-IN-0"},
 		}, {
-			description:     "build action executed at root directory",
-			dirsInTrees:     []string{},
-			buildFiles:      []string{},
-			args:            []string{},
-			curDir:          ".",
-			tidyOnly:        "",
-			expectedArgs:    []string{},
-			expectedEnvVars: []envVar{},
+			description:  "build action executed at root directory",
+			dirsInTrees:  []string{},
+			buildFiles:   []string{},
+			rootSymlink:  false,
+			args:         []string{},
+			curDir:       ".",
+			tidyOnly:     "",
+			expectedArgs: []string{},
 		}, {
-			description:     "build file not found - no error is expected to return",
-			dirsInTrees:     []string{"0/1/2"},
-			buildFiles:      []string{},
-			args:            []string{},
-			curDir:          "0/1/2",
-			tidyOnly:        "",
-			expectedArgs:    []string{"MODULES-IN-0-1-2"},
-			expectedEnvVars: []envVar{},
+			description:  "build action executed at root directory in symlink",
+			dirsInTrees:  []string{},
+			buildFiles:   []string{},
+			rootSymlink:  true,
+			args:         []string{},
+			curDir:       ".",
+			tidyOnly:     "",
+			expectedArgs: []string{},
 		}, {
-			description:     "GET-INSTALL-PATH specified,",
-			dirsInTrees:     []string{"0/1/2"},
-			buildFiles:      []string{"0/1/Android.mk"},
-			args:            []string{"GET-INSTALL-PATH", "-j", "-k", "GET-INSTALL-PATH"},
-			curDir:          "0/1/2",
-			tidyOnly:        "",
-			expectedArgs:    []string{"-j", "-k", "GET-INSTALL-PATH-IN-0-1"},
-			expectedEnvVars: []envVar{},
+			description:    "build file not found",
+			dirsInTrees:    []string{"0/1/2"},
+			buildFiles:     []string{},
+			args:           []string{},
+			curDir:         "0/1/2",
+			tidyOnly:       "",
+			expectedArgs:   []string{"MODULES-IN-0-1-2"},
+			expectedErrStr: "Build file not found for 0/1/2 directory",
 		}, {
-			description:     "tidy only environment variable specified,",
-			dirsInTrees:     []string{"0/1/2"},
-			buildFiles:      []string{"0/1/Android.mk"},
-			args:            []string{"GET-INSTALL-PATH"},
-			curDir:          "0/1/2",
-			tidyOnly:        "true",
-			expectedArgs:    []string{"tidy_only"},
-			expectedEnvVars: []envVar{},
+			description:  "GET-INSTALL-PATH specified,",
+			dirsInTrees:  []string{"0/1/2"},
+			buildFiles:   []string{"0/1/Android.mk"},
+			args:         []string{"GET-INSTALL-PATH", "-j", "-k", "GET-INSTALL-PATH"},
+			curDir:       "0/1/2",
+			tidyOnly:     "",
+			expectedArgs: []string{"-j", "-k", "GET-INSTALL-PATH-IN-0-1"},
 		}, {
-			description:     "normal execution in root directory with args",
-			dirsInTrees:     []string{},
-			buildFiles:      []string{},
-			args:            []string{"-j", "-k", "fake_module"},
-			curDir:          "",
-			tidyOnly:        "",
-			expectedArgs:    []string{"-j", "-k", "fake_module"},
-			expectedEnvVars: []envVar{},
+			description:  "tidy only environment variable specified,",
+			dirsInTrees:  []string{"0/1/2"},
+			buildFiles:   []string{"0/1/Android.mk"},
+			args:         []string{"GET-INSTALL-PATH"},
+			curDir:       "0/1/2",
+			tidyOnly:     "true",
+			expectedArgs: []string{"tidy_only"},
+		}, {
+			description:  "normal execution in root directory with args",
+			dirsInTrees:  []string{},
+			buildFiles:   []string{},
+			args:         []string{"-j", "-k", "fake_module"},
+			curDir:       "",
+			tidyOnly:     "",
+			expectedArgs: []string{"-j", "-k", "fake_module"},
 		}}
 	for _, tt := range tests {
 		t.Run("build action BUILD_MODULES_IN_DIR, "+tt.description, func(t *testing.T) {
-			testGetConfigArgs(t, tt, BUILD_MODULES_IN_A_DIRECTORY, true)
-		})
-	}
-}
-
-// TODO: Remove this test case once mmm shell build command has been deprecated.
-func TestGetConfigArgsBuildModulesInDirectoriesNoDeps(t *testing.T) {
-	tests := []buildActionTestCase{{
-		description:  "normal execution in a directory",
-		dirsInTrees:  []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/2/3.3"},
-		buildFiles:   []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/2/3.3/Android.bp"},
-		args:         []string{"3.1/:t1,t2", "3.2/:t3,t4", "3.3/:t5,t6"},
-		curDir:       "0/1/2",
-		tidyOnly:     "",
-		expectedArgs: []string{"t1", "t2", "t3", "t4", "t5", "t6"},
-		expectedEnvVars: []envVar{
-			envVar{
-				name:  "ONE_SHOT_MAKEFILE",
-				value: "0/1/2/3.1/Android.mk 0/1/2/3.2/Android.mk 0/1/2/3.3/Android.mk"}},
-	}, {
-		description:  "GET-INSTALL-PATH specified",
-		dirsInTrees:  []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/2/3.3"},
-		buildFiles:   []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/2/3.3/Android.bp"},
-		args:         []string{"GET-INSTALL-PATH", "3.1/", "3.2/", "3.3/:t6"},
-		curDir:       "0/1/2",
-		tidyOnly:     "",
-		expectedArgs: []string{"GET-INSTALL-PATH-IN-0-1-2-3.1", "GET-INSTALL-PATH-IN-0-1-2-3.2", "t6"},
-		expectedEnvVars: []envVar{
-			envVar{
-				name:  "ONE_SHOT_MAKEFILE",
-				value: "0/1/2/3.1/Android.mk 0/1/2/3.2/Android.mk 0/1/2/3.3/Android.mk"}},
-	}, {
-		description:  "tidy only environment variable specified",
-		dirsInTrees:  []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/2/3.3"},
-		buildFiles:   []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/2/3.3/Android.bp"},
-		args:         []string{"GET-INSTALL-PATH", "3.1/", "3.2/", "3.3/:t6"},
-		curDir:       "0/1/2",
-		tidyOnly:     "1",
-		expectedArgs: []string{"tidy_only"},
-		expectedEnvVars: []envVar{
-			envVar{
-				name:  "ONE_SHOT_MAKEFILE",
-				value: "0/1/2/3.1/Android.mk 0/1/2/3.2/Android.mk 0/1/2/3.3/Android.mk"}},
-	}, {
-		description:  "normal execution from top dir directory",
-		dirsInTrees:  []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/2/3.3"},
-		buildFiles:   []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/2/3.3/Android.bp"},
-		args:         []string{"0/1/2/3.1", "0/1/2/3.2/:t3,t4", "0/1/2/3.3/:t5,t6"},
-		curDir:       ".",
-		tidyOnly:     "",
-		expectedArgs: []string{"MODULES-IN-0-1-2-3.1", "t3", "t4", "t5", "t6"},
-		expectedEnvVars: []envVar{
-			envVar{
-				name:  "ONE_SHOT_MAKEFILE",
-				value: "0/1/2/3.1/Android.mk 0/1/2/3.2/Android.mk 0/1/2/3.3/Android.mk"}},
-	}}
-	for _, tt := range tests {
-		t.Run("build action BUILD_MODULES_IN_DIRS_NO_DEPS, "+tt.description, func(t *testing.T) {
-			testGetConfigArgs(t, tt, BUILD_MODULES_IN_DIRECTORIES, false)
+			testGetConfigArgs(t, tt, BUILD_MODULES_IN_A_DIRECTORY)
 		})
 	}
 }
@@ -1003,10 +952,6 @@
 		curDir:       "0/1/2",
 		tidyOnly:     "",
 		expectedArgs: []string{"MODULES-IN-0-1-2-3.1", "MODULES-IN-0-1-2-3.2", "MODULES-IN-0-1-2-3.3"},
-		expectedEnvVars: []envVar{
-			envVar{
-				name:  "ONE_SHOT_MAKEFILE",
-				value: ""}},
 	}, {
 		description:  "GET-INSTALL-PATH specified",
 		dirsInTrees:  []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3"},
@@ -1015,10 +960,6 @@
 		curDir:       "0/1",
 		tidyOnly:     "",
 		expectedArgs: []string{"GET-INSTALL-PATH-IN-0-1-2-3.1", "GET-INSTALL-PATH-IN-0-1-2-3.2", "GET-INSTALL-PATH-IN-0-1"},
-		expectedEnvVars: []envVar{
-			envVar{
-				name:  "ONE_SHOT_MAKEFILE",
-				value: ""}},
 	}, {
 		description:  "tidy only environment variable specified",
 		dirsInTrees:  []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/2/3.3"},
@@ -1027,26 +968,28 @@
 		curDir:       "0/1/2",
 		tidyOnly:     "1",
 		expectedArgs: []string{"tidy_only"},
-		expectedEnvVars: []envVar{
-			envVar{
-				name:  "ONE_SHOT_MAKEFILE",
-				value: ""}},
 	}, {
 		description:  "normal execution from top dir directory",
 		dirsInTrees:  []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3", "0/2"},
 		buildFiles:   []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/3/Android.bp", "0/2/Android.bp"},
+		rootSymlink:  false,
 		args:         []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3", "0/2"},
 		curDir:       ".",
 		tidyOnly:     "",
 		expectedArgs: []string{"MODULES-IN-0-1-2-3.1", "MODULES-IN-0-1-2-3.2", "MODULES-IN-0-1-3", "MODULES-IN-0-2"},
-		expectedEnvVars: []envVar{
-			envVar{
-				name:  "ONE_SHOT_MAKEFILE",
-				value: ""}},
+	}, {
+		description:  "normal execution from top dir directory in symlink",
+		dirsInTrees:  []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3", "0/2"},
+		buildFiles:   []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/3/Android.bp", "0/2/Android.bp"},
+		rootSymlink:  true,
+		args:         []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3", "0/2"},
+		curDir:       ".",
+		tidyOnly:     "",
+		expectedArgs: []string{"MODULES-IN-0-1-2-3.1", "MODULES-IN-0-1-2-3.2", "MODULES-IN-0-1-3", "MODULES-IN-0-2"},
 	}}
 	for _, tt := range tests {
 		t.Run("build action BUILD_MODULES_IN_DIRS, "+tt.description, func(t *testing.T) {
-			testGetConfigArgs(t, tt, BUILD_MODULES_IN_DIRECTORIES, true)
+			testGetConfigArgs(t, tt, BUILD_MODULES_IN_DIRECTORIES)
 		})
 	}
 }
diff --git a/ui/build/dumpvars.go b/ui/build/dumpvars.go
index 266130f..c3da38b 100644
--- a/ui/build/dumpvars.go
+++ b/ui/build/dumpvars.go
@@ -17,6 +17,8 @@
 import (
 	"bytes"
 	"fmt"
+	"io/ioutil"
+	"os"
 	"strings"
 
 	"android/soong/ui/metrics"
@@ -40,6 +42,7 @@
 	soongUiVars := map[string]func() string{
 		"OUT_DIR":  func() string { return config.OutDir() },
 		"DIST_DIR": func() string { return config.DistDir() },
+		"TMPDIR":   func() string { return absPath(ctx, config.TempDir()) },
 	}
 
 	makeVars := make([]string, 0, len(vars))
@@ -51,7 +54,17 @@
 
 	var ret map[string]string
 	if len(makeVars) > 0 {
-		var err error
+		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)
+
 		ret, err = dumpMakeVars(ctx, config, goals, makeVars, false)
 		if err != nil {
 			return ret, err
@@ -169,7 +182,7 @@
 	// Variables to export into the environment of Kati/Ninja
 	exportEnvVars := []string{
 		// So that we can use the correct TARGET_PRODUCT if it's been
-		// modified by PRODUCT-*/APP-* arguments
+		// modified by a buildspec.mk
 		"TARGET_PRODUCT",
 		"TARGET_BUILD_VARIANT",
 		"TARGET_BUILD_APPS",
@@ -203,11 +216,16 @@
 		// Whether to enable the network during the build
 		"BUILD_BROKEN_USES_NETWORK",
 
+		// Extra environment variables to be exported to ninja
+		"BUILD_BROKEN_NINJA_USES_ENV_VARS",
+
 		// Not used, but useful to be in the soong.log
 		"BOARD_VNDK_VERSION",
 
 		"DEFAULT_WARNING_BUILD_MODULE_TYPES",
 		"DEFAULT_ERROR_BUILD_MODULE_TYPES",
+		"BUILD_BROKEN_PREBUILT_ELF_FILES",
+		"BUILD_BROKEN_TREBLE_SYSPROP_NEVERALLOW",
 		"BUILD_BROKEN_USES_BUILD_AUX_EXECUTABLE",
 		"BUILD_BROKEN_USES_BUILD_AUX_STATIC_LIBRARY",
 		"BUILD_BROKEN_USES_BUILD_COPY_HEADERS",
@@ -269,4 +287,5 @@
 	config.SetPdkBuild(make_vars["TARGET_BUILD_PDK"] == "true")
 	config.SetBuildBrokenDupRules(make_vars["BUILD_BROKEN_DUP_RULES"] == "true")
 	config.SetBuildBrokenUsesNetwork(make_vars["BUILD_BROKEN_USES_NETWORK"] == "true")
+	config.SetBuildBrokenNinjaUsesEnvVars(strings.Fields(make_vars["BUILD_BROKEN_NINJA_USES_ENV_VARS"]))
 }
diff --git a/ui/build/exec.go b/ui/build/exec.go
index 5c312bc..e435c53 100644
--- a/ui/build/exec.go
+++ b/ui/build/exec.go
@@ -15,7 +15,10 @@
 package build
 
 import (
+	"bufio"
+	"io"
 	"os/exec"
+	"strings"
 )
 
 // Cmd is a wrapper of os/exec.Cmd that integrates with the build context for
@@ -139,3 +142,34 @@
 	st.Finish()
 	c.reportError(err)
 }
+
+// RunAndStreamOrFatal will run the command, while running print
+// any output, then handle any errors with a call to ctx.Fatal
+func (c *Cmd) RunAndStreamOrFatal() {
+	out, err := c.StdoutPipe()
+	if err != nil {
+		c.ctx.Fatal(err)
+	}
+	c.Stderr = c.Stdout
+
+	st := c.ctx.Status.StartTool()
+
+	c.StartOrFatal()
+
+	buf := bufio.NewReaderSize(out, 2*1024*1024)
+	for {
+		// Attempt to read whole lines, but write partial lines that are too long to fit in the buffer or hit EOF
+		line, err := buf.ReadString('\n')
+		if line != "" {
+			st.Print(strings.TrimSuffix(line, "\n"))
+		} else if err == io.EOF {
+			break
+		} else if err != nil {
+			c.ctx.Fatal(err)
+		}
+	}
+
+	err = c.Wait()
+	st.Finish()
+	c.reportError(err)
+}
diff --git a/ui/build/goma.go b/ui/build/goma.go
index ff0b40e..ae9b784 100644
--- a/ui/build/goma.go
+++ b/ui/build/goma.go
@@ -72,6 +72,7 @@
 	}
 
 	cmd := Command(ctx, config, "goma_ctl.py ensure_start", gomaCtl, "ensure_start")
+	cmd.Environment.Set("DIST_DIR", config.DistDir())
 
 	if output, err := cmd.CombinedOutput(); err != nil {
 		ctx.Fatalf("goma_ctl.py ensure_start failed with: %v\n%s\n", err, output)
diff --git a/ui/build/kati.go b/ui/build/kati.go
index 5ad966a..a845c5b 100644
--- a/ui/build/kati.go
+++ b/ui/build/kati.go
@@ -42,9 +42,6 @@
 	if args := config.KatiArgs(); len(args) > 0 {
 		katiSuffix += "-" + spaceSlashReplacer.Replace(strings.Join(args, "_"))
 	}
-	if oneShot, ok := config.Environment().Get("ONE_SHOT_MAKEFILE"); ok {
-		katiSuffix += "-" + spaceSlashReplacer.Replace(oneShot)
-	}
 
 	// If the suffix is too long, replace it with a md5 hash and write a
 	// file that contains the original suffix.
@@ -92,6 +89,10 @@
 		args = append(args, "--empty_ninja_file")
 	}
 
+	if config.UseRemoteBuild() {
+		args = append(args, "--default_pool=local_pool")
+	}
+
 	cmd := Command(ctx, config, "ckati", executable, args...)
 	cmd.Sandbox = katiSandbox
 	pipe, err := cmd.StdoutPipe()
@@ -150,6 +151,63 @@
 		"KATI_PACKAGE_MK_DIR="+config.KatiPackageMkDir())
 
 	runKati(ctx, config, katiBuildSuffix, args, func(env *Environment) {})
+
+	cleanCopyHeaders(ctx, config)
+	cleanOldInstalledFiles(ctx, config)
+}
+
+func cleanCopyHeaders(ctx Context, config Config) {
+	ctx.BeginTrace("clean", "clean copy headers")
+	defer ctx.EndTrace()
+
+	data, err := ioutil.ReadFile(filepath.Join(config.ProductOut(), ".copied_headers_list"))
+	if err != nil {
+		if os.IsNotExist(err) {
+			return
+		}
+		ctx.Fatalf("Failed to read copied headers list: %v", err)
+	}
+
+	headers := strings.Fields(string(data))
+	if len(headers) < 1 {
+		ctx.Fatal("Failed to parse copied headers list: %q", string(data))
+	}
+	headerDir := headers[0]
+	headers = headers[1:]
+
+	filepath.Walk(headerDir,
+		func(path string, info os.FileInfo, err error) error {
+			if err != nil {
+				return nil
+			}
+			if info.IsDir() {
+				return nil
+			}
+			if !inList(path, headers) {
+				ctx.Printf("Removing obsolete header %q", path)
+				if err := os.Remove(path); err != nil {
+					ctx.Fatalf("Failed to remove obsolete header %q: %v", path, err)
+				}
+			}
+			return nil
+		})
+}
+
+func cleanOldInstalledFiles(ctx Context, config Config) {
+	ctx.BeginTrace("clean", "clean old installed files")
+	defer ctx.EndTrace()
+
+	// We shouldn't be removing files from one side of the two-step asan builds
+	var suffix string
+	if v, ok := config.Environment().Get("SANITIZE_TARGET"); ok {
+		if sanitize := strings.Fields(v); inList("address", sanitize) {
+			suffix = "_asan"
+		}
+	}
+
+	cleanOldFiles(ctx, config.ProductOut(), ".installable_files"+suffix)
+
+	cleanOldFiles(ctx, config.HostOut(), ".installable_test_files")
 }
 
 func runKatiPackage(ctx Context, config Config) {
@@ -174,6 +232,7 @@
 			"TMPDIR",
 
 			// Tool configs
+			"ASAN_SYMBOLIZER_PATH",
 			"JAVA_HOME",
 			"PYTHONDONTWRITEBYTECODE",
 
diff --git a/ui/build/ninja.go b/ui/build/ninja.go
index 7994f3a..0b56b67 100644
--- a/ui/build/ninja.go
+++ b/ui/build/ninja.go
@@ -18,6 +18,7 @@
 	"fmt"
 	"os"
 	"path/filepath"
+	"sort"
 	"strconv"
 	"strings"
 	"time"
@@ -43,7 +44,7 @@
 	args = append(args, config.NinjaArgs()...)
 
 	var parallel int
-	if config.UseGoma() {
+	if config.UseRemoteBuild() {
 		parallel = config.RemoteParallel()
 	} else {
 		parallel = config.Parallel()
@@ -65,8 +66,6 @@
 		cmd.Environment.AppendFromKati(config.KatiEnvFile())
 	}
 
-	cmd.Environment.Set("DIST_DIR", config.DistDir())
-
 	// Allow both NINJA_ARGS and NINJA_EXTRA_ARGS, since both have been
 	// used in the past to specify extra ninja arguments.
 	if extra, ok := cmd.Environment.Get("NINJA_ARGS"); ok {
@@ -85,6 +84,75 @@
 			ninjaHeartbeatDuration = overrideDuration
 		}
 	}
+
+	// Filter the environment, as ninja does not rebuild files when environment variables change.
+	//
+	// Anything listed here must not change the output of rules/actions when the value changes,
+	// otherwise incremental builds may be unsafe. Vars explicitly set to stable values
+	// elsewhere in soong_ui are fine.
+	//
+	// For the majority of cases, either Soong or the makefiles should be replicating any
+	// necessary environment variables in the command line of each action that needs it.
+	if cmd.Environment.IsEnvTrue("ALLOW_NINJA_ENV") {
+		ctx.Println("Allowing all environment variables during ninja; incremental builds may be unsafe.")
+	} else {
+		cmd.Environment.Allow(append([]string{
+			"ASAN_SYMBOLIZER_PATH",
+			"HOME",
+			"JAVA_HOME",
+			"LANG",
+			"LC_MESSAGES",
+			"OUT_DIR",
+			"PATH",
+			"PWD",
+			"PYTHONDONTWRITEBYTECODE",
+			"TMPDIR",
+			"USER",
+
+			// TODO: remove these carefully
+			"ASAN_OPTIONS",
+			"TARGET_BUILD_APPS",
+			"TARGET_BUILD_VARIANT",
+			"TARGET_PRODUCT",
+			// b/147197813 - used by art-check-debug-apex-gen
+			"EMMA_INSTRUMENT_FRAMEWORK",
+
+			// Goma -- gomacc may not need all of these
+			"GOMA_DIR",
+			"GOMA_DISABLED",
+			"GOMA_FAIL_FAST",
+			"GOMA_FALLBACK",
+			"GOMA_GCE_SERVICE_ACCOUNT",
+			"GOMA_TMP_DIR",
+			"GOMA_USE_LOCAL",
+
+			// RBE client
+			"FLAG_compare",
+			"FLAG_exec_root",
+			"FLAG_exec_strategy",
+			"FLAG_invocation_id",
+			"FLAG_log_dir",
+			"FLAG_platform",
+			"FLAG_server_address",
+
+			// ccache settings
+			"CCACHE_COMPILERCHECK",
+			"CCACHE_SLOPPINESS",
+			"CCACHE_BASEDIR",
+			"CCACHE_CPP2",
+		}, config.BuildBrokenNinjaUsesEnvVars()...)...)
+	}
+
+	cmd.Environment.Set("DIST_DIR", config.DistDir())
+	cmd.Environment.Set("SHELL", "/bin/bash")
+
+	ctx.Verboseln("Ninja environment: ")
+	envVars := cmd.Environment.Environ()
+	sort.Strings(envVars)
+	for _, envVar := range envVars {
+		ctx.Verbosef("  %s", envVar)
+	}
+
 	// Poll the ninja log for updates; if it isn't updated enough, then we want to show some diagnostics
 	done := make(chan struct{})
 	defer close(done)
@@ -103,7 +171,7 @@
 	}()
 
 	ctx.Status.Status("Starting ninja...")
-	cmd.RunAndPrintOrFatal()
+	cmd.RunAndStreamOrFatal()
 }
 
 type statusChecker struct {
diff --git a/ui/build/path.go b/ui/build/path.go
index 0e1c02c..c34ba1b 100644
--- a/ui/build/path.go
+++ b/ui/build/path.go
@@ -18,6 +18,7 @@
 	"fmt"
 	"io/ioutil"
 	"os"
+	"os/exec"
 	"path/filepath"
 	"runtime"
 	"strings"
@@ -53,6 +54,51 @@
 	return ret
 }
 
+// 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) {
+	if config.pathReplaced {
+		return
+	}
+
+	ctx.BeginTrace(metrics.RunSetupTool, "litepath")
+	defer ctx.EndTrace()
+
+	origPath, _ := config.Environment().Get("PATH")
+	myPath, _ := config.Environment().Get("TMPDIR")
+	myPath = filepath.Join(myPath, "path")
+	ensureEmptyDirectoriesExist(ctx, myPath)
+
+	os.Setenv("PATH", origPath)
+	for name, pathConfig := range paths.Configuration {
+		if !pathConfig.Symlink {
+			continue
+		}
+
+		origExec, err := exec.LookPath(name)
+		if err != nil {
+			continue
+		}
+		origExec, err = filepath.Abs(origExec)
+		if err != nil {
+			continue
+		}
+
+		err = os.Symlink(origExec, filepath.Join(myPath, name))
+		if err != nil {
+			ctx.Fatalln("Failed to create symlink:", err)
+		}
+	}
+
+	myPath, _ = filepath.Abs(myPath)
+
+	prebuiltsPath, _ := filepath.Abs("prebuilts/build-tools/path/" + runtime.GOOS + "-x86")
+	myPath = prebuiltsPath + string(os.PathListSeparator) + myPath
+
+	config.Environment().Set("PATH", myPath)
+	config.pathReplaced = true
+}
+
 func SetupPath(ctx Context, config Config) {
 	if config.pathReplaced {
 		return
diff --git a/ui/build/paths/config.go b/ui/build/paths/config.go
index 184e6f1..bfe662d 100644
--- a/ui/build/paths/config.go
+++ b/ui/build/paths/config.go
@@ -74,21 +74,14 @@
 }
 
 var Configuration = map[string]PathConfig{
-	"bash": Allowed,
-	"bc":   Allowed,
-	// We need bzip2 here even though we provide a bzip2 binary because
-	// GNU tar seems to avoid calling ours.
-	"bzip2":    Allowed,
+	"bash":     Allowed,
 	"dd":       Allowed,
 	"diff":     Allowed,
-	"egrep":    Allowed,
+	"dlv":      Allowed,
 	"expr":     Allowed,
-	"find":     Allowed,
 	"fuser":    Allowed,
 	"getopt":   Allowed,
 	"git":      Allowed,
-	"grep":     Allowed,
-	"gzip":     Allowed,
 	"hexdump":  Allowed,
 	"jar":      Allowed,
 	"java":     Allowed,
@@ -98,14 +91,11 @@
 	"patch":    Allowed,
 	"pstree":   Allowed,
 	"python3":  Allowed,
-	"realpath": Allowed,
 	"rsync":    Allowed,
 	"sh":       Allowed,
-	"tar":      Allowed,
 	"tr":       Allowed,
 	"unzip":    Allowed,
 	"zip":      Allowed,
-	"zipinfo":  Allowed,
 
 	// Host toolchain is removed. In-tree toolchain should be used instead.
 	// GCC also can't find cc1 with this implementation.
@@ -121,69 +111,18 @@
 	"ld.gold":    Forbidden,
 	"pkg-config": Forbidden,
 
-	// On Linux we'll use the toybox versions of these instead.
-	"basename":  LinuxOnlyPrebuilt,
-	"cat":       LinuxOnlyPrebuilt,
-	"chmod":     LinuxOnlyPrebuilt,
-	"cmp":       LinuxOnlyPrebuilt,
-	"cp":        LinuxOnlyPrebuilt,
-	"comm":      LinuxOnlyPrebuilt,
-	"cut":       LinuxOnlyPrebuilt,
-	"date":      LinuxOnlyPrebuilt,
-	"dirname":   LinuxOnlyPrebuilt,
-	"du":        LinuxOnlyPrebuilt,
-	"echo":      LinuxOnlyPrebuilt,
-	"env":       LinuxOnlyPrebuilt,
-	"head":      LinuxOnlyPrebuilt,
-	"getconf":   LinuxOnlyPrebuilt,
-	"hostname":  LinuxOnlyPrebuilt,
-	"id":        LinuxOnlyPrebuilt,
-	"ln":        LinuxOnlyPrebuilt,
-	"ls":        LinuxOnlyPrebuilt,
-	"md5sum":    LinuxOnlyPrebuilt,
-	"mkdir":     LinuxOnlyPrebuilt,
-	"mktemp":    LinuxOnlyPrebuilt,
-	"mv":        LinuxOnlyPrebuilt,
-	"od":        LinuxOnlyPrebuilt,
-	"paste":     LinuxOnlyPrebuilt,
-	"pgrep":     LinuxOnlyPrebuilt,
-	"pkill":     LinuxOnlyPrebuilt,
-	"ps":        LinuxOnlyPrebuilt,
-	"pwd":       LinuxOnlyPrebuilt,
-	"readlink":  LinuxOnlyPrebuilt,
-	"rm":        LinuxOnlyPrebuilt,
-	"rmdir":     LinuxOnlyPrebuilt,
-	"sed":       LinuxOnlyPrebuilt,
-	"seq":       LinuxOnlyPrebuilt,
-	"setsid":    LinuxOnlyPrebuilt,
-	"sha1sum":   LinuxOnlyPrebuilt,
-	"sha256sum": LinuxOnlyPrebuilt,
-	"sha512sum": LinuxOnlyPrebuilt,
-	"sleep":     LinuxOnlyPrebuilt,
-	"sort":      LinuxOnlyPrebuilt,
-	"stat":      LinuxOnlyPrebuilt,
-	"tail":      LinuxOnlyPrebuilt,
-	"tee":       LinuxOnlyPrebuilt,
-	"timeout":   LinuxOnlyPrebuilt,
-	"touch":     LinuxOnlyPrebuilt,
-	"true":      LinuxOnlyPrebuilt,
-	"uname":     LinuxOnlyPrebuilt,
-	"uniq":      LinuxOnlyPrebuilt,
-	"unix2dos":  LinuxOnlyPrebuilt,
-	"wc":        LinuxOnlyPrebuilt,
-	"whoami":    LinuxOnlyPrebuilt,
-	"which":     LinuxOnlyPrebuilt,
-	"xargs":     LinuxOnlyPrebuilt,
-	"xxd":       LinuxOnlyPrebuilt,
+	// These are toybox tools that only work on Linux.
+	"pgrep": LinuxOnlyPrebuilt,
+	"pkill": LinuxOnlyPrebuilt,
+	"ps":    LinuxOnlyPrebuilt,
 }
 
 func init() {
 	if runtime.GOOS == "darwin" {
-		Configuration["md5"] = Allowed
 		Configuration["sw_vers"] = Allowed
 		Configuration["xcrun"] = Allowed
 
-		// We don't have darwin prebuilts for some tools (like toybox),
+		// We don't have darwin prebuilts for some tools,
 		// so allow the host versions.
 		for name, config := range Configuration {
 			if config.LinuxOnlyPrebuilt {
diff --git a/ui/build/rbe.go b/ui/build/rbe.go
new file mode 100644
index 0000000..ceea4bf
--- /dev/null
+++ b/ui/build/rbe.go
@@ -0,0 +1,52 @@
+// 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 build
+
+import (
+	"path/filepath"
+
+	"android/soong/ui/metrics"
+)
+
+const bootstrapCmd = "bootstrap"
+const rbeLeastNProcs = 2500
+const rbeLeastNFiles = 16000
+
+func startRBE(ctx Context, config Config) {
+	ctx.BeginTrace(metrics.RunSetupTool, "rbe_bootstrap")
+	defer ctx.EndTrace()
+
+	if u := ulimitOrFatal(ctx, config, "-u"); u < rbeLeastNProcs {
+		ctx.Fatalf("max user processes is insufficient: %d; want >= %d.\n", u, rbeLeastNProcs)
+	}
+	if n := ulimitOrFatal(ctx, config, "-n"); n < rbeLeastNFiles {
+		ctx.Fatalf("max open files is insufficient: %d; want >= %d.\n", n, rbeLeastNFiles)
+	}
+
+	var rbeBootstrap string
+	if rbeDir, ok := config.Environment().Get("RBE_DIR"); ok {
+		rbeBootstrap = filepath.Join(rbeDir, bootstrapCmd)
+	} else if home, ok := config.Environment().Get("HOME"); ok {
+		rbeBootstrap = filepath.Join(home, "rbe", bootstrapCmd)
+	} else {
+		ctx.Fatalln("rbe bootstrap not found")
+	}
+
+	cmd := Command(ctx, config, "boostrap", rbeBootstrap)
+
+	if output, err := cmd.CombinedOutput(); err != nil {
+		ctx.Fatalf("rbe bootstrap failed with: %v\n%s\n", err, output)
+	}
+}
diff --git a/ui/build/sandbox_linux.go b/ui/build/sandbox_linux.go
index b94db74..11ff667 100644
--- a/ui/build/sandbox_linux.go
+++ b/ui/build/sandbox_linux.go
@@ -162,6 +162,10 @@
 		c.ctx.Printf("AllowBuildBrokenUsesNetwork: %v", c.Sandbox.AllowBuildBrokenUsesNetwork)
 		c.ctx.Printf("BuildBrokenUsesNetwork: %v", c.config.BuildBrokenUsesNetwork())
 		sandboxArgs = append(sandboxArgs, "-N")
+	} else if dlv, _ := c.config.Environment().Get("SOONG_DELVE"); dlv != "" {
+		// The debugger is enabled and soong_build will pause until a remote delve process connects, allow
+		// network connections.
+		sandboxArgs = append(sandboxArgs, "-N")
 	}
 
 	// Stop nsjail from parsing arguments
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 2ce1ac9..afbc073 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -119,8 +119,9 @@
 			"-j", strconv.Itoa(config.Parallel()),
 			"--frontend_file", fifo,
 			"-f", filepath.Join(config.SoongOutDir(), file))
+		cmd.Environment.Set("SOONG_SANDBOX_SOONG_BUILD", "true")
 		cmd.Sandbox = soongSandbox
-		cmd.RunAndPrintOrFatal()
+		cmd.RunAndStreamOrFatal()
 	}
 
 	ninja("minibootstrap", ".minibootstrap/build.ninja")
diff --git a/ui/metrics/metrics.go b/ui/metrics/metrics.go
index bc86f0a..64bbbf3 100644
--- a/ui/metrics/metrics.go
+++ b/ui/metrics/metrics.go
@@ -19,9 +19,9 @@
 	"os"
 	"strconv"
 
-	"android/soong/ui/metrics/metrics_proto"
-
 	"github.com/golang/protobuf/proto"
+
+	soong_metrics_proto "android/soong/ui/metrics/metrics_proto"
 )
 
 const (
@@ -137,13 +137,32 @@
 	}
 }
 
-func (m *Metrics) Serialize() (data []byte, err error) {
-	return proto.Marshal(&m.metrics)
-}
-
 // exports the output to the file at outputPath
 func (m *Metrics) Dump(outputPath string) (err error) {
-	data, err := m.Serialize()
+	return writeMessageToFile(&m.metrics, outputPath)
+}
+
+type CriticalUserJourneysMetrics struct {
+	cujs soong_metrics_proto.CriticalUserJourneysMetrics
+}
+
+func NewCriticalUserJourneysMetrics() *CriticalUserJourneysMetrics {
+	return &CriticalUserJourneysMetrics{}
+}
+
+func (c *CriticalUserJourneysMetrics) Add(name string, metrics *Metrics) {
+	c.cujs.Cujs = append(c.cujs.Cujs, &soong_metrics_proto.CriticalUserJourneyMetrics{
+		Name:    proto.String(name),
+		Metrics: &metrics.metrics,
+	})
+}
+
+func (c *CriticalUserJourneysMetrics) Dump(outputPath string) (err error) {
+	return writeMessageToFile(&c.cujs, outputPath)
+}
+
+func writeMessageToFile(pb proto.Message, outputPath string) (err error) {
+	data, err := proto.Marshal(pb)
 	if err != nil {
 		return err
 	}
diff --git a/ui/metrics/metrics_proto/metrics.pb.go b/ui/metrics/metrics_proto/metrics.pb.go
index 5486ec1..0fe5a0d 100644
--- a/ui/metrics/metrics_proto/metrics.pb.go
+++ b/ui/metrics/metrics_proto/metrics.pb.go
@@ -509,6 +509,95 @@
 	return 0
 }
 
+type CriticalUserJourneyMetrics struct {
+	// The name of a critical user journey test.
+	Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
+	// The metrics produced when running the critical user journey test.
+	Metrics              *MetricsBase `protobuf:"bytes,2,opt,name=metrics" json:"metrics,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}     `json:"-"`
+	XXX_unrecognized     []byte       `json:"-"`
+	XXX_sizecache        int32        `json:"-"`
+}
+
+func (m *CriticalUserJourneyMetrics) Reset()         { *m = CriticalUserJourneyMetrics{} }
+func (m *CriticalUserJourneyMetrics) String() string { return proto.CompactTextString(m) }
+func (*CriticalUserJourneyMetrics) ProtoMessage()    {}
+func (*CriticalUserJourneyMetrics) Descriptor() ([]byte, []int) {
+	return fileDescriptor_6039342a2ba47b72, []int{3}
+}
+
+func (m *CriticalUserJourneyMetrics) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_CriticalUserJourneyMetrics.Unmarshal(m, b)
+}
+func (m *CriticalUserJourneyMetrics) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_CriticalUserJourneyMetrics.Marshal(b, m, deterministic)
+}
+func (m *CriticalUserJourneyMetrics) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_CriticalUserJourneyMetrics.Merge(m, src)
+}
+func (m *CriticalUserJourneyMetrics) XXX_Size() int {
+	return xxx_messageInfo_CriticalUserJourneyMetrics.Size(m)
+}
+func (m *CriticalUserJourneyMetrics) XXX_DiscardUnknown() {
+	xxx_messageInfo_CriticalUserJourneyMetrics.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_CriticalUserJourneyMetrics proto.InternalMessageInfo
+
+func (m *CriticalUserJourneyMetrics) GetName() string {
+	if m != nil && m.Name != nil {
+		return *m.Name
+	}
+	return ""
+}
+
+func (m *CriticalUserJourneyMetrics) GetMetrics() *MetricsBase {
+	if m != nil {
+		return m.Metrics
+	}
+	return nil
+}
+
+type CriticalUserJourneysMetrics struct {
+	// A set of metrics from a run of the critical user journey tests.
+	Cujs                 []*CriticalUserJourneyMetrics `protobuf:"bytes,1,rep,name=cujs" json:"cujs,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}                      `json:"-"`
+	XXX_unrecognized     []byte                        `json:"-"`
+	XXX_sizecache        int32                         `json:"-"`
+}
+
+func (m *CriticalUserJourneysMetrics) Reset()         { *m = CriticalUserJourneysMetrics{} }
+func (m *CriticalUserJourneysMetrics) String() string { return proto.CompactTextString(m) }
+func (*CriticalUserJourneysMetrics) ProtoMessage()    {}
+func (*CriticalUserJourneysMetrics) Descriptor() ([]byte, []int) {
+	return fileDescriptor_6039342a2ba47b72, []int{4}
+}
+
+func (m *CriticalUserJourneysMetrics) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_CriticalUserJourneysMetrics.Unmarshal(m, b)
+}
+func (m *CriticalUserJourneysMetrics) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_CriticalUserJourneysMetrics.Marshal(b, m, deterministic)
+}
+func (m *CriticalUserJourneysMetrics) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_CriticalUserJourneysMetrics.Merge(m, src)
+}
+func (m *CriticalUserJourneysMetrics) XXX_Size() int {
+	return xxx_messageInfo_CriticalUserJourneysMetrics.Size(m)
+}
+func (m *CriticalUserJourneysMetrics) XXX_DiscardUnknown() {
+	xxx_messageInfo_CriticalUserJourneysMetrics.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_CriticalUserJourneysMetrics proto.InternalMessageInfo
+
+func (m *CriticalUserJourneysMetrics) GetCujs() []*CriticalUserJourneyMetrics {
+	if m != nil {
+		return m.Cujs
+	}
+	return nil
+}
+
 func init() {
 	proto.RegisterEnum("soong_build_metrics.MetricsBase_BuildVariant", MetricsBase_BuildVariant_name, MetricsBase_BuildVariant_value)
 	proto.RegisterEnum("soong_build_metrics.MetricsBase_Arch", MetricsBase_Arch_name, MetricsBase_Arch_value)
@@ -516,59 +605,65 @@
 	proto.RegisterType((*MetricsBase)(nil), "soong_build_metrics.MetricsBase")
 	proto.RegisterType((*PerfInfo)(nil), "soong_build_metrics.PerfInfo")
 	proto.RegisterType((*ModuleTypeInfo)(nil), "soong_build_metrics.ModuleTypeInfo")
+	proto.RegisterType((*CriticalUserJourneyMetrics)(nil), "soong_build_metrics.CriticalUserJourneyMetrics")
+	proto.RegisterType((*CriticalUserJourneysMetrics)(nil), "soong_build_metrics.CriticalUserJourneysMetrics")
 }
 
 func init() { proto.RegisterFile("metrics.proto", fileDescriptor_6039342a2ba47b72) }
 
 var fileDescriptor_6039342a2ba47b72 = []byte{
-	// 769 bytes of a gzipped FileDescriptorProto
+	// 834 bytes of a gzipped FileDescriptorProto
 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x55, 0x6f, 0x6b, 0xdb, 0x46,
-	0x18, 0xaf, 0x62, 0x25, 0x96, 0x1e, 0xc5, 0xae, 0x7a, 0xc9, 0xa8, 0xca, 0x08, 0x33, 0x66, 0x1d,
-	0x7e, 0xb1, 0xba, 0xc5, 0x14, 0x53, 0x4c, 0x19, 0xd8, 0x89, 0x29, 0x25, 0xd8, 0x2e, 0x4a, 0xdc,
-	0x95, 0xed, 0xc5, 0xa1, 0x4a, 0xe7, 0x46, 0x9b, 0xa5, 0x13, 0x77, 0xa7, 0x32, 0x7f, 0x88, 0x7d,
-	0x93, 0x7d, 0xad, 0x7d, 0x8f, 0x71, 0xcf, 0x49, 0x8e, 0x02, 0x81, 0x85, 0xbe, 0x3b, 0x3d, 0xbf,
-	0x3f, 0xf7, 0x7b, 0x4e, 0xba, 0x47, 0xd0, 0xc9, 0x98, 0x12, 0x69, 0x2c, 0x87, 0x85, 0xe0, 0x8a,
-	0x93, 0x13, 0xc9, 0x79, 0xfe, 0x85, 0x7e, 0x2e, 0xd3, 0x6d, 0x42, 0x2b, 0xa8, 0xff, 0x8f, 0x0b,
-	0xde, 0xc2, 0xac, 0x67, 0x91, 0x64, 0xe4, 0x15, 0x9c, 0x1a, 0x42, 0x12, 0x29, 0x46, 0x55, 0x9a,
-	0x31, 0xa9, 0xa2, 0xac, 0x08, 0xac, 0x9e, 0x35, 0x68, 0x85, 0x04, 0xb1, 0x8b, 0x48, 0xb1, 0xeb,
-	0x1a, 0x21, 0xcf, 0xc0, 0x31, 0x8a, 0x34, 0x09, 0x0e, 0x7a, 0xd6, 0xc0, 0x0d, 0xdb, 0xf8, 0xfc,
-	0x3e, 0x21, 0x13, 0x78, 0x56, 0x6c, 0x23, 0xb5, 0xe1, 0x22, 0xa3, 0x5f, 0x99, 0x90, 0x29, 0xcf,
-	0x69, 0xcc, 0x13, 0x96, 0x47, 0x19, 0x0b, 0x5a, 0xc8, 0x7d, 0x5a, 0x13, 0x3e, 0x1a, 0xfc, 0xbc,
-	0x82, 0xc9, 0x73, 0xe8, 0xaa, 0x48, 0x7c, 0x61, 0x8a, 0x16, 0x82, 0x27, 0x65, 0xac, 0x02, 0x1b,
-	0x05, 0x1d, 0x53, 0xfd, 0x60, 0x8a, 0x24, 0x81, 0xd3, 0x8a, 0x66, 0x42, 0x7c, 0x8d, 0x44, 0x1a,
-	0xe5, 0x2a, 0x38, 0xec, 0x59, 0x83, 0xee, 0xe8, 0xc5, 0xf0, 0x9e, 0x9e, 0x87, 0x8d, 0x7e, 0x87,
-	0x33, 0x8d, 0x7c, 0x34, 0xa2, 0x49, 0x6b, 0xbe, 0x7c, 0x17, 0x12, 0xe3, 0xd7, 0x04, 0xc8, 0x0a,
-	0xbc, 0x6a, 0x97, 0x48, 0xc4, 0x37, 0xc1, 0x11, 0x9a, 0x3f, 0xff, 0x5f, 0xf3, 0xa9, 0x88, 0x6f,
-	0x26, 0xed, 0xf5, 0xf2, 0x72, 0xb9, 0xfa, 0x75, 0x19, 0x82, 0xb1, 0xd0, 0x45, 0x32, 0x84, 0x93,
-	0x86, 0xe1, 0x3e, 0x75, 0x1b, 0x5b, 0x7c, 0x72, 0x4b, 0xac, 0x03, 0xfc, 0x0c, 0x55, 0x2c, 0x1a,
-	0x17, 0xe5, 0x9e, 0xee, 0x20, 0xdd, 0x37, 0xc8, 0x79, 0x51, 0xd6, 0xec, 0x4b, 0x70, 0x6f, 0xb8,
-	0xac, 0xc2, 0xba, 0xdf, 0x14, 0xd6, 0xd1, 0x06, 0x18, 0x35, 0x84, 0x0e, 0x9a, 0x8d, 0xf2, 0xc4,
-	0x18, 0xc2, 0x37, 0x19, 0x7a, 0xda, 0x64, 0x94, 0x27, 0xe8, 0xf9, 0x14, 0xda, 0xe8, 0xc9, 0x65,
-	0xe0, 0x61, 0x0f, 0x47, 0xfa, 0x71, 0x25, 0x49, 0xbf, 0xda, 0x8c, 0x4b, 0xca, 0xfe, 0x52, 0x22,
-	0x0a, 0x8e, 0x11, 0xf6, 0x0c, 0x3c, 0xd7, 0xa5, 0x3d, 0x27, 0x16, 0x5c, 0x4a, 0x6d, 0xd1, 0xb9,
-	0xe5, 0x9c, 0xeb, 0xda, 0x4a, 0x92, 0x9f, 0xe0, 0x71, 0x83, 0x83, 0xb1, 0xbb, 0xe6, 0xf3, 0xd9,
-	0xb3, 0x30, 0xc8, 0x0b, 0x38, 0x69, 0xf0, 0xf6, 0x2d, 0x3e, 0x36, 0x07, 0xbb, 0xe7, 0x36, 0x72,
-	0xf3, 0x52, 0xd1, 0x24, 0x15, 0x81, 0x6f, 0x72, 0xf3, 0x52, 0x5d, 0xa4, 0x82, 0xfc, 0x02, 0x9e,
-	0x64, 0xaa, 0x2c, 0xa8, 0xe2, 0x7c, 0x2b, 0x83, 0x27, 0xbd, 0xd6, 0xc0, 0x1b, 0x9d, 0xdd, 0x7b,
-	0x44, 0x1f, 0x98, 0xd8, 0xbc, 0xcf, 0x37, 0x3c, 0x04, 0x54, 0x5c, 0x6b, 0x01, 0x99, 0x80, 0xfb,
-	0x67, 0xa4, 0x52, 0x2a, 0xca, 0x5c, 0x06, 0xe4, 0x21, 0x6a, 0x47, 0xf3, 0xc3, 0x32, 0x97, 0xe4,
-	0x2d, 0x80, 0x61, 0xa2, 0xf8, 0xe4, 0x21, 0x62, 0x17, 0xd1, 0x5a, 0x9d, 0xa7, 0xf9, 0x1f, 0x91,
-	0x51, 0x9f, 0x3e, 0x48, 0x8d, 0x02, 0xad, 0xee, 0xbf, 0x82, 0xe3, 0x3b, 0x17, 0xc5, 0x01, 0x7b,
-	0x7d, 0x35, 0x0f, 0xfd, 0x47, 0xa4, 0x03, 0xae, 0x5e, 0x5d, 0xcc, 0x67, 0xeb, 0x77, 0xbe, 0x45,
-	0xda, 0xa0, 0x2f, 0x97, 0x7f, 0xd0, 0x7f, 0x0b, 0x36, 0x1e, 0xa5, 0x07, 0xf5, 0xa7, 0xe1, 0x3f,
-	0xd2, 0xe8, 0x34, 0x5c, 0xf8, 0x16, 0x71, 0xe1, 0x70, 0x1a, 0x2e, 0xc6, 0xaf, 0xfd, 0x03, 0x5d,
-	0xfb, 0xf4, 0x66, 0xec, 0xb7, 0x08, 0xc0, 0xd1, 0xa7, 0x37, 0x63, 0x3a, 0x7e, 0xed, 0xdb, 0xfd,
-	0xbf, 0x2d, 0x70, 0xea, 0x1c, 0x84, 0x80, 0x9d, 0x30, 0x19, 0xe3, 0x6c, 0x72, 0x43, 0x5c, 0xeb,
-	0x1a, 0x4e, 0x17, 0x33, 0x89, 0x70, 0x4d, 0xce, 0x00, 0xa4, 0x8a, 0x84, 0xc2, 0x71, 0x86, 0x73,
-	0xc7, 0x0e, 0x5d, 0xac, 0xe8, 0x29, 0x46, 0xbe, 0x07, 0x57, 0xb0, 0x68, 0x6b, 0x50, 0x1b, 0x51,
-	0x47, 0x17, 0x10, 0x3c, 0x03, 0xc8, 0x58, 0xc6, 0xc5, 0x8e, 0x96, 0x92, 0xe1, 0x54, 0xb1, 0x43,
-	0xd7, 0x54, 0xd6, 0x92, 0xf5, 0xff, 0xb5, 0xa0, 0xbb, 0xe0, 0x49, 0xb9, 0x65, 0xd7, 0xbb, 0x82,
-	0x61, 0xaa, 0xdf, 0xe1, 0xd8, 0x9c, 0x9b, 0xdc, 0x49, 0xc5, 0x32, 0x4c, 0xd7, 0x1d, 0xbd, 0xbc,
-	0xff, 0xba, 0xdc, 0x91, 0x9a, 0x61, 0x74, 0x85, 0xb2, 0xc6, 0xc5, 0xf9, 0x7c, 0x5b, 0x25, 0x3f,
-	0x80, 0x97, 0xa1, 0x86, 0xaa, 0x5d, 0x51, 0x77, 0x09, 0xd9, 0xde, 0x86, 0xfc, 0x08, 0xdd, 0xbc,
-	0xcc, 0x28, 0xdf, 0x50, 0x53, 0x94, 0xd8, 0x6f, 0x27, 0x3c, 0xce, 0xcb, 0x6c, 0xb5, 0x31, 0xfb,
-	0xc9, 0xfe, 0x4b, 0xf0, 0x1a, 0x7b, 0xdd, 0x7d, 0x17, 0x2e, 0x1c, 0x5e, 0xad, 0x56, 0x4b, 0xfd,
-	0xd2, 0x1c, 0xb0, 0x17, 0xd3, 0xcb, 0xb9, 0x7f, 0x30, 0xfb, 0xee, 0xb7, 0xea, 0xef, 0x51, 0x25,
-	0xa7, 0xf8, 0x4b, 0xf9, 0x2f, 0x00, 0x00, 0xff, 0xff, 0x81, 0xd0, 0x84, 0x23, 0x62, 0x06, 0x00,
-	0x00,
+	0x18, 0xaf, 0x62, 0x25, 0xb6, 0x1e, 0xc5, 0xae, 0x7a, 0xc9, 0xa8, 0xba, 0x12, 0x66, 0xc4, 0x3a,
+	0xf2, 0x62, 0x75, 0x8b, 0x29, 0xa1, 0x98, 0x32, 0x48, 0x1c, 0x53, 0xba, 0x60, 0xbb, 0x28, 0x71,
+	0x57, 0xb6, 0x17, 0x87, 0x22, 0x9d, 0x1b, 0x75, 0x96, 0x4e, 0xdc, 0x9d, 0xca, 0xfc, 0x21, 0xf6,
+	0x4d, 0xf6, 0xb5, 0xf6, 0x3d, 0xc6, 0x3d, 0x27, 0x39, 0x0a, 0x78, 0x34, 0xf4, 0xdd, 0xe9, 0xf9,
+	0xfd, 0xb9, 0xdf, 0x73, 0xd2, 0x3d, 0x82, 0x6e, 0xc6, 0x94, 0x48, 0x63, 0x39, 0x28, 0x04, 0x57,
+	0x9c, 0x1c, 0x48, 0xce, 0xf3, 0x4f, 0xf4, 0xba, 0x4c, 0x57, 0x09, 0xad, 0xa0, 0xe0, 0x1f, 0x07,
+	0xdc, 0xa9, 0x59, 0x9f, 0x45, 0x92, 0x91, 0x97, 0x70, 0x68, 0x08, 0x49, 0xa4, 0x18, 0x55, 0x69,
+	0xc6, 0xa4, 0x8a, 0xb2, 0xc2, 0xb7, 0xfa, 0xd6, 0x71, 0x2b, 0x24, 0x88, 0x9d, 0x47, 0x8a, 0x5d,
+	0xd5, 0x08, 0x79, 0x02, 0x1d, 0xa3, 0x48, 0x13, 0x7f, 0xa7, 0x6f, 0x1d, 0x3b, 0x61, 0x1b, 0x9f,
+	0xdf, 0x25, 0x64, 0x04, 0x4f, 0x8a, 0x55, 0xa4, 0x96, 0x5c, 0x64, 0xf4, 0x0b, 0x13, 0x32, 0xe5,
+	0x39, 0x8d, 0x79, 0xc2, 0xf2, 0x28, 0x63, 0x7e, 0x0b, 0xb9, 0x8f, 0x6b, 0xc2, 0x07, 0x83, 0x8f,
+	0x2b, 0x98, 0x3c, 0x83, 0x9e, 0x8a, 0xc4, 0x27, 0xa6, 0x68, 0x21, 0x78, 0x52, 0xc6, 0xca, 0xb7,
+	0x51, 0xd0, 0x35, 0xd5, 0xf7, 0xa6, 0x48, 0x12, 0x38, 0xac, 0x68, 0x26, 0xc4, 0x97, 0x48, 0xa4,
+	0x51, 0xae, 0xfc, 0xdd, 0xbe, 0x75, 0xdc, 0x1b, 0x3e, 0x1f, 0x6c, 0xe9, 0x79, 0xd0, 0xe8, 0x77,
+	0x70, 0xa6, 0x91, 0x0f, 0x46, 0x34, 0x6a, 0x4d, 0x66, 0x6f, 0x43, 0x62, 0xfc, 0x9a, 0x00, 0x99,
+	0x83, 0x5b, 0xed, 0x12, 0x89, 0xf8, 0xc6, 0xdf, 0x43, 0xf3, 0x67, 0x5f, 0x35, 0x3f, 0x15, 0xf1,
+	0xcd, 0xa8, 0xbd, 0x98, 0x5d, 0xcc, 0xe6, 0xbf, 0xcd, 0x42, 0x30, 0x16, 0xba, 0x48, 0x06, 0x70,
+	0xd0, 0x30, 0xdc, 0xa4, 0x6e, 0x63, 0x8b, 0x8f, 0x6e, 0x89, 0x75, 0x80, 0x9f, 0xa1, 0x8a, 0x45,
+	0xe3, 0xa2, 0xdc, 0xd0, 0x3b, 0x48, 0xf7, 0x0c, 0x32, 0x2e, 0xca, 0x9a, 0x7d, 0x01, 0xce, 0x0d,
+	0x97, 0x55, 0x58, 0xe7, 0x9b, 0xc2, 0x76, 0xb4, 0x01, 0x46, 0x0d, 0xa1, 0x8b, 0x66, 0xc3, 0x3c,
+	0x31, 0x86, 0xf0, 0x4d, 0x86, 0xae, 0x36, 0x19, 0xe6, 0x09, 0x7a, 0x3e, 0x86, 0x36, 0x7a, 0x72,
+	0xe9, 0xbb, 0xd8, 0xc3, 0x9e, 0x7e, 0x9c, 0x4b, 0x12, 0x54, 0x9b, 0x71, 0x49, 0xd9, 0x5f, 0x4a,
+	0x44, 0xfe, 0x3e, 0xc2, 0xae, 0x81, 0x27, 0xba, 0xb4, 0xe1, 0xc4, 0x82, 0x4b, 0xa9, 0x2d, 0xba,
+	0xb7, 0x9c, 0xb1, 0xae, 0xcd, 0x25, 0xf9, 0x09, 0x1e, 0x36, 0x38, 0x18, 0xbb, 0x67, 0x3e, 0x9f,
+	0x0d, 0x0b, 0x83, 0x3c, 0x87, 0x83, 0x06, 0x6f, 0xd3, 0xe2, 0x43, 0x73, 0xb0, 0x1b, 0x6e, 0x23,
+	0x37, 0x2f, 0x15, 0x4d, 0x52, 0xe1, 0x7b, 0x26, 0x37, 0x2f, 0xd5, 0x79, 0x2a, 0xc8, 0x2f, 0xe0,
+	0x4a, 0xa6, 0xca, 0x82, 0x2a, 0xce, 0x57, 0xd2, 0x7f, 0xd4, 0x6f, 0x1d, 0xbb, 0xc3, 0xa3, 0xad,
+	0x47, 0xf4, 0x9e, 0x89, 0xe5, 0xbb, 0x7c, 0xc9, 0x43, 0x40, 0xc5, 0x95, 0x16, 0x90, 0x11, 0x38,
+	0x7f, 0x46, 0x2a, 0xa5, 0xa2, 0xcc, 0xa5, 0x4f, 0xee, 0xa3, 0xee, 0x68, 0x7e, 0x58, 0xe6, 0x92,
+	0xbc, 0x01, 0x30, 0x4c, 0x14, 0x1f, 0xdc, 0x47, 0xec, 0x20, 0x5a, 0xab, 0xf3, 0x34, 0xff, 0x1c,
+	0x19, 0xf5, 0xe1, 0xbd, 0xd4, 0x28, 0xd0, 0xea, 0xe0, 0x25, 0xec, 0xdf, 0xb9, 0x28, 0x1d, 0xb0,
+	0x17, 0x97, 0x93, 0xd0, 0x7b, 0x40, 0xba, 0xe0, 0xe8, 0xd5, 0xf9, 0xe4, 0x6c, 0xf1, 0xd6, 0xb3,
+	0x48, 0x1b, 0xf4, 0xe5, 0xf2, 0x76, 0x82, 0x37, 0x60, 0xe3, 0x51, 0xba, 0x50, 0x7f, 0x1a, 0xde,
+	0x03, 0x8d, 0x9e, 0x86, 0x53, 0xcf, 0x22, 0x0e, 0xec, 0x9e, 0x86, 0xd3, 0x93, 0x57, 0xde, 0x8e,
+	0xae, 0x7d, 0x7c, 0x7d, 0xe2, 0xb5, 0x08, 0xc0, 0xde, 0xc7, 0xd7, 0x27, 0xf4, 0xe4, 0x95, 0x67,
+	0x07, 0x7f, 0x5b, 0xd0, 0xa9, 0x73, 0x10, 0x02, 0x76, 0xc2, 0x64, 0x8c, 0xb3, 0xc9, 0x09, 0x71,
+	0xad, 0x6b, 0x38, 0x5d, 0xcc, 0x24, 0xc2, 0x35, 0x39, 0x02, 0x90, 0x2a, 0x12, 0x0a, 0xc7, 0x19,
+	0xce, 0x1d, 0x3b, 0x74, 0xb0, 0xa2, 0xa7, 0x18, 0x79, 0x0a, 0x8e, 0x60, 0xd1, 0xca, 0xa0, 0x36,
+	0xa2, 0x1d, 0x5d, 0x40, 0xf0, 0x08, 0x20, 0x63, 0x19, 0x17, 0x6b, 0x5a, 0x4a, 0x86, 0x53, 0xc5,
+	0x0e, 0x1d, 0x53, 0x59, 0x48, 0x16, 0xfc, 0x6b, 0x41, 0x6f, 0xca, 0x93, 0x72, 0xc5, 0xae, 0xd6,
+	0x05, 0xc3, 0x54, 0x7f, 0xc0, 0xbe, 0x39, 0x37, 0xb9, 0x96, 0x8a, 0x65, 0x98, 0xae, 0x37, 0x7c,
+	0xb1, 0xfd, 0xba, 0xdc, 0x91, 0x9a, 0x61, 0x74, 0x89, 0xb2, 0xc6, 0xc5, 0xb9, 0xbe, 0xad, 0x92,
+	0x1f, 0xc0, 0xcd, 0x50, 0x43, 0xd5, 0xba, 0xa8, 0xbb, 0x84, 0x6c, 0x63, 0x43, 0x7e, 0x84, 0x5e,
+	0x5e, 0x66, 0x94, 0x2f, 0xa9, 0x29, 0x4a, 0xec, 0xb7, 0x1b, 0xee, 0xe7, 0x65, 0x36, 0x5f, 0x9a,
+	0xfd, 0x64, 0xf0, 0x02, 0xdc, 0xc6, 0x5e, 0x77, 0xdf, 0x85, 0x03, 0xbb, 0x97, 0xf3, 0xf9, 0x4c,
+	0xbf, 0xb4, 0x0e, 0xd8, 0xd3, 0xd3, 0x8b, 0x89, 0xb7, 0x13, 0xac, 0xe0, 0xfb, 0xb1, 0x48, 0x55,
+	0x1a, 0x47, 0xab, 0x85, 0x64, 0xe2, 0x57, 0x5e, 0x8a, 0x9c, 0xad, 0xab, 0xdb, 0xbe, 0x39, 0x74,
+	0xab, 0x71, 0xe8, 0x23, 0x68, 0x57, 0x5d, 0x62, 0x4a, 0x77, 0xd8, 0xff, 0xda, 0xc0, 0x08, 0x6b,
+	0x41, 0x70, 0x0d, 0x4f, 0xb7, 0xec, 0x26, 0xeb, 0xed, 0xc6, 0x60, 0xc7, 0xe5, 0x67, 0xe9, 0x5b,
+	0xf8, 0xb1, 0x6e, 0x3f, 0xd9, 0xff, 0x4f, 0x1b, 0xa2, 0xf8, 0xec, 0xbb, 0xdf, 0xab, 0xff, 0x61,
+	0xa5, 0xa0, 0xf8, 0x93, 0xfc, 0x2f, 0x00, 0x00, 0xff, 0xff, 0x20, 0x07, 0xbc, 0xf0, 0x34, 0x07,
+	0x00, 0x00,
 }
diff --git a/ui/metrics/metrics_proto/metrics.proto b/ui/metrics/metrics_proto/metrics.proto
index 93034eb..1ea24bf 100644
--- a/ui/metrics/metrics_proto/metrics.proto
+++ b/ui/metrics/metrics_proto/metrics.proto
@@ -125,3 +125,16 @@
   // The number of logical modules.
   optional uint32 num_of_modules = 3;
 }
+
+message CriticalUserJourneyMetrics {
+  // The name of a critical user journey test.
+  optional string name = 1;
+
+  // The metrics produced when running the critical user journey test.
+  optional MetricsBase metrics = 2;
+}
+
+message CriticalUserJourneysMetrics {
+  // A set of metrics from a run of the critical user journey tests.
+  repeated CriticalUserJourneyMetrics cujs = 1;
+}
\ No newline at end of file
diff --git a/ui/status/Android.bp b/ui/status/Android.bp
index 901a713..ec929b3 100644
--- a/ui/status/Android.bp
+++ b/ui/status/Android.bp
@@ -19,14 +19,17 @@
         "golang-protobuf-proto",
         "soong-ui-logger",
         "soong-ui-status-ninja_frontend",
+        "soong-ui-status-build_error_proto",
     ],
     srcs: [
+        "critical_path.go",
         "kati.go",
         "log.go",
         "ninja.go",
         "status.go",
     ],
     testSrcs: [
+        "critical_path_test.go",
         "kati_test.go",
         "ninja_test.go",
         "status_test.go",
@@ -41,3 +44,12 @@
         "ninja_frontend/frontend.pb.go",
     ],
 }
+
+bootstrap_go_package {
+    name: "soong-ui-status-build_error_proto",
+    pkgPath: "android/soong/ui/status/build_error_proto",
+    deps: ["golang-protobuf-proto"],
+    srcs: [
+        "build_error_proto/build_error.pb.go",
+    ],
+}
diff --git a/ui/status/build_error_proto/build_error.pb.go b/ui/status/build_error_proto/build_error.pb.go
new file mode 100644
index 0000000..d4d0a6e
--- /dev/null
+++ b/ui/status/build_error_proto/build_error.pb.go
@@ -0,0 +1,175 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: build_error.proto
+
+package soong_build_error_proto
+
+import (
+	fmt "fmt"
+	proto "github.com/golang/protobuf/proto"
+	math "math"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+
+type BuildError struct {
+	// List of error messages of the overall build. The error messages
+	// are not associated with a build action.
+	ErrorMessages []string `protobuf:"bytes,1,rep,name=error_messages,json=errorMessages" json:"error_messages,omitempty"`
+	// List of build action errors.
+	ActionErrors         []*BuildActionError `protobuf:"bytes,2,rep,name=action_errors,json=actionErrors" json:"action_errors,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}            `json:"-"`
+	XXX_unrecognized     []byte              `json:"-"`
+	XXX_sizecache        int32               `json:"-"`
+}
+
+func (m *BuildError) Reset()         { *m = BuildError{} }
+func (m *BuildError) String() string { return proto.CompactTextString(m) }
+func (*BuildError) ProtoMessage()    {}
+func (*BuildError) Descriptor() ([]byte, []int) {
+	return fileDescriptor_a2e15b05802a5501, []int{0}
+}
+
+func (m *BuildError) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_BuildError.Unmarshal(m, b)
+}
+func (m *BuildError) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_BuildError.Marshal(b, m, deterministic)
+}
+func (m *BuildError) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_BuildError.Merge(m, src)
+}
+func (m *BuildError) XXX_Size() int {
+	return xxx_messageInfo_BuildError.Size(m)
+}
+func (m *BuildError) XXX_DiscardUnknown() {
+	xxx_messageInfo_BuildError.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_BuildError proto.InternalMessageInfo
+
+func (m *BuildError) GetErrorMessages() []string {
+	if m != nil {
+		return m.ErrorMessages
+	}
+	return nil
+}
+
+func (m *BuildError) GetActionErrors() []*BuildActionError {
+	if m != nil {
+		return m.ActionErrors
+	}
+	return nil
+}
+
+// Build is composed of a list of build action. There can be a set of build
+// actions that can failed.
+type BuildActionError struct {
+	// Description of the command.
+	Description *string `protobuf:"bytes,1,opt,name=description" json:"description,omitempty"`
+	// The command name that raised the error.
+	Command *string `protobuf:"bytes,2,opt,name=command" json:"command,omitempty"`
+	// The command output stream.
+	Output *string `protobuf:"bytes,3,opt,name=output" json:"output,omitempty"`
+	// List of artifacts (i.e. files) that was produced by the command.
+	Artifacts []string `protobuf:"bytes,4,rep,name=artifacts" json:"artifacts,omitempty"`
+	// The error string produced by the build action.
+	Error                *string  `protobuf:"bytes,5,opt,name=error" json:"error,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *BuildActionError) Reset()         { *m = BuildActionError{} }
+func (m *BuildActionError) String() string { return proto.CompactTextString(m) }
+func (*BuildActionError) ProtoMessage()    {}
+func (*BuildActionError) Descriptor() ([]byte, []int) {
+	return fileDescriptor_a2e15b05802a5501, []int{1}
+}
+
+func (m *BuildActionError) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_BuildActionError.Unmarshal(m, b)
+}
+func (m *BuildActionError) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_BuildActionError.Marshal(b, m, deterministic)
+}
+func (m *BuildActionError) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_BuildActionError.Merge(m, src)
+}
+func (m *BuildActionError) XXX_Size() int {
+	return xxx_messageInfo_BuildActionError.Size(m)
+}
+func (m *BuildActionError) XXX_DiscardUnknown() {
+	xxx_messageInfo_BuildActionError.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_BuildActionError proto.InternalMessageInfo
+
+func (m *BuildActionError) GetDescription() string {
+	if m != nil && m.Description != nil {
+		return *m.Description
+	}
+	return ""
+}
+
+func (m *BuildActionError) GetCommand() string {
+	if m != nil && m.Command != nil {
+		return *m.Command
+	}
+	return ""
+}
+
+func (m *BuildActionError) GetOutput() string {
+	if m != nil && m.Output != nil {
+		return *m.Output
+	}
+	return ""
+}
+
+func (m *BuildActionError) GetArtifacts() []string {
+	if m != nil {
+		return m.Artifacts
+	}
+	return nil
+}
+
+func (m *BuildActionError) GetError() string {
+	if m != nil && m.Error != nil {
+		return *m.Error
+	}
+	return ""
+}
+
+func init() {
+	proto.RegisterType((*BuildError)(nil), "soong_build_error.BuildError")
+	proto.RegisterType((*BuildActionError)(nil), "soong_build_error.BuildActionError")
+}
+
+func init() { proto.RegisterFile("build_error.proto", fileDescriptor_a2e15b05802a5501) }
+
+var fileDescriptor_a2e15b05802a5501 = []byte{
+	// 229 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x90, 0xc1, 0x4a, 0xc3, 0x40,
+	0x10, 0x86, 0x49, 0x63, 0x95, 0x4c, 0xad, 0xd8, 0x41, 0x74, 0x04, 0x0f, 0xa1, 0x22, 0xe4, 0x94,
+	0x83, 0x6f, 0x60, 0x41, 0xf0, 0xe2, 0x25, 0x47, 0x2f, 0x61, 0xdd, 0xac, 0x65, 0xc1, 0x64, 0xc2,
+	0xce, 0xe6, 0xe8, 0x8b, 0xf8, 0xb4, 0x92, 0x69, 0xa5, 0xa5, 0x39, 0x7e, 0xdf, 0x3f, 0xfb, 0xef,
+	0xce, 0xc2, 0xea, 0x73, 0xf0, 0xdf, 0x4d, 0xed, 0x42, 0xe0, 0x50, 0xf6, 0x81, 0x23, 0xe3, 0x4a,
+	0x98, 0xbb, 0x6d, 0x7d, 0x14, 0xac, 0x7f, 0x00, 0x36, 0x23, 0xbe, 0x8e, 0x84, 0x4f, 0x70, 0xa5,
+	0xba, 0x6e, 0x9d, 0x88, 0xd9, 0x3a, 0xa1, 0x24, 0x4f, 0x8b, 0xac, 0x5a, 0xaa, 0x7d, 0xdf, 0x4b,
+	0x7c, 0x83, 0xa5, 0xb1, 0xd1, 0x73, 0xb7, 0x2b, 0x11, 0x9a, 0xe5, 0x69, 0xb1, 0x78, 0x7e, 0x2c,
+	0x27, 0xfd, 0xa5, 0x96, 0xbf, 0xe8, 0xb0, 0x5e, 0x51, 0x5d, 0x9a, 0x03, 0xc8, 0xfa, 0x37, 0x81,
+	0xeb, 0xd3, 0x11, 0xcc, 0x61, 0xd1, 0x38, 0xb1, 0xc1, 0xf7, 0xa3, 0xa3, 0x24, 0x4f, 0x8a, 0xac,
+	0x3a, 0x56, 0x48, 0x70, 0x61, 0xb9, 0x6d, 0x4d, 0xd7, 0xd0, 0x4c, 0xd3, 0x7f, 0xc4, 0x5b, 0x38,
+	0xe7, 0x21, 0xf6, 0x43, 0xa4, 0x54, 0x83, 0x3d, 0xe1, 0x03, 0x64, 0x26, 0x44, 0xff, 0x65, 0x6c,
+	0x14, 0x3a, 0xd3, 0xa5, 0x0e, 0x02, 0x6f, 0x60, 0xae, 0xcf, 0xa5, 0xb9, 0x1e, 0xda, 0xc1, 0xe6,
+	0xfe, 0xe3, 0x6e, 0xb2, 0x50, 0xad, 0x3f, 0xf9, 0x17, 0x00, 0x00, 0xff, 0xff, 0xb6, 0x18, 0x9e,
+	0x17, 0x5d, 0x01, 0x00, 0x00,
+}
diff --git a/ui/status/build_error_proto/build_error.proto b/ui/status/build_error_proto/build_error.proto
new file mode 100644
index 0000000..9c8470d
--- /dev/null
+++ b/ui/status/build_error_proto/build_error.proto
@@ -0,0 +1,46 @@
+// 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.
+
+syntax = "proto2";
+
+package soong_build_error;
+option go_package = "soong_build_error_proto";
+
+message BuildError {
+  // List of error messages of the overall build. The error messages
+  // are not associated with a build action.
+  repeated string error_messages = 1;
+
+  // List of build action errors.
+  repeated BuildActionError action_errors = 2;
+}
+
+// Build is composed of a list of build action. There can be a set of build
+// actions that can failed.
+message BuildActionError {
+  // Description of the command.
+  optional string description = 1;
+
+  // The command name that raised the error.
+  optional string command = 2;
+
+  // The command output stream.
+  optional string output = 3;
+
+  // List of artifacts (i.e. files) that was produced by the command.
+  repeated string artifacts = 4;
+
+  // The error string produced by the build action.
+  optional string error = 5;
+}
diff --git a/ui/status/build_error_proto/regen.sh b/ui/status/build_error_proto/regen.sh
new file mode 100755
index 0000000..7c3ec8f
--- /dev/null
+++ b/ui/status/build_error_proto/regen.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+aprotoc --go_out=paths=source_relative:. build_error.proto
diff --git a/ui/status/critical_path.go b/ui/status/critical_path.go
new file mode 100644
index 0000000..8065c60
--- /dev/null
+++ b/ui/status/critical_path.go
@@ -0,0 +1,154 @@
+// 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 status
+
+import (
+	"time"
+
+	"android/soong/ui/logger"
+)
+
+func NewCriticalPath(log logger.Logger) StatusOutput {
+	return &criticalPath{
+		log:     log,
+		running: make(map[*Action]time.Time),
+		nodes:   make(map[string]*node),
+		clock:   osClock{},
+	}
+}
+
+type criticalPath struct {
+	log logger.Logger
+
+	nodes   map[string]*node
+	running map[*Action]time.Time
+
+	start, end time.Time
+
+	clock clock
+}
+
+type clock interface {
+	Now() time.Time
+}
+
+type osClock struct{}
+
+func (osClock) Now() time.Time { return time.Now() }
+
+// A critical path node stores the critical path (the minimum time to build the node and all of its dependencies given
+// perfect parallelism) for an node.
+type node struct {
+	action             *Action
+	cumulativeDuration time.Duration
+	duration           time.Duration
+	input              *node
+}
+
+func (cp *criticalPath) StartAction(action *Action, counts Counts) {
+	start := cp.clock.Now()
+	if cp.start.IsZero() {
+		cp.start = start
+	}
+	cp.running[action] = start
+}
+
+func (cp *criticalPath) FinishAction(result ActionResult, counts Counts) {
+	if start, ok := cp.running[result.Action]; ok {
+		delete(cp.running, result.Action)
+
+		// Determine the input to this edge with the longest cumulative duration
+		var criticalPathInput *node
+		for _, input := range result.Action.Inputs {
+			if x := cp.nodes[input]; x != nil {
+				if criticalPathInput == nil || x.cumulativeDuration > criticalPathInput.cumulativeDuration {
+					criticalPathInput = x
+				}
+			}
+		}
+
+		end := cp.clock.Now()
+		duration := end.Sub(start)
+
+		cumulativeDuration := duration
+		if criticalPathInput != nil {
+			cumulativeDuration += criticalPathInput.cumulativeDuration
+		}
+
+		node := &node{
+			action:             result.Action,
+			cumulativeDuration: cumulativeDuration,
+			duration:           duration,
+			input:              criticalPathInput,
+		}
+
+		for _, output := range result.Action.Outputs {
+			cp.nodes[output] = node
+		}
+
+		cp.end = end
+	}
+}
+
+func (cp *criticalPath) Flush() {
+	criticalPath := cp.criticalPath()
+
+	if len(criticalPath) > 0 {
+		// Log the critical path to the verbose log
+		criticalTime := criticalPath[0].cumulativeDuration.Round(time.Second)
+		cp.log.Verbosef("critical path took %s", criticalTime.String())
+		if !cp.start.IsZero() {
+			elapsedTime := cp.end.Sub(cp.start).Round(time.Second)
+			cp.log.Verbosef("elapsed time %s", elapsedTime.String())
+			if elapsedTime > 0 {
+				cp.log.Verbosef("perfect parallelism ratio %d%%",
+					int(float64(criticalTime)/float64(elapsedTime)*100))
+			}
+		}
+		cp.log.Verbose("critical path:")
+		for i := len(criticalPath) - 1; i >= 0; i-- {
+			duration := criticalPath[i].duration
+			duration = duration.Round(time.Second)
+			seconds := int(duration.Seconds())
+			cp.log.Verbosef("   %2d:%02d %s",
+				seconds/60, seconds%60, criticalPath[i].action.Description)
+		}
+	}
+}
+
+func (cp *criticalPath) Message(level MsgLevel, msg string) {}
+
+func (cp *criticalPath) Write(p []byte) (n int, err error) { return len(p), nil }
+
+func (cp *criticalPath) criticalPath() []*node {
+	var max *node
+
+	// Find the node with the longest critical path
+	for _, node := range cp.nodes {
+		if max == nil || node.cumulativeDuration > max.cumulativeDuration {
+			max = node
+		}
+	}
+
+	// Follow the critical path back to the leaf node
+	var criticalPath []*node
+	node := max
+	for node != nil {
+		criticalPath = append(criticalPath, node)
+		node = node.input
+	}
+
+	return criticalPath
+}
diff --git a/ui/status/critical_path_test.go b/ui/status/critical_path_test.go
new file mode 100644
index 0000000..965e0ad
--- /dev/null
+++ b/ui/status/critical_path_test.go
@@ -0,0 +1,166 @@
+// 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 status
+
+import (
+	"reflect"
+	"testing"
+	"time"
+)
+
+type testCriticalPath struct {
+	*criticalPath
+	Counts
+
+	actions map[int]*Action
+}
+
+type testClock time.Time
+
+func (t testClock) Now() time.Time { return time.Time(t) }
+
+func (t *testCriticalPath) start(id int, startTime time.Duration, outputs, inputs []string) {
+	t.clock = testClock(time.Unix(0, 0).Add(startTime))
+	action := &Action{
+		Description: outputs[0],
+		Outputs:     outputs,
+		Inputs:      inputs,
+	}
+
+	t.actions[id] = action
+	t.StartAction(action, t.Counts)
+}
+
+func (t *testCriticalPath) finish(id int, endTime time.Duration) {
+	t.clock = testClock(time.Unix(0, 0).Add(endTime))
+	t.FinishAction(ActionResult{
+		Action: t.actions[id],
+	}, t.Counts)
+}
+
+func TestCriticalPath(t *testing.T) {
+	tests := []struct {
+		name     string
+		msgs     func(*testCriticalPath)
+		want     []string
+		wantTime time.Duration
+	}{
+		{
+			name: "empty",
+			msgs: func(cp *testCriticalPath) {},
+		},
+		{
+			name: "duplicate",
+			msgs: func(cp *testCriticalPath) {
+				cp.start(0, 0, []string{"a"}, nil)
+				cp.start(1, 0, []string{"a"}, nil)
+				cp.finish(0, 1000)
+				cp.finish(0, 2000)
+			},
+			want:     []string{"a"},
+			wantTime: 1000,
+		},
+		{
+			name: "linear",
+			//  a
+			//  |
+			//  b
+			//  |
+			//  c
+			msgs: func(cp *testCriticalPath) {
+				cp.start(0, 0, []string{"a"}, nil)
+				cp.finish(0, 1000)
+				cp.start(1, 1000, []string{"b"}, []string{"a"})
+				cp.finish(1, 2000)
+				cp.start(2, 3000, []string{"c"}, []string{"b"})
+				cp.finish(2, 4000)
+			},
+			want:     []string{"c", "b", "a"},
+			wantTime: 3000,
+		},
+		{
+			name: "diamond",
+			//  a
+			//  |\
+			//  b c
+			//  |/
+			//  d
+			msgs: func(cp *testCriticalPath) {
+				cp.start(0, 0, []string{"a"}, nil)
+				cp.finish(0, 1000)
+				cp.start(1, 1000, []string{"b"}, []string{"a"})
+				cp.start(2, 1000, []string{"c"}, []string{"a"})
+				cp.finish(1, 2000)
+				cp.finish(2, 3000)
+				cp.start(3, 3000, []string{"d"}, []string{"b", "c"})
+				cp.finish(3, 4000)
+			},
+			want:     []string{"d", "c", "a"},
+			wantTime: 4000,
+		},
+		{
+			name: "multiple",
+			//  a d
+			//  | |
+			//  b e
+			//  |
+			//  c
+			msgs: func(cp *testCriticalPath) {
+				cp.start(0, 0, []string{"a"}, nil)
+				cp.start(3, 0, []string{"d"}, nil)
+				cp.finish(0, 1000)
+				cp.finish(3, 1000)
+				cp.start(1, 1000, []string{"b"}, []string{"a"})
+				cp.start(4, 1000, []string{"e"}, []string{"d"})
+				cp.finish(1, 2000)
+				cp.start(2, 2000, []string{"c"}, []string{"b"})
+				cp.finish(2, 3000)
+				cp.finish(4, 4000)
+
+			},
+			want:     []string{"e", "d"},
+			wantTime: 4000,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			cp := &testCriticalPath{
+				criticalPath: NewCriticalPath(nil).(*criticalPath),
+				actions:      make(map[int]*Action),
+			}
+
+			tt.msgs(cp)
+
+			criticalPath := cp.criticalPath.criticalPath()
+
+			var descs []string
+			for _, x := range criticalPath {
+				descs = append(descs, x.action.Description)
+			}
+
+			if !reflect.DeepEqual(descs, tt.want) {
+				t.Errorf("criticalPath.criticalPath() = %v, want %v", descs, tt.want)
+			}
+
+			var gotTime time.Duration
+			if len(criticalPath) > 0 {
+				gotTime = criticalPath[0].cumulativeDuration
+			}
+			if gotTime != tt.wantTime {
+				t.Errorf("cumulativeDuration[0].cumulativeDuration = %v, want %v", gotTime, tt.wantTime)
+			}
+		})
+	}
+}
diff --git a/ui/status/log.go b/ui/status/log.go
index 7badac7..d407248 100644
--- a/ui/status/log.go
+++ b/ui/status/log.go
@@ -15,11 +15,17 @@
 package status
 
 import (
-	"android/soong/ui/logger"
 	"compress/gzip"
+	"errors"
 	"fmt"
 	"io"
+	"io/ioutil"
 	"strings"
+
+	"github.com/golang/protobuf/proto"
+
+	"android/soong/ui/logger"
+	"android/soong/ui/status/build_error_proto"
 )
 
 type verboseLog struct {
@@ -77,8 +83,7 @@
 }
 
 type errorLog struct {
-	w io.WriteCloser
-
+	w     io.WriteCloser
 	empty bool
 }
 
@@ -102,20 +107,17 @@
 		return
 	}
 
-	cmd := result.Command
-	if cmd == "" {
-		cmd = result.Description
-	}
-
 	if !e.empty {
 		fmt.Fprintf(e.w, "\n\n")
 	}
 	e.empty = false
 
 	fmt.Fprintf(e.w, "FAILED: %s\n", result.Description)
+
 	if len(result.Outputs) > 0 {
 		fmt.Fprintf(e.w, "Outputs: %s\n", strings.Join(result.Outputs, " "))
 	}
+
 	fmt.Fprintf(e.w, "Error: %s\n", result.Error)
 	if result.Command != "" {
 		fmt.Fprintf(e.w, "Command: %s\n", result.Command)
@@ -144,3 +146,55 @@
 	fmt.Fprint(e.w, string(p))
 	return len(p), nil
 }
+
+type errorProtoLog struct {
+	errorProto soong_build_error_proto.BuildError
+	filename   string
+	log        logger.Logger
+}
+
+func NewProtoErrorLog(log logger.Logger, filename string) StatusOutput {
+	return &errorProtoLog{
+		errorProto: soong_build_error_proto.BuildError{},
+		filename:   filename,
+		log:        log,
+	}
+}
+
+func (e *errorProtoLog) StartAction(action *Action, counts Counts) {}
+
+func (e *errorProtoLog) FinishAction(result ActionResult, counts Counts) {
+	if result.Error == nil {
+		return
+	}
+
+	e.errorProto.ActionErrors = append(e.errorProto.ActionErrors, &soong_build_error_proto.BuildActionError{
+		Description: proto.String(result.Description),
+		Command:     proto.String(result.Command),
+		Output:      proto.String(result.Output),
+		Artifacts:   result.Outputs,
+		Error:       proto.String(result.Error.Error()),
+	})
+}
+
+func (e *errorProtoLog) Flush() {
+	data, err := proto.Marshal(&e.errorProto)
+	if err != nil {
+		e.log.Printf("Failed to marshal build status proto: %v\n", err)
+		return
+	}
+	err = ioutil.WriteFile(e.filename, []byte(data), 0644)
+	if err != nil {
+		e.log.Printf("Failed to write file %s: %v\n", e.filename, err)
+	}
+}
+
+func (e *errorProtoLog) Message(level MsgLevel, message string) {
+	if level > ErrorLvl {
+		e.errorProto.ErrorMessages = append(e.errorProto.ErrorMessages, message)
+	}
+}
+
+func (e *errorProtoLog) Write(p []byte) (int, error) {
+	return 0, errors.New("not supported")
+}
diff --git a/ui/status/ninja.go b/ui/status/ninja.go
index ee2a2da..9cf2f6a 100644
--- a/ui/status/ninja.go
+++ b/ui/status/ninja.go
@@ -142,6 +142,7 @@
 			action := &Action{
 				Description: msg.EdgeStarted.GetDesc(),
 				Outputs:     msg.EdgeStarted.Outputs,
+				Inputs:      msg.EdgeStarted.Inputs,
 				Command:     msg.EdgeStarted.GetCommand(),
 			}
 			n.status.StartAction(action)
diff --git a/ui/status/status.go b/ui/status/status.go
index 3d8cd7a..df33baa 100644
--- a/ui/status/status.go
+++ b/ui/status/status.go
@@ -32,6 +32,10 @@
 	// but they can be any string.
 	Outputs []string
 
+	// Inputs is the (optional) list of inputs. Usually these are files,
+	// but they can be any string.
+	Inputs []string
+
 	// Command is the actual command line executed to perform the action.
 	// It's optional, but one of either Description or Command should be
 	// set.
diff --git a/ui/terminal/smart_status.go b/ui/terminal/smart_status.go
index 9638cdf..6bdf140 100644
--- a/ui/terminal/smart_status.go
+++ b/ui/terminal/smart_status.go
@@ -59,21 +59,24 @@
 // current build status similarly to Ninja's built-in terminal
 // output.
 func NewSmartStatusOutput(w io.Writer, formatter formatter) status.StatusOutput {
-	tableHeight, _ := strconv.Atoi(os.Getenv(tableHeightEnVar))
-
 	s := &smartStatusOutput{
 		writer:    w,
 		formatter: formatter,
 
 		haveBlankLine: true,
 
-		tableMode:            tableHeight > 0,
-		requestedTableHeight: tableHeight,
+		tableMode: true,
 
 		done:     make(chan bool),
 		sigwinch: make(chan os.Signal),
 	}
 
+	if env, ok := os.LookupEnv(tableHeightEnVar); ok {
+		h, _ := strconv.Atoi(env)
+		s.tableMode = h > 0
+		s.requestedTableHeight = h
+	}
+
 	s.updateTermSize()
 
 	if s.tableMode {
@@ -167,6 +170,13 @@
 }
 
 func (s *smartStatusOutput) Flush() {
+	if s.tableMode {
+		// Stop the action table tick outside of the lock to avoid lock ordering issues between s.done and
+		// s.lock, the goroutine in startActionTableTick can get blocked on the lock and be unable to read
+		// from the channel.
+		s.stopActionTableTick()
+	}
+
 	s.lock.Lock()
 	defer s.lock.Unlock()
 
@@ -177,8 +187,6 @@
 	s.runningActions = nil
 
 	if s.tableMode {
-		s.stopActionTableTick()
-
 		// Update the table after clearing runningActions to clear it
 		s.actionTable()
 
@@ -186,7 +194,7 @@
 		fmt.Fprintf(s.writer, ansi.resetScrollingMargins())
 		_, height, _ := termSize(s.writer)
 		// Move the cursor to the top of the now-blank, previously non-scrolling region
-		fmt.Fprintf(s.writer, ansi.setCursor(height-s.tableHeight, 0))
+		fmt.Fprintf(s.writer, ansi.setCursor(height-s.tableHeight, 1))
 		// Turn the cursor back on
 		fmt.Fprintf(s.writer, ansi.showCursor())
 	}
@@ -225,9 +233,7 @@
 
 	// Limit line width to the terminal width, otherwise we'll wrap onto
 	// another line and we won't delete the previous line.
-	if s.termWidth > 0 {
-		str = s.elide(str)
-	}
+	str = elide(str, s.termWidth)
 
 	// Move to the beginning on the line, turn on bold, print the output,
 	// turn off bold, then clear the rest of the line.
@@ -237,11 +243,11 @@
 	s.haveBlankLine = false
 }
 
-func (s *smartStatusOutput) elide(str string) string {
-	if len(str) > s.termWidth {
+func elide(str string, width int) string {
+	if width > 0 && len(str) > width {
 		// TODO: Just do a max. Ninja elides the middle, but that's
 		// more complicated and these lines aren't that important.
-		str = str[:s.termWidth]
+		str = str[:width]
 	}
 
 	return str
@@ -299,6 +305,14 @@
 
 		if s.tableMode {
 			tableHeight := s.requestedTableHeight
+			if tableHeight == 0 {
+				tableHeight = s.termHeight / 4
+				if tableHeight < 1 {
+					tableHeight = 1
+				} else if tableHeight > 10 {
+					tableHeight = 10
+				}
+			}
 			if tableHeight > s.termHeight-1 {
 				tableHeight = s.termHeight - 1
 			}
@@ -325,43 +339,44 @@
 	scrollingHeight := s.termHeight - s.tableHeight
 
 	// Update the scrolling region in case the height of the terminal changed
-	fmt.Fprint(s.writer, ansi.setScrollingMargins(0, scrollingHeight))
-	// Move the cursor to the first line of the non-scrolling region
-	fmt.Fprint(s.writer, ansi.setCursor(scrollingHeight+1, 0))
+
+	fmt.Fprint(s.writer, ansi.setScrollingMargins(1, scrollingHeight))
 
 	// Write as many status lines as fit in the table
-	var tableLine int
-	var runningAction actionTableEntry
-	for tableLine, runningAction = range s.runningActions {
+	for tableLine := 0; tableLine < s.tableHeight; tableLine++ {
 		if tableLine >= s.tableHeight {
 			break
 		}
+		// Move the cursor to the correct line of the non-scrolling region
+		fmt.Fprint(s.writer, ansi.setCursor(scrollingHeight+1+tableLine, 1))
 
-		seconds := int(time.Since(runningAction.startTime).Round(time.Second).Seconds())
+		if tableLine < len(s.runningActions) {
+			runningAction := s.runningActions[tableLine]
 
-		desc := runningAction.action.Description
-		if desc == "" {
-			desc = runningAction.action.Command
+			seconds := int(time.Since(runningAction.startTime).Round(time.Second).Seconds())
+
+			desc := runningAction.action.Description
+			if desc == "" {
+				desc = runningAction.action.Command
+			}
+
+			color := ""
+			if seconds >= 60 {
+				color = ansi.red() + ansi.bold()
+			} else if seconds >= 30 {
+				color = ansi.yellow() + ansi.bold()
+			}
+
+			durationStr := fmt.Sprintf("   %2d:%02d ", seconds/60, seconds%60)
+			desc = elide(desc, s.termWidth-len(durationStr))
+			durationStr = color + durationStr + ansi.regular()
+			fmt.Fprint(s.writer, durationStr, desc)
 		}
-
-		str := fmt.Sprintf("   %2d:%02d %s", seconds/60, seconds%60, desc)
-		str = s.elide(str)
-		fmt.Fprint(s.writer, str, ansi.clearToEndOfLine())
-		if tableLine < s.tableHeight-1 {
-			fmt.Fprint(s.writer, "\n")
-		}
-	}
-
-	// Clear any remaining lines in the table
-	for ; tableLine < s.tableHeight; tableLine++ {
 		fmt.Fprint(s.writer, ansi.clearToEndOfLine())
-		if tableLine < s.tableHeight-1 {
-			fmt.Fprint(s.writer, "\n")
-		}
 	}
 
 	// Move the cursor back to the last line of the scrolling region
-	fmt.Fprint(s.writer, ansi.setCursor(scrollingHeight, 0))
+	fmt.Fprint(s.writer, ansi.setCursor(scrollingHeight, 1))
 }
 
 var ansi = ansiImpl{}
@@ -387,6 +402,14 @@
 	return fmt.Sprintf("\x1b[r")
 }
 
+func (ansiImpl) red() string {
+	return "\x1b[31m"
+}
+
+func (ansiImpl) yellow() string {
+	return "\x1b[33m"
+}
+
 func (ansiImpl) bold() string {
 	return "\x1b[1m"
 }
diff --git a/ui/terminal/status.go b/ui/terminal/status.go
index 69a2a09..60dfc70 100644
--- a/ui/terminal/status.go
+++ b/ui/terminal/status.go
@@ -26,10 +26,10 @@
 //
 // statusFormat takes nearly all the same options as NINJA_STATUS.
 // %c is currently unsupported.
-func NewStatusOutput(w io.Writer, statusFormat string, quietBuild bool) status.StatusOutput {
+func NewStatusOutput(w io.Writer, statusFormat string, forceDumbOutput, quietBuild bool) status.StatusOutput {
 	formatter := newFormatter(statusFormat, quietBuild)
 
-	if isSmartTerminal(w) {
+	if !forceDumbOutput && isSmartTerminal(w) {
 		return NewSmartStatusOutput(w, formatter)
 	} else {
 		return NewDumbStatusOutput(w, formatter)
diff --git a/ui/terminal/status_test.go b/ui/terminal/status_test.go
index 81aa238..9f60829 100644
--- a/ui/terminal/status_test.go
+++ b/ui/terminal/status_test.go
@@ -94,7 +94,7 @@
 
 			t.Run("smart", func(t *testing.T) {
 				smart := &fakeSmartTerminal{termWidth: 40}
-				stat := NewStatusOutput(smart, "", false)
+				stat := NewStatusOutput(smart, "", false, false)
 				tt.calls(stat)
 				stat.Flush()
 
@@ -105,7 +105,7 @@
 
 			t.Run("dumb", func(t *testing.T) {
 				dumb := &bytes.Buffer{}
-				stat := NewStatusOutput(dumb, "", false)
+				stat := NewStatusOutput(dumb, "", false, false)
 				tt.calls(stat)
 				stat.Flush()
 
@@ -113,6 +113,17 @@
 					t.Errorf("want:\n%q\ngot:\n%q", w, g)
 				}
 			})
+
+			t.Run("force dumb", func(t *testing.T) {
+				smart := &fakeSmartTerminal{termWidth: 40}
+				stat := NewStatusOutput(smart, "", true, false)
+				tt.calls(stat)
+				stat.Flush()
+
+				if g, w := smart.String(), tt.dumb; g != w {
+					t.Errorf("want:\n%q\ngot:\n%q", w, g)
+				}
+			})
 		})
 	}
 }
@@ -258,7 +269,7 @@
 	os.Setenv(tableHeightEnVar, "")
 
 	smart := &fakeSmartTerminal{termWidth: 40}
-	stat := NewStatusOutput(smart, "", false)
+	stat := NewStatusOutput(smart, "", false, false)
 	smartStat := stat.(*smartStatusOutput)
 	smartStat.sigwinchHandled = make(chan bool)
 
diff --git a/ui/terminal/util.go b/ui/terminal/util.go
index c9377f1..7a603d7 100644
--- a/ui/terminal/util.go
+++ b/ui/terminal/util.go
@@ -24,6 +24,9 @@
 
 func isSmartTerminal(w io.Writer) bool {
 	if f, ok := w.(*os.File); ok {
+		if term, ok := os.LookupEnv("TERM"); ok && term == "dumb" {
+			return false
+		}
 		var termios syscall.Termios
 		_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, f.Fd(),
 			ioctlGetTermios, uintptr(unsafe.Pointer(&termios)),
diff --git a/vnames.go.json b/vnames.go.json
new file mode 100644
index 0000000..5842097
--- /dev/null
+++ b/vnames.go.json
@@ -0,0 +1,9 @@
+[
+    {
+        "pattern": "(.*)",
+        "vname": {
+            "corpus": "android.googlesource.com/platform/superproject",
+            "path": "build/soong/@1@"
+        }
+    }
+]
diff --git a/vnames.json b/vnames.json
new file mode 100644
index 0000000..f9d3adc
--- /dev/null
+++ b/vnames.json
@@ -0,0 +1,18 @@
+[
+  {
+    "pattern": "out/(.*)",
+    "vname": {
+      "corpus": "android.googlesource.com/platform/superproject",
+      "root": "out",
+      "path": "@1@"
+    }
+  },
+  {
+    "pattern": "(.*)",
+    "vname": {
+      "corpus": "android.googlesource.com/platform/superproject",
+      "path": "@1@"
+    }
+  }
+]
+
diff --git a/xml/xml.go b/xml/xml.go
index 7c670fb..3a680ec 100644
--- a/xml/xml.go
+++ b/xml/xml.go
@@ -71,10 +71,6 @@
 	return android.PathForModuleOut(ctx, p.PrebuiltEtc.SourceFilePath(ctx).Base()+"-timestamp")
 }
 
-func (p *prebuiltEtcXml) DepsMutator(ctx android.BottomUpMutatorContext) {
-	p.PrebuiltEtc.DepsMutator(ctx)
-}
-
 func (p *prebuiltEtcXml) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	p.PrebuiltEtc.GenerateAndroidBuildActions(ctx)
 
@@ -125,9 +121,8 @@
 func PrebuiltEtcXmlFactory() android.Module {
 	module := &prebuiltEtcXml{}
 	module.AddProperties(&module.properties)
-
-	android.InitPrebuiltEtcModule(&module.PrebuiltEtc)
+	android.InitPrebuiltEtcModule(&module.PrebuiltEtc, "etc")
 	// This module is device-only
-	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
 	return module
 }
diff --git a/xml/xml_test.go b/xml/xml_test.go
index e8fa49c..f8ec823 100644
--- a/xml/xml_test.go
+++ b/xml/xml_test.go
@@ -15,27 +15,51 @@
 package xml
 
 import (
-	"android/soong/android"
 	"io/ioutil"
 	"os"
 	"testing"
+
+	"android/soong/android"
 )
 
-func testXml(t *testing.T, bp string) *android.TestContext {
-	config, buildDir := setup(t)
-	defer teardown(buildDir)
-	ctx := android.NewTestArchContext()
-	ctx.RegisterModuleType("prebuilt_etc", android.ModuleFactoryAdaptor(android.PrebuiltEtcFactory))
-	ctx.RegisterModuleType("prebuilt_etc_xml", android.ModuleFactoryAdaptor(PrebuiltEtcXmlFactory))
-	ctx.Register()
-	mockFiles := map[string][]byte{
-		"Android.bp": []byte(bp),
-		"foo.xml":    nil,
-		"foo.dtd":    nil,
-		"bar.xml":    nil,
-		"bar.xsd":    nil,
+var buildDir string
+
+func setUp() {
+	var err error
+	buildDir, err = ioutil.TempDir("", "soong_xml_test")
+	if err != nil {
+		panic(err)
 	}
-	ctx.MockFileSystem(mockFiles)
+}
+
+func tearDown() {
+	os.RemoveAll(buildDir)
+}
+
+func TestMain(m *testing.M) {
+	run := func() int {
+		setUp()
+		defer tearDown()
+
+		return m.Run()
+	}
+
+	os.Exit(run())
+}
+
+func testXml(t *testing.T, bp string) *android.TestContext {
+	fs := map[string][]byte{
+		"foo.xml": nil,
+		"foo.dtd": nil,
+		"bar.xml": nil,
+		"bar.xsd": nil,
+		"baz.xml": nil,
+	}
+	config := android.TestArchConfig(buildDir, nil, bp, fs)
+	ctx := android.NewTestArchContext()
+	ctx.RegisterModuleType("prebuilt_etc", android.PrebuiltEtcFactory)
+	ctx.RegisterModuleType("prebuilt_etc_xml", PrebuiltEtcXmlFactory)
+	ctx.Register(config)
 	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
 	android.FailIfErrored(t, errs)
 	_, errs = ctx.PrepareBuildActions(config)
@@ -44,19 +68,11 @@
 	return ctx
 }
 
-func setup(t *testing.T) (config android.Config, buildDir string) {
-	buildDir, err := ioutil.TempDir("", "soong_xml_test")
-	if err != nil {
-		t.Fatal(err)
+func assertEqual(t *testing.T, name, expected, actual string) {
+	t.Helper()
+	if expected != actual {
+		t.Errorf(name+" expected %q != got %q", expected, actual)
 	}
-
-	config = android.TestArchConfig(buildDir, nil)
-
-	return
-}
-
-func teardown(buildDir string) {
-	os.RemoveAll(buildDir)
 }
 
 // Minimal test
@@ -72,15 +88,28 @@
 			src: "bar.xml",
 			schema: "bar.xsd",
 		}
+		prebuilt_etc_xml {
+			name: "baz.xml",
+			src: "baz.xml",
+		}
 	`)
 
-	xmllint := ctx.ModuleForTests("foo.xml", "android_common").Rule("xmllint")
-	input := xmllint.Input.String()
-	if input != "foo.xml" {
-		t.Errorf("input expected %q != got %q", "foo.xml", input)
+	for _, tc := range []struct {
+		rule, input, schemaType, schema string
+	}{
+		{rule: "xmllint-dtd", input: "foo.xml", schemaType: "dtd", schema: "foo.dtd"},
+		{rule: "xmllint-xsd", input: "bar.xml", schemaType: "xsd", schema: "bar.xsd"},
+		{rule: "xmllint-minimal", input: "baz.xml"},
+	} {
+		t.Run(tc.schemaType, func(t *testing.T) {
+			rule := ctx.ModuleForTests(tc.input, "android_arm64_armv8-a").Rule(tc.rule)
+			assertEqual(t, "input", tc.input, rule.Input.String())
+			if tc.schemaType != "" {
+				assertEqual(t, "schema", tc.schema, rule.Args[tc.schemaType])
+			}
+		})
 	}
-	schema := xmllint.Args["dtd"]
-	if schema != "foo.dtd" {
-		t.Errorf("dtd expected %q != got %q", "foo.dtdl", schema)
-	}
+
+	m := ctx.ModuleForTests("foo.xml", "android_arm64_armv8-a").Module().(*prebuiltEtcXml)
+	assertEqual(t, "installDir", buildDir+"/target/product/test_device/system/etc", m.InstallDirPath().String())
 }
diff --git a/zip/zip.go b/zip/zip.go
index 707c4ef..3c710a7 100644
--- a/zip/zip.go
+++ b/zip/zip.go
@@ -145,7 +145,7 @@
 	}
 
 	arg := b.state
-	arg.SourceFiles = strings.Split(string(list), "\n")
+	arg.SourceFiles = strings.Fields(string(list))
 	b.fileArgs = append(b.fileArgs, arg)
 	return b
 }
diff --git a/zip/zip_test.go b/zip/zip_test.go
index 84317d1..9705d6c 100644
--- a/zip/zip_test.go
+++ b/zip/zip_test.go
@@ -46,7 +46,8 @@
 	"dangling -> missing": nil,
 	"a/a/d -> b":          nil,
 	"c":                   fileC,
-	"l":                   []byte("a/a/a\na/a/b\nc\n"),
+	"l_nl":                []byte("a/a/a\na/a/b\nc\n"),
+	"l_sp":                []byte("a/a/a a/a/b c"),
 	"l2":                  []byte("missing\n"),
 	"manifest.txt":        fileCustomManifest,
 })
@@ -224,7 +225,19 @@
 		{
 			name: "list",
 			args: fileArgsBuilder().
-				List("l"),
+				List("l_nl"),
+			compressionLevel: 9,
+
+			files: []zip.FileHeader{
+				fh("a/a/a", fileA, zip.Deflate),
+				fh("a/a/b", fileB, zip.Deflate),
+				fh("c", fileC, zip.Deflate),
+			},
+		},
+		{
+			name: "list",
+			args: fileArgsBuilder().
+				List("l_sp"),
 			compressionLevel: 9,
 
 			files: []zip.FileHeader{