Merge "Add coverage flags for Clang coverage builds."
diff --git a/Android.bp b/Android.bp
index 6910d75..ab03a36 100644
--- a/Android.bp
+++ b/Android.bp
@@ -69,6 +69,7 @@
         "android/proto.go",
         "android/register.go",
         "android/rule_builder.go",
+        "android/sandbox.go",
         "android/sdk.go",
         "android/sh_binary.go",
         "android/singleton.go",
@@ -155,6 +156,7 @@
         "cc/androidmk.go",
         "cc/builder.go",
         "cc/cc.go",
+        "cc/ccdeps.go",
         "cc/check.go",
         "cc/coverage.go",
         "cc/gen.go",
@@ -186,6 +188,7 @@
         "cc/binary.go",
         "cc/fuzz.go",
         "cc/library.go",
+        "cc/library_sdk_member.go",
         "cc/object.go",
         "cc/test.go",
         "cc/toolchain_library.go",
@@ -297,6 +300,7 @@
         "java/sdk.go",
         "java/sdk_library.go",
         "java/support_libraries.go",
+        "java/sysprop.go",
         "java/system_modules.go",
         "java/testing.go",
         "java/tradefed.go",
@@ -346,6 +350,7 @@
         "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",
     ],
 }
@@ -504,11 +509,16 @@
     ],
     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"],
 }
@@ -586,6 +596,7 @@
     vendor_available: true,
     recovery_available: true,
     native_bridge_supported: true,
+    sdk_version: "current",
 
     arch: {
         arm: {
diff --git a/README.md b/README.md
index 37feb1d..b6fda50 100644
--- a/README.md
+++ b/README.md
@@ -74,7 +74,7 @@
 ```
 
 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.
 
@@ -168,37 +168,114 @@
 }
 ```
 
-### Name resolution
+### Referencing Modules
 
-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:
+A module `libfoo` can be referenced by its name
 
 ```
-soong_namespace {
-    imports: ["path/to/otherNamespace1", "path/to/otherNamespace2"],
+cc_binary {
+    name: "app",
+    shared_libs: ["libfoo"],
 }
 ```
 
-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.
+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.
 
-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.
+#### Namespaces
 
-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.
+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
 
@@ -266,7 +343,7 @@
 
 ### 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:
 ```
diff --git a/android/androidmk.go b/android/androidmk.go
index 4893bf4..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
 }
 
@@ -323,7 +323,7 @@
 		return
 	}
 
-	err := translateAndroidMk(ctx, transMk.String(), androidMkModulesList)
+	err := translateAndroidMk(ctx, absolutePath(transMk.String()), androidMkModulesList)
 	if err != nil {
 		ctx.Errorf(err.Error())
 	}
@@ -364,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 {
@@ -383,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 {
@@ -510,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
index 940e324..71f8020 100644
--- a/android/androidmk_test.go
+++ b/android/androidmk_test.go
@@ -43,14 +43,6 @@
 }
 
 func TestAndroidMkSingleton_PassesUpdatedAndroidMkDataToCustomCallback(t *testing.T) {
-	config := TestConfig(buildDir, nil)
-	config.inMake = true // Enable androidmk Singleton
-
-	ctx := NewTestContext()
-	ctx.RegisterSingletonType("androidmk", AndroidMkSingleton)
-	ctx.RegisterModuleType("custom", customModuleFactory)
-	ctx.Register()
-
 	bp := `
 	custom {
 		name: "foo",
@@ -60,9 +52,13 @@
 	}
 	`
 
-	ctx.MockFileSystem(map[string][]byte{
-		"Android.bp": []byte(bp),
-	})
+	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)
diff --git a/android/apex.go b/android/apex.go
index 44387cd..a4b6956 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -82,7 +82,10 @@
 }
 
 type ApexProperties struct {
-	// Availability of this module in APEXes. Only the listed APEXes can include this module.
+	// Availability of this module in APEXes. Only the listed APEXes can contain
+	// this module. If the module has stubs then other APEXes and the platform may
+	// access it through them (subject to visibility).
+	//
 	// "//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"].
@@ -138,18 +141,18 @@
 }
 
 const (
-	availableToPlatform = "//apex_available:platform"
+	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
+		// apex_available defaults to ["//apex_available:platform"],
+		// which means 'available to the platform but no apexes'.
+		return what == AvailableToPlatform
 	}
 	return InList(what, apex_available) ||
-		(what != availableToPlatform && InList(availableToAnyApex, apex_available))
+		(what != AvailableToPlatform && InList(availableToAnyApex, apex_available))
 }
 
 func (m *ApexModuleBase) AvailableFor(what string) bool {
@@ -165,7 +168,7 @@
 
 func (m *ApexModuleBase) checkApexAvailableProperty(mctx BaseModuleContext) {
 	for _, n := range m.ApexProperties.Apex_available {
-		if n == availableToPlatform || n == availableToAnyApex {
+		if n == AvailableToPlatform || n == availableToAnyApex {
 			continue
 		}
 		if !mctx.OtherModuleExists(n) && !mctx.Config().AllowMissingDependencies() {
@@ -179,12 +182,14 @@
 		m.checkApexAvailableProperty(mctx)
 		sort.Strings(m.apexVariations)
 		variations := []string{}
-		availableForPlatform := mctx.Module().(ApexModule).AvailableFor(availableToPlatform)
+		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 availableForPlatform && i == 0 {
@@ -228,6 +233,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/arch.go b/android/arch.go
index 0519e76..b5b8a8f 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -822,9 +822,12 @@
 
 	os := base.commonProperties.CompileOS
 	osTargets := mctx.Config().Targets[os]
-
+	image := base.commonProperties.ImageVariation
 	// Filter NativeBridge targets unless they are explicitly supported
-	if os == Android && !Bool(base.commonProperties.Native_bridge_supported) {
+	// Skip creating native bridge variants for vendor modules
+	if os == Android &&
+		!(Bool(base.commonProperties.Native_bridge_supported) && image == CoreVariation) {
+
 		var targets []Target
 		for _, t := range osTargets {
 			if !t.NativeBridge {
@@ -859,6 +862,12 @@
 		}
 	}
 
+	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
@@ -907,6 +916,16 @@
 	}
 }
 
+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--
+		}
+	}
+	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.
@@ -1583,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
 
diff --git a/android/arch_test.go b/android/arch_test.go
index b41e1ab..98b0534 100644
--- a/android/arch_test.go
+++ b/android/arch_test.go
@@ -289,10 +289,6 @@
 		}
 	`
 
-	mockFS := map[string][]byte{
-		"Android.bp": []byte(bp),
-	}
-
 	testCases := []struct {
 		name        string
 		config      func(Config)
@@ -337,11 +333,11 @@
 
 	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.MockFileSystem(mockFS)
-			ctx.Register()
-			config := TestArchConfig(buildDir, nil)
+			ctx.Register(config)
 			if tt.config != nil {
 				tt.config(config)
 			}
diff --git a/android/config.go b/android/config.go
index db8e98e..5c4f0a8 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"
 )
 
@@ -115,6 +117,9 @@
 
 	stopBefore bootstrap.StopBefore
 
+	fs         pathtools.FileSystem
+	mockBpList string
+
 	OncePer
 }
 
@@ -130,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.
@@ -199,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
@@ -231,6 +247,8 @@
 	}
 	config.TestProductVariables = &config.productVariables
 
+	config.mockFileSystem(bp, fs)
+
 	if err := config.fromEnv(); err != nil {
 		panic(err)
 	}
@@ -238,8 +256,8 @@
 	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{
@@ -252,8 +270,8 @@
 	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{
@@ -269,8 +287,8 @@
 }
 
 // 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{
@@ -312,6 +330,8 @@
 		srcDir:            srcDir,
 		buildDir:          buildDir,
 		multilibConflicts: make(map[ArchType]bool),
+
+		fs: pathtools.NewOsFs(absSrcDir),
 	}
 
 	config.deviceConfig = &deviceConfig{
@@ -341,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
 	}
 
@@ -355,6 +375,8 @@
 		archConfig = getMegaDeviceConfig()
 	} else if config.NdkAbis() {
 		archConfig = getNdkAbisConfig()
+	} else if config.AmlAbis() {
+		archConfig = getAmlAbisConfig()
 	}
 
 	if archConfig != nil {
@@ -387,6 +409,38 @@
 	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 "", "true":
@@ -418,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 {
@@ -769,6 +835,14 @@
 	return c.Getenv("XREF_CORPUS")
 }
 
+// Returns Compilation Unit encoding to use. Can be 'json' (default), 'proto' or 'all'.
+func (c *config) XrefCuEncoding() string {
+	if enc := c.Getenv("KYTHE_KZIP_ENCODING"); enc != "" {
+		return enc
+	}
+	return "json"
+}
+
 func (c *config) EmitXrefRules() bool {
 	return c.XrefCorpusName() != ""
 }
@@ -848,8 +922,13 @@
 	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 {
@@ -895,6 +974,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
 }
@@ -1075,6 +1158,10 @@
 	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)
 }
@@ -1095,6 +1182,10 @@
 	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_test.go b/android/csuite_config_test.go
index 5f86bbb..bf1a19a 100644
--- a/android/csuite_config_test.go
+++ b/android/csuite_config_test.go
@@ -19,15 +19,11 @@
 )
 
 func testCSuiteConfig(test *testing.T, bpFileContents string) *TestContext {
-	config := TestArchConfig(buildDir, nil)
+	config := TestArchConfig(buildDir, nil, bpFileContents, nil)
 
 	ctx := NewTestArchContext()
 	ctx.RegisterModuleType("csuite_config", CSuiteConfigFactory)
-	ctx.Register()
-	mockFiles := map[string][]byte{
-		"Android.bp": []byte(bpFileContents),
-	}
-	ctx.MockFileSystem(mockFiles)
+	ctx.Register(config)
 	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
 	FailIfErrored(test, errs)
 	_, errs = ctx.PrepareBuildActions(config)
diff --git a/android/defaults.go b/android/defaults.go
index f489c02..7597446 100644
--- a/android/defaults.go
+++ b/android/defaults.go
@@ -151,7 +151,8 @@
 	module.AddProperties(
 		&hostAndDeviceProperties{},
 		commonProperties,
-		&variableProperties{})
+		&variableProperties{},
+		&ApexProperties{})
 
 	InitArchModule(module)
 	InitDefaultableModule(module)
diff --git a/android/defaults_test.go b/android/defaults_test.go
index 80980f7..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", defaultsTestModuleFactory)
-	ctx.RegisterModuleType("defaults", 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 d9f2db2..46bd3d6 100644
--- a/android/env.go
+++ b/android/env.go
@@ -52,6 +52,17 @@
 	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{}
 }
@@ -66,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/hooks.go b/android/hooks.go
index 604cb9c..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,53 +29,78 @@
 // 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
+	EarlyModuleContext
+
 	AppendProperties(...interface{})
 	PrependProperties(...interface{})
 	CreateModule(ModuleFactory, ...interface{}) Module
 }
 
-// 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{})
-}
-
 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() InstallPath
@@ -118,31 +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 {
-		m.base().commonProperties.DebugName = ctx.ModuleName()
-
-		// 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
index 5ec1b16..5291ce3 100644
--- a/android/image.go
+++ b/android/image.go
@@ -14,7 +14,7 @@
 
 package android
 
-// ImageInterface is implemented by modules that need to be split by the ImageMutator.
+// 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)
@@ -42,15 +42,15 @@
 	// 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 = "core"
+	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
+// 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) {
+func imageMutator(ctx BottomUpMutatorContext) {
 	if ctx.Os() != Android {
 		return
 	}
diff --git a/android/makevars.go b/android/makevars.go
index 38a028c..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
@@ -151,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
@@ -175,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 b4f8f1a..05115d6 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,16 +55,52 @@
 
 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
@@ -91,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
@@ -123,18 +141,11 @@
 	Windows() bool
 	Debug() bool
 	PrimaryArch() bool
-	Platform() bool
-	DeviceSpecific() bool
-	SocSpecific() bool
-	ProductSpecific() bool
-	SystemExtSpecific() bool
-	AConfig() Config
-	DeviceConfig() DeviceConfig
 }
 
-// Deprecated: use BaseModuleContext instead
+// Deprecated: use EarlyModuleContext instead
 type BaseContext interface {
-	BaseModuleContext
+	EarlyModuleContext
 }
 
 type ModuleContext interface {
@@ -221,6 +232,10 @@
 
 	// Get the visibility rules that control the visibility of this module.
 	visibility() []string
+
+	RequiredModuleNames() []string
+	HostRequiredModuleNames() []string
+	TargetRequiredModuleNames() []string
 }
 
 // Qualified id for a module
@@ -302,9 +317,6 @@
 	// 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 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.
@@ -890,6 +902,18 @@
 	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{}
@@ -946,7 +970,7 @@
 	}
 }
 
-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)
@@ -1005,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,
 	}
 }
 
@@ -1118,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
@@ -1246,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...)
@@ -1303,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})
 			}
@@ -1323,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)
 			}
 		}
@@ -1341,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)
 		}
@@ -1353,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)
 			}
 		}
@@ -1363,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 {
@@ -1379,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)
 		}
@@ -1387,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 {
@@ -1403,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 {
@@ -1463,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 {
@@ -1497,34 +1610,6 @@
 	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) SystemExtSpecific() bool {
-	return b.kind == systemExtSpecificModule
-}
-
 // Makes this module a platform module, i.e. not specific to soc, device,
 // product, or system_ext.
 func (m *ModuleBase) MakeAsPlatform() {
@@ -1661,7 +1746,7 @@
 			Rule:        Symlink,
 			Description: "install symlink " + fullInstallPath.Base(),
 			Output:      fullInstallPath,
-			OrderOnly:   Paths{srcPath},
+			Input:       srcPath,
 			Default:     !m.Config().EmbeddedInMake(),
 			Args: map[string]string{
 				"fromPath": relPath,
@@ -1804,6 +1889,49 @@
 	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
 }
@@ -1835,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 fef1766..6e648d7 100644
--- a/android/module_test.go
+++ b/android/module_test.go
@@ -178,15 +178,9 @@
 		}
 	`
 
-	mockFS := map[string][]byte{
-		"Android.bp": []byte(bp),
-	}
+	config := TestConfig(buildDir, nil, bp, nil)
 
-	ctx.MockFileSystem(mockFS)
-
-	ctx.Register()
-
-	config := TestConfig(buildDir, nil)
+	ctx.Register(config)
 
 	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
 	FailIfErrored(t, errs)
diff --git a/android/mutator.go b/android/mutator.go
index 0d253eb..3d1bb4f 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -27,6 +27,7 @@
 //   run Pre-deps mutators
 //   run depsMutator
 //   run PostDeps mutators
+//   run FinalDeps mutators (CreateVariations disallowed in this phase)
 //   continue on to GenerateAndroidBuildActions
 
 func registerMutatorsToContext(ctx *blueprint.Context, mutators []*mutator) {
@@ -43,7 +44,7 @@
 	}
 }
 
-func registerMutators(ctx *blueprint.Context, preArch, preDeps, postDeps []RegisterMutatorFunc) {
+func registerMutators(ctx *blueprint.Context, preArch, preDeps, postDeps, finalDeps []RegisterMutatorFunc) {
 	mctx := &registerMutatorsContext{}
 
 	register := func(funcs []RegisterMutatorFunc) {
@@ -60,11 +61,15 @@
 
 	register(postDeps)
 
+	mctx.finalPhase = true
+	register(finalDeps)
+
 	registerMutatorsToContext(ctx, mctx.mutators)
 }
 
 type registerMutatorsContext struct {
-	mutators []*mutator
+	mutators   []*mutator
+	finalPhase bool
 }
 
 type RegisterMutatorsContext interface {
@@ -75,20 +80,19 @@
 type RegisterMutatorFunc func(RegisterMutatorsContext)
 
 var preArch = []RegisterMutatorFunc{
-	registerLoadHookMutator,
 	RegisterNamespaceMutator,
 	// Rename package module types.
-	registerPackageRenamer,
+	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{
@@ -98,11 +102,13 @@
 var postDeps = []RegisterMutatorFunc{
 	registerPathDepsMutator,
 	RegisterPrebuiltsPostDepsMutators,
-	registerVisibilityRuleEnforcer,
+	RegisterVisibilityRuleEnforcer,
 	registerNeverallowMutator,
 	RegisterOverridePostDepsMutators,
 }
 
+var finalDeps = []RegisterMutatorFunc{}
+
 func PreArchMutators(f RegisterMutatorFunc) {
 	preArch = append(preArch, f)
 }
@@ -115,6 +121,10 @@
 	postDeps = append(postDeps, f)
 }
 
+func FinalDepsMutators(f RegisterMutatorFunc) {
+	finalDeps = append(finalDeps, f)
+}
+
 type TopDownMutator func(TopDownMutatorContext)
 
 type TopDownMutatorContext interface {
@@ -157,14 +167,17 @@
 type bottomUpMutatorContext struct {
 	bp blueprint.BottomUpMutatorContext
 	baseModuleContext
+	finalPhase bool
 }
 
 func (x *registerMutatorsContext) BottomUp(name string, m BottomUpMutator) MutatorHandle {
+	finalPhase := x.finalPhase
 	f := func(ctx blueprint.BottomUpMutatorContext) {
 		if a, ok := ctx.Module().(Module); ok {
 			actx := &bottomUpMutatorContext{
 				bp:                ctx,
 				baseModuleContext: a.base().baseModuleContextFactory(ctx),
+				finalPhase:        finalPhase,
 			}
 			m(actx)
 		}
@@ -286,6 +299,10 @@
 }
 
 func (b *bottomUpMutatorContext) CreateVariations(variations ...string) []Module {
+	if b.finalPhase {
+		panic("CreateVariations not allowed in FinalDepsMutators")
+	}
+
 	modules := b.bp.CreateVariations(variations...)
 
 	aModules := make([]Module, len(modules))
@@ -300,6 +317,10 @@
 }
 
 func (b *bottomUpMutatorContext) CreateLocalVariations(variations ...string) []Module {
+	if b.finalPhase {
+		panic("CreateLocalVariations not allowed in FinalDepsMutators")
+	}
+
 	modules := b.bp.CreateLocalVariations(variations...)
 
 	aModules := make([]Module, len(modules))
diff --git a/android/mutator_test.go b/android/mutator_test.go
index 2350fdb..191b535 100644
--- a/android/mutator_test.go
+++ b/android/mutator_test.go
@@ -15,9 +15,12 @@
 package android
 
 import (
+	"fmt"
 	"reflect"
+	"strings"
 	"testing"
 
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
 
@@ -56,7 +59,15 @@
 }
 
 func TestMutatorAddMissingDependencies(t *testing.T) {
-	config := TestConfig(buildDir, nil)
+	bp := `
+		test {
+			name: "foo",
+			deps_missing_deps: ["regular_missing_dep"],
+			mutator_missing_deps: ["added_missing_dep"],
+		}
+	`
+
+	config := TestConfig(buildDir, nil, bp, nil)
 	config.TestProductVariables.Allow_missing_dependencies = proptools.BoolPtr(true)
 
 	ctx := NewTestContext()
@@ -67,21 +78,7 @@
 		ctx.TopDown("add_missing_dependencies", addMissingDependenciesMutator)
 	})
 
-	bp := `
-		test {
-			name: "foo",
-			deps_missing_deps: ["regular_missing_dep"],
-			mutator_missing_deps: ["added_missing_dep"],
-		}
-	`
-
-	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)
@@ -139,15 +136,9 @@
 		}
 	`
 
-	mockFS := map[string][]byte{
-		"Android.bp": []byte(bp),
-	}
+	config := TestConfig(buildDir, nil, bp, nil)
 
-	ctx.MockFileSystem(mockFS)
-
-	ctx.Register()
-
-	config := TestConfig(buildDir, nil)
+	ctx.Register(config)
 
 	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
 	FailIfErrored(t, errs)
@@ -197,3 +188,110 @@
 		t.Errorf("want module String() values:\n%q\ngot:\n%q", want, moduleStrings)
 	}
 }
+
+func TestFinalDepsPhase(t *testing.T) {
+	ctx := NewTestContext()
+
+	finalGot := map[string]int{}
+
+	dep1Tag := struct {
+		blueprint.BaseDependencyTag
+	}{}
+	dep2Tag := struct {
+		blueprint.BaseDependencyTag
+	}{}
+
+	ctx.PostDepsMutators(func(ctx RegisterMutatorsContext) {
+		ctx.BottomUp("far_deps_1", func(ctx BottomUpMutatorContext) {
+			if !strings.HasPrefix(ctx.ModuleName(), "common_dep") {
+				ctx.AddFarVariationDependencies([]blueprint.Variation{}, dep1Tag, "common_dep_1")
+			}
+		})
+		ctx.BottomUp("variant", func(ctx BottomUpMutatorContext) {
+			ctx.CreateLocalVariations("a", "b")
+		})
+	})
+
+	ctx.FinalDepsMutators(func(ctx RegisterMutatorsContext) {
+		ctx.BottomUp("far_deps_2", func(ctx BottomUpMutatorContext) {
+			if !strings.HasPrefix(ctx.ModuleName(), "common_dep") {
+				ctx.AddFarVariationDependencies([]blueprint.Variation{}, dep2Tag, "common_dep_2")
+			}
+		})
+		ctx.BottomUp("final", func(ctx BottomUpMutatorContext) {
+			finalGot[ctx.Module().String()] += 1
+			ctx.VisitDirectDeps(func(mod Module) {
+				finalGot[fmt.Sprintf("%s -> %s", ctx.Module().String(), mod)] += 1
+			})
+		})
+	})
+
+	ctx.RegisterModuleType("test", mutatorTestModuleFactory)
+
+	bp := `
+		test {
+			name: "common_dep_1",
+		}
+		test {
+			name: "common_dep_2",
+		}
+		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)
+
+	finalWant := map[string]int{
+		"common_dep_1{variant:a}":                   1,
+		"common_dep_1{variant:b}":                   1,
+		"common_dep_2{variant:a}":                   1,
+		"common_dep_2{variant:b}":                   1,
+		"foo{variant:a}":                            1,
+		"foo{variant:a} -> common_dep_1{variant:a}": 1,
+		"foo{variant:a} -> common_dep_2{variant:a}": 1,
+		"foo{variant:b}":                            1,
+		"foo{variant:b} -> common_dep_1{variant:b}": 1,
+		"foo{variant:b} -> common_dep_2{variant:a}": 1,
+	}
+
+	if !reflect.DeepEqual(finalWant, finalGot) {
+		t.Errorf("want:\n%q\ngot:\n%q", finalWant, finalGot)
+	}
+}
+
+func TestNoCreateVariationsInFinalDeps(t *testing.T) {
+	ctx := NewTestContext()
+
+	checkErr := func() {
+		if err := recover(); err == nil || !strings.Contains(fmt.Sprintf("%s", err), "not allowed in FinalDepsMutators") {
+			panic("Expected FinalDepsMutators consistency check to fail")
+		}
+	}
+
+	ctx.FinalDepsMutators(func(ctx RegisterMutatorsContext) {
+		ctx.BottomUp("vars", func(ctx BottomUpMutatorContext) {
+			defer checkErr()
+			ctx.CreateVariations("a", "b")
+		})
+		ctx.BottomUp("local_vars", func(ctx BottomUpMutatorContext) {
+			defer checkErr()
+			ctx.CreateLocalVariations("a", "b")
+		})
+	})
+
+	ctx.RegisterModuleType("test", mutatorTestModuleFactory)
+	config := TestConfig(buildDir, nil, `test {name: "foo"}`, nil)
+	ctx.Register(config)
+
+	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+	FailIfErrored(t, errs)
+	_, errs = ctx.PrepareBuildActions(config)
+	FailIfErrored(t, errs)
+}
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 90058e3..66c0d89 100644
--- a/android/namespace_test.go
+++ b/android/namespace_test.go
@@ -633,10 +633,9 @@
 }
 
 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", newTestModule)
 	ctx.RegisterModuleType("soong_namespace", NamespaceFactory)
 	ctx.Context.RegisterModuleType("blueprint_test_module", newBlueprintTestModule)
@@ -644,7 +643,7 @@
 	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 48efb4f..cef73fb 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -136,9 +136,6 @@
 		"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.
diff --git a/android/neverallow_test.go b/android/neverallow_test.go
index bd94e37..6f07a4a 100644
--- a/android/neverallow_test.go
+++ b/android/neverallow_test.go
@@ -44,11 +44,11 @@
 			NeverAllow().InDirectDeps("not_allowed_in_direct_deps"),
 		},
 		fs: map[string][]byte{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				cc_library {
 					name: "not_allowed_in_direct_deps",
 				}`),
-			"other/Blueprints": []byte(`
+			"other/Android.bp": []byte(`
 				cc_library {
 					name: "libother",
 					static_libs: ["not_allowed_in_direct_deps"],
@@ -65,7 +65,7 @@
 	{
 		name: "include_dir not allowed to reference art",
 		fs: map[string][]byte{
-			"other/Blueprints": []byte(`
+			"other/Android.bp": []byte(`
 				cc_library {
 					name: "libother",
 					include_dirs: ["art/libdexfile/include"],
@@ -78,7 +78,7 @@
 	{
 		name: "include_dir can reference another location",
 		fs: map[string][]byte{
-			"other/Blueprints": []byte(`
+			"other/Android.bp": []byte(`
 				cc_library {
 					name: "libother",
 					include_dirs: ["another/include"],
@@ -89,7 +89,7 @@
 	{
 		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,
@@ -105,7 +105,7 @@
 	{
 		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,
@@ -121,7 +121,7 @@
 	{
 		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,
@@ -129,7 +129,7 @@
 						enabled: true,
 					},
 				}`),
-			"vendor/Blueprints": []byte(`
+			"vendor/Android.bp": []byte(`
 				cc_library {
 					name: "libvndk2_ext",
 					vendor: true,
@@ -143,7 +143,7 @@
 	{
 		name: "no enforce_vintf_manifest.cflags",
 		fs: map[string][]byte{
-			"Blueprints": []byte(`
+			"Android.bp": []byte(`
 				cc_library {
 					name: "libexample",
 					product_variables: {
@@ -161,7 +161,7 @@
 	{
 		name: "no treble_linker_namespaces.cflags",
 		fs: map[string][]byte{
-			"Blueprints": []byte(`
+			"Android.bp": []byte(`
 				cc_library {
 					name: "libexample",
 					product_variables: {
@@ -178,7 +178,7 @@
 	{
 		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: {
@@ -192,7 +192,7 @@
 	{
 		name: "dependency on updatable-media",
 		fs: map[string][]byte{
-			"Blueprints": []byte(`
+			"Android.bp": []byte(`
 				java_library {
 					name: "needs_updatable_media",
 					libs: ["updatable-media"],
@@ -205,7 +205,7 @@
 	{
 		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"],
@@ -219,7 +219,7 @@
 	{
 		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",
@@ -229,7 +229,7 @@
 	{
 		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",
@@ -242,7 +242,7 @@
 	{
 		name: "sdk_version: \"current\"",
 		fs: map[string][]byte{
-			"Blueprints": []byte(`
+			"Android.bp": []byte(`
 				java_library {
 					name: "outside_core_libraries",
 					sdk_version: "current",
@@ -254,31 +254,29 @@
 func TestNeverallow(t *testing.T) {
 	for _, test := range neverallowTests {
 		// Create a test per config to allow for test specific config, e.g. test rules.
-		config := TestConfig(buildDir, nil)
+		config := TestConfig(buildDir, nil, "", test.fs)
 
 		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, test.fs)
+			_, errs := testNeverallow(config)
 			CheckErrorsAgainstExpectations(t, errs, test.expectedErrors)
 		})
 	}
 }
 
-func testNeverallow(config Config, fs map[string][]byte) (*TestContext, []error) {
+func testNeverallow(config Config) (*TestContext, []error) {
 	ctx := NewTestContext()
 	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
 	}
diff --git a/android/override_module.go b/android/override_module.go
index f946587..9f5127d 100644
--- a/android/override_module.go
+++ b/android/override_module.go
@@ -82,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)
 
@@ -97,6 +100,10 @@
 	OverridablePropertiesDepsMutator(ctx BottomUpMutatorContext)
 }
 
+type overridableModuleProperties struct {
+	OverriddenBy string `blueprint:"mutated"`
+}
+
 // Base module struct for overridable module types
 type OverridableModuleBase struct {
 	// List of OverrideModules that override this base module
@@ -114,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{}) {
@@ -143,11 +155,6 @@
 
 // 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, ctx.ModuleName())
-	}
 	for _, p := range b.overridableProperties {
 		for _, op := range o.getOverridingProperties() {
 			if proptools.TypeEqual(p, op) {
@@ -162,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) {
@@ -247,7 +263,7 @@
 
 func replaceDepsOnOverridingModuleMutator(ctx BottomUpMutatorContext) {
 	if b, ok := ctx.Module().(OverridableModule); ok {
-		if o := b.getOverriddenBy(); o != "" {
+		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.
diff --git a/android/package.go b/android/package.go
index 880d6a9..077c4a4 100644
--- a/android/package.go
+++ b/android/package.go
@@ -22,7 +22,19 @@
 )
 
 func init() {
-	RegisterModuleType("package", PackageFactory)
+	RegisterPackageBuildComponents(InitRegistrationContext)
+}
+
+// Register the package module type and supporting mutators.
+//
+// This must be called in the correct order (relative to other methods that also
+// register mutators) to match the order of mutator registration in mutator.go.
+// Failing to do so will result in an unrealistic test environment.
+func RegisterPackageBuildComponents(ctx RegistrationContext) {
+	ctx.RegisterModuleType("package", PackageFactory)
+
+	// Register mutators that are hard coded in to mutator.go.
+	ctx.HardCodedPreArchMutators(RegisterPackageRenamer)
 }
 
 // The information maintained about each package.
@@ -98,7 +110,7 @@
 }
 
 // Registers the function that renames the packages.
-func registerPackageRenamer(ctx RegisterMutatorsContext) {
+func RegisterPackageRenamer(ctx RegisterMutatorsContext) {
 	ctx.BottomUp("packageRenamer", packageRenamer).Parallel()
 	ctx.BottomUp("packageErrorReporter", packageErrorReporter).Parallel()
 }
diff --git a/android/package_ctx.go b/android/package_ctx.go
index cf8face..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
@@ -177,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
diff --git a/android/package_test.go b/android/package_test.go
index ae286d6..4710e39 100644
--- a/android/package_test.go
+++ b/android/package_test.go
@@ -84,14 +84,11 @@
 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)
+	config := TestArchConfig(buildDir, nil, "", fs)
 
 	ctx := NewTestArchContext()
-	ctx.RegisterModuleType("package", PackageFactory)
-	ctx.PreArchMutators(registerPackageRenamer)
-	ctx.Register()
-
-	ctx.MockFileSystem(fs)
+	RegisterPackageBuildComponents(ctx)
+	ctx.Register(config)
 
 	_, errs := ctx.ParseBlueprintsFiles(".")
 	if len(errs) > 0 {
diff --git a/android/path_properties_test.go b/android/path_properties_test.go
index c859bc5..f367b82 100644
--- a/android/path_properties_test.go
+++ b/android/path_properties_test.go
@@ -97,12 +97,6 @@
 
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
-			config := TestArchConfig(buildDir, nil)
-			ctx := NewTestArchContext()
-
-			ctx.RegisterModuleType("test", pathDepsMutatorTestModuleFactory)
-			ctx.RegisterModuleType("filegroup", FileGroupFactory)
-
 			bp := test.bp + `
 				filegroup {
 					name: "a",
@@ -121,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 1a37a34..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)
 }
@@ -89,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
@@ -109,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()
@@ -378,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)
@@ -397,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 = ""
@@ -512,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)
@@ -704,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...)
 	}
 
@@ -736,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)
@@ -835,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.
@@ -1138,6 +1162,13 @@
 	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 {
@@ -1289,6 +1320,10 @@
 
 func (p PhonyPath) writablePath() {}
 
+func (p PhonyPath) buildDir() string {
+	return p.config.buildDir
+}
+
 var _ Path = PhonyPath{}
 var _ WritablePath = PhonyPath{}
 
@@ -1300,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 {
@@ -1326,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),
 	}
 }
 
@@ -1399,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 1d8afa9..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"
 )
 
@@ -207,10 +206,6 @@
 	inRoot         bool
 }
 
-func (moduleInstallPathContextImpl) Fs() pathtools.FileSystem {
-	return pathtools.MockFs(nil)
-}
-
 func (m moduleInstallPathContextImpl) Config() Config {
 	return m.baseModuleContext.config
 }
@@ -241,8 +236,12 @@
 	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}
@@ -257,6 +256,7 @@
 			name: "host binary",
 			ctx: &moduleInstallPathContextImpl{
 				baseModuleContext: baseModuleContext{
+					os:     hostTarget.Os,
 					target: hostTarget,
 				},
 			},
@@ -268,6 +268,7 @@
 			name: "system binary",
 			ctx: &moduleInstallPathContextImpl{
 				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
 					target: deviceTarget,
 				},
 			},
@@ -278,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"},
@@ -289,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"},
@@ -300,8 +307,11 @@
 			name: "product binary",
 			ctx: &moduleInstallPathContextImpl{
 				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
 					target: deviceTarget,
-					kind:   productSpecificModule,
+					earlyModuleContext: earlyModuleContext{
+						kind: productSpecificModule,
+					},
 				},
 			},
 			in:  []string{"bin", "my_test"},
@@ -311,8 +321,11 @@
 			name: "system_ext binary",
 			ctx: &moduleInstallPathContextImpl{
 				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
 					target: deviceTarget,
-					kind:   systemExtSpecificModule,
+					earlyModuleContext: earlyModuleContext{
+						kind: systemExtSpecificModule,
+					},
 				},
 			},
 			in:  []string{"bin", "my_test"},
@@ -322,6 +335,7 @@
 			name: "root binary",
 			ctx: &moduleInstallPathContextImpl{
 				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
 					target: deviceTarget,
 				},
 				inRoot: true,
@@ -333,6 +347,7 @@
 			name: "recovery binary",
 			ctx: &moduleInstallPathContextImpl{
 				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
 					target: deviceTarget,
 				},
 				inRecovery: true,
@@ -344,6 +359,7 @@
 			name: "recovery root binary",
 			ctx: &moduleInstallPathContextImpl{
 				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
 					target: deviceTarget,
 				},
 				inRecovery: true,
@@ -357,6 +373,7 @@
 			name: "system native test binary",
 			ctx: &moduleInstallPathContextImpl{
 				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
 					target: deviceTarget,
 				},
 				inData: true,
@@ -368,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,
 			},
@@ -380,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,
 			},
@@ -392,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,
 			},
@@ -405,8 +431,11 @@
 			name: "system_ext native test binary",
 			ctx: &moduleInstallPathContextImpl{
 				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
 					target: deviceTarget,
-					kind:   systemExtSpecificModule,
+					earlyModuleContext: earlyModuleContext{
+						kind: systemExtSpecificModule,
+					},
 				},
 				inData: true,
 			},
@@ -418,6 +447,7 @@
 			name: "sanitized system binary",
 			ctx: &moduleInstallPathContextImpl{
 				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
 					target: deviceTarget,
 				},
 				inSanitizerDir: true,
@@ -429,8 +459,11 @@
 			name: "sanitized vendor binary",
 			ctx: &moduleInstallPathContextImpl{
 				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
 					target: deviceTarget,
-					kind:   socSpecificModule,
+					earlyModuleContext: earlyModuleContext{
+						kind: socSpecificModule,
+					},
 				},
 				inSanitizerDir: true,
 			},
@@ -441,8 +474,11 @@
 			name: "sanitized odm binary",
 			ctx: &moduleInstallPathContextImpl{
 				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
 					target: deviceTarget,
-					kind:   deviceSpecificModule,
+					earlyModuleContext: earlyModuleContext{
+						kind: deviceSpecificModule,
+					},
 				},
 				inSanitizerDir: true,
 			},
@@ -453,8 +489,11 @@
 			name: "sanitized product binary",
 			ctx: &moduleInstallPathContextImpl{
 				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
 					target: deviceTarget,
-					kind:   productSpecificModule,
+					earlyModuleContext: earlyModuleContext{
+						kind: productSpecificModule,
+					},
 				},
 				inSanitizerDir: true,
 			},
@@ -466,8 +505,11 @@
 			name: "sanitized system_ext binary",
 			ctx: &moduleInstallPathContextImpl{
 				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
 					target: deviceTarget,
-					kind:   systemExtSpecificModule,
+					earlyModuleContext: earlyModuleContext{
+						kind: systemExtSpecificModule,
+					},
 				},
 				inSanitizerDir: true,
 			},
@@ -479,6 +521,7 @@
 			name: "sanitized system native test binary",
 			ctx: &moduleInstallPathContextImpl{
 				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
 					target: deviceTarget,
 				},
 				inData:         true,
@@ -491,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,
@@ -504,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,
@@ -517,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,
@@ -530,8 +582,11 @@
 			name: "sanitized system_ext native test binary",
 			ctx: &moduleInstallPathContextImpl{
 				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
 					target: deviceTarget,
-					kind:   systemExtSpecificModule,
+					earlyModuleContext: earlyModuleContext{
+						kind: systemExtSpecificModule,
+					},
 				},
 				inData:         true,
 				inSanitizerDir: true,
@@ -555,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"),
@@ -730,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 {
@@ -862,7 +918,6 @@
 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", pathForModuleSrcTestModuleFactory)
@@ -896,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)
@@ -1073,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", pathForModuleSrcTestModuleFactory)
-
 	bp := `
 		test {
 			name: "foo",
@@ -1097,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)
@@ -1136,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")
@@ -1150,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 0ee1201..c780cb2 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -25,12 +25,20 @@
 // 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
 
+// Mark this tag so dependencies that use it are excluded from visibility enforcement.
+func (t prebuiltDependencyTag) ExcludeFromVisibilityEnforcement() {}
+
 type PrebuiltProperties struct {
 	// When prefer is set to true the prebuilt will be used instead of any source module with
 	// a matching name.
diff --git a/android/prebuilt_etc.go b/android/prebuilt_etc.go
index 2701185..388d17f 100644
--- a/android/prebuilt_etc.go
+++ b/android/prebuilt_etc.go
@@ -165,12 +165,12 @@
 	})
 }
 
-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),
@@ -187,7 +187,7 @@
 				}
 			},
 		},
-	}
+	}}
 }
 
 func InitPrebuiltEtcModule(p *PrebuiltEtc, dirBase string) {
diff --git a/android/prebuilt_etc_test.go b/android/prebuilt_etc_test.go
index 3855dac..6e751e7 100644
--- a/android/prebuilt_etc_test.go
+++ b/android/prebuilt_etc_test.go
@@ -21,7 +21,14 @@
 )
 
 func testPrebuiltEtc(t *testing.T, bp string) (*TestContext, Config) {
-	config := TestArchConfig(buildDir, nil)
+	fs := map[string][]byte{
+		"foo.conf": nil,
+		"bar.conf": nil,
+		"baz.conf": nil,
+	}
+
+	config := TestArchConfig(buildDir, nil, bp, fs)
+
 	ctx := NewTestArchContext()
 	ctx.RegisterModuleType("prebuilt_etc", PrebuiltEtcFactory)
 	ctx.RegisterModuleType("prebuilt_etc_host", PrebuiltEtcHostFactory)
@@ -29,17 +36,7 @@
 	ctx.RegisterModuleType("prebuilt_usr_share_host", PrebuiltUserShareHostFactory)
 	ctx.RegisterModuleType("prebuilt_font", PrebuiltFontFactory)
 	ctx.RegisterModuleType("prebuilt_firmware", PrebuiltFirmwareFactory)
-	ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
-		ctx.BottomUp("prebuilt_etc", ImageMutator).Parallel()
-	})
-	ctx.Register()
-	mockFiles := map[string][]byte{
-		"Android.bp": []byte(bp),
-		"foo.conf":   nil,
-		"bar.conf":   nil,
-		"baz.conf":   nil,
-	}
-	ctx.MockFileSystem(mockFiles)
+	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,7 +178,7 @@
 		}
 	`)
 
-	p := ctx.ModuleForTests("foo.conf", "android_arm64_armv8-a_core").Module().(*PrebuiltEtc)
+	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())
@@ -213,7 +210,7 @@
 		}
 	`)
 
-	p := ctx.ModuleForTests("foo.conf", "android_arm64_armv8-a_core").Module().(*PrebuiltEtc)
+	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())
@@ -248,7 +245,7 @@
 	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)
+			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 81fb278..8ff5c40 100644
--- a/android/prebuilt_test.go
+++ b/android/prebuilt_test.go
@@ -125,29 +125,27 @@
 }
 
 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", FileGroupFactory)
-			ctx.RegisterModuleType("prebuilt", newPrebuiltModule)
-			ctx.RegisterModuleType("source", 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()
+			registerTestPrebuiltBuildComponents(ctx)
+			ctx.RegisterModuleType("filegroup", FileGroupFactory)
+			ctx.Register(config)
+
+			_, errs := ctx.ParseBlueprintsFiles("Android.bp")
 			FailIfErrored(t, errs)
 			_, errs = ctx.PrepareBuildActions(config)
 			FailIfErrored(t, errs)
@@ -212,6 +210,13 @@
 	}
 }
 
+func registerTestPrebuiltBuildComponents(ctx RegistrationContext) {
+	ctx.RegisterModuleType("prebuilt", newPrebuiltModule)
+	ctx.RegisterModuleType("source", newSourceModule)
+
+	RegisterPrebuiltMutators(ctx)
+}
+
 type prebuiltModule struct {
 	ModuleBase
 	prebuilt   Prebuilt
diff --git a/android/register.go b/android/register.go
index d79982a..ccfe01e 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() {
@@ -98,7 +102,7 @@
 		ctx.RegisterSingletonType(t.name, t.factory)
 	}
 
-	registerMutators(ctx.Context, preArch, preDeps, postDeps)
+	registerMutators(ctx.Context, preArch, preDeps, postDeps, finalDeps)
 
 	// Register makevars after other singletons so they can export values through makevars
 	ctx.RegisterSingletonType("makevars", SingletonFactoryAdaptor(makeVarsSingletonFunc))
@@ -114,3 +118,87 @@
 	}
 	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)
+
+	// Register pre arch mutators that are hard coded into mutator.go.
+	//
+	// Only registers mutators for testing, is a noop on the InitRegistrationContext.
+	HardCodedPreArchMutators(f RegisterMutatorFunc)
+
+	PreDepsMutators(f RegisterMutatorFunc)
+	PostDepsMutators(f RegisterMutatorFunc)
+	FinalDepsMutators(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) HardCodedPreArchMutators(f RegisterMutatorFunc) {
+	// Nothing to do as the mutators are hard code in preArch in mutator.go
+}
+
+func (ctx *initRegistrationContext) PreDepsMutators(f RegisterMutatorFunc) {
+	PreDepsMutators(f)
+}
+
+func (ctx *initRegistrationContext) PostDepsMutators(f RegisterMutatorFunc) {
+	PostDepsMutators(f)
+}
+
+func (ctx *initRegistrationContext) FinalDepsMutators(f RegisterMutatorFunc) {
+	FinalDepsMutators(f)
+}
diff --git a/android/rule_builder_test.go b/android/rule_builder_test.go
index 52c32df..acf8127 100644
--- a/android/rule_builder_test.go
+++ b/android/rule_builder_test.go
@@ -27,19 +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,
-			"javac":   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() {
@@ -276,7 +275,7 @@
 		"input3":    nil,
 	}
 
-	ctx := PathContextForTesting(TestConfig("out", nil), fs)
+	ctx := PathContextForTesting(TestConfig("out", nil, "", fs))
 
 	addCommands := func(rule *RuleBuilder) {
 		cmd := rule.Command().
@@ -445,6 +444,11 @@
 }
 
 func TestRuleBuilder_Build(t *testing.T) {
+	fs := map[string][]byte{
+		"bar": nil,
+		"cp":  nil,
+	}
+
 	bp := `
 		rule_builder_test {
 			name: "foo",
@@ -458,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", testRuleBuilderFactory)
 	ctx.RegisterSingletonType("rule_builder_test", testRuleBuilderSingletonFactory)
-	ctx.Register()
+	ctx.Register(config)
 
 	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
 	FailIfErrored(t, errs)
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
index 01e18ed..9e6ad16 100644
--- a/android/sdk.go
+++ b/android/sdk.go
@@ -15,8 +15,10 @@
 package android
 
 import (
+	"sort"
 	"strings"
 
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
 
@@ -31,9 +33,6 @@
 	MemberName() string
 	BuildWithSdks(sdks SdkRefs)
 	RequiredSdks() SdkRefs
-
-	// Build a snapshot of the module.
-	BuildSnapshot(sdkModuleContext ModuleContext, builder SnapshotBuilder)
 }
 
 // SdkRef refers to a version of an SDK
@@ -180,7 +179,7 @@
 	// 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(name string, moduleType string) BpModule
+	AddPrebuiltModule(member SdkMember, moduleType string) BpModule
 }
 
 // A set of properties for use in a .bp file.
@@ -203,3 +202,145 @@
 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 2b649c4..7d9dc67 100644
--- a/android/sh_binary.go
+++ b/android/sh_binary.go
@@ -16,6 +16,7 @@
 
 import (
 	"fmt"
+	"path/filepath"
 	"strings"
 )
 
@@ -74,8 +75,11 @@
 
 	sourceFilePath Path
 	outputFilePath OutputPath
+	installedFile  InstallPath
 }
 
+var _ HostToolProvider = (*ShBinary)(nil)
+
 type ShTest struct {
 	ShBinary
 
@@ -84,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
 }
@@ -110,7 +114,7 @@
 	return s.properties.Symlinks
 }
 
-func (s *ShBinary) GenerateAndroidBuildActions(ctx ModuleContext) {
+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)
@@ -135,8 +139,14 @@
 	})
 }
 
-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",
@@ -145,7 +155,7 @@
 				s.customAndroidMkEntries(entries)
 			},
 		},
-	}
+	}}
 }
 
 func (s *ShBinary) customAndroidMkEntries(entries *AndroidMkEntries) {
@@ -158,13 +168,28 @@
 }
 
 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",
@@ -185,7 +210,7 @@
 				}
 			},
 		},
-	}
+	}}
 }
 
 func InitShBinaryModule(s *ShBinary) {
diff --git a/android/sh_binary_test.go b/android/sh_binary_test.go
index a138754..137e773 100644
--- a/android/sh_binary_test.go
+++ b/android/sh_binary_test.go
@@ -6,19 +6,18 @@
 )
 
 func testShBinary(t *testing.T, bp string) (*TestContext, Config) {
-	config := TestArchConfig(buildDir, nil)
-
-	ctx := NewTestArchContext()
-	ctx.RegisterModuleType("sh_test", ShTestFactory)
-	ctx.RegisterModuleType("sh_test_host", ShTestHostFactory)
-	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)
@@ -42,7 +41,7 @@
 
 	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) {
diff --git a/android/singleton.go b/android/singleton.go
index 5519ca0..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
@@ -74,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 {
diff --git a/android/testing.go b/android/testing.go
index 4b55920..9aff039 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
@@ -52,14 +50,20 @@
 
 type TestContext struct {
 	*Context
-	preArch, preDeps, postDeps []RegisterMutatorFunc
-	NameResolver               *NameResolver
+	preArch, preDeps, postDeps, finalDeps []RegisterMutatorFunc
+	NameResolver                          *NameResolver
+	config                                Config
 }
 
 func (ctx *TestContext) PreArchMutators(f RegisterMutatorFunc) {
 	ctx.preArch = append(ctx.preArch, f)
 }
 
+func (ctx *TestContext) HardCodedPreArchMutators(f RegisterMutatorFunc) {
+	// Register mutator function as normal for testing.
+	ctx.PreArchMutators(f)
+}
+
 func (ctx *TestContext) PreDepsMutators(f RegisterMutatorFunc) {
 	ctx.preDeps = append(ctx.preDeps, f)
 }
@@ -68,10 +72,32 @@
 	ctx.postDeps = append(ctx.postDeps, f)
 }
 
-func (ctx *TestContext) Register() {
-	registerMutators(ctx.Context.Context, ctx.preArch, ctx.preDeps, ctx.postDeps)
+func (ctx *TestContext) FinalDepsMutators(f RegisterMutatorFunc) {
+	ctx.finalDeps = append(ctx.finalDeps, f)
+}
+
+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.finalDeps)
 
 	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) {
@@ -132,25 +158,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
@@ -403,15 +410,18 @@
 
 }
 
-func AndroidMkEntriesForTest(t *testing.T, config Config, bpPath string, mod blueprint.Module) AndroidMkEntries {
+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 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 {
@@ -424,3 +434,33 @@
 	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/variable.go b/android/variable.go
index bcab46a..c588672 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -289,6 +289,7 @@
 	Exclude_draft_ndk_apis *bool `json:",omitempty"`
 
 	Flatten_apex *bool `json:",omitempty"`
+	Aml_abis     *bool `json:",omitempty"`
 
 	DexpreoptGlobalConfig *string `json:",omitempty"`
 
@@ -307,11 +308,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 {
diff --git a/android/variable_test.go b/android/variable_test.go
index 1826e39..451d43d 100644
--- a/android/variable_test.go
+++ b/android/variable_test.go
@@ -198,18 +198,11 @@
 			name: "baz",
 		}
 	`
-
-	mockFS := map[string][]byte{
-		"Android.bp": []byte(bp),
-	}
-
-	ctx.MockFileSystem(mockFS)
-
-	ctx.Register()
-
-	config := TestConfig(buildDir, nil)
+	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)
diff --git a/android/visibility.go b/android/visibility.go
index a7e718b..a597687 100644
--- a/android/visibility.go
+++ b/android/visibility.go
@@ -19,6 +19,8 @@
 	"regexp"
 	"strings"
 	"sync"
+
+	"github.com/google/blueprint"
 )
 
 // Enforces visibility rules between modules.
@@ -117,12 +119,15 @@
 }
 
 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).
@@ -187,9 +192,18 @@
 	}).(*sync.Map)
 }
 
+// Marker interface that identifies dependencies that are excluded from visibility
+// enforcement.
+type ExcludeFromVisibilityEnforcementTag interface {
+	blueprint.DependencyTag
+
+	// Method that differentiates this interface from others.
+	ExcludeFromVisibilityEnforcement()
+}
+
 // 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()
 }
 
@@ -199,12 +213,12 @@
 // having to process multiple variants for each module. This goes after defaults expansion to gather
 // 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) {
+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()
 }
 
@@ -384,10 +398,14 @@
 
 	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) {
+		// Ignore dependencies that have an ExcludeFromVisibilityEnforcementTag
+		tag := ctx.OtherModuleDependencyTag(dep)
+		if _, ok := tag.(ExcludeFromVisibilityEnforcementTag); ok {
+			return
+		}
+
 		depName := ctx.OtherModuleName(dep)
 		depDir := ctx.OtherModuleDir(dep)
 		depQualified := qualifiedModuleName{depDir, depName}
@@ -397,19 +415,25 @@
 			return
 		}
 
-		value, ok := moduleToVisibilityRule.Load(depQualified)
-		var rule compositeRule
-		if ok {
-			rule = value.(compositeRule)
-		} else {
-			rule = packageDefaultVisibility(ctx, 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()
@@ -433,3 +457,19 @@
 		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 fd9e98c..6006072 100644
--- a/android/visibility_test.go
+++ b/android/visibility_test.go
@@ -853,6 +853,51 @@
 				` not visible to this module`,
 		},
 	},
+	{
+		name: "verify that prebuilt dependencies are ignored for visibility reasons (not preferred)",
+		fs: map[string][]byte{
+			"prebuilts/Blueprints": []byte(`
+				prebuilt {
+					name: "module",
+					visibility: ["//top/other"],
+				}`),
+			"top/sources/source_file": nil,
+			"top/sources/Blueprints": []byte(`
+				source {
+					name: "module",
+					visibility: ["//top/other"],
+				}`),
+			"top/other/source_file": nil,
+			"top/other/Blueprints": []byte(`
+				source {
+					name: "other",
+					deps: [":module"],
+				}`),
+		},
+	},
+	{
+		name: "verify that prebuilt dependencies are ignored for visibility reasons (preferred)",
+		fs: map[string][]byte{
+			"prebuilts/Blueprints": []byte(`
+				prebuilt {
+					name: "module",
+					visibility: ["//top/other"],
+					prefer: true,
+				}`),
+			"top/sources/source_file": nil,
+			"top/sources/Blueprints": []byte(`
+				source {
+					name: "module",
+					visibility: ["//top/other"],
+				}`),
+			"top/other/source_file": nil,
+			"top/other/Blueprints": []byte(`
+				source {
+					name: "other",
+					deps: [":module"],
+				}`),
+		},
+	},
 }
 
 func TestVisibility(t *testing.T) {
@@ -868,20 +913,20 @@
 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("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)
+	// Order of the following method calls is significant.
+	RegisterPackageBuildComponents(ctx)
+	registerTestPrebuiltBuildComponents(ctx)
+	ctx.PreArchMutators(RegisterVisibilityRuleChecker)
+	ctx.PreArchMutators(RegisterDefaultsPreArchMutators)
+	ctx.PreArchMutators(RegisterVisibilityRuleGatherer)
+	ctx.PostDepsMutators(RegisterVisibilityRuleEnforcer)
+	ctx.Register(config)
 
 	_, errs := ctx.ParseBlueprintsFiles(".")
 	if len(errs) > 0 {
diff --git a/android/vts_config_test.go b/android/vts_config_test.go
index 162944d..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", VtsConfigFactory)
-	ctx.Register()
-	mockFiles := map[string][]byte{
-		"Android.bp": []byte(bpFileContents),
-	}
-	ctx.MockFileSystem(mockFiles)
+	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 4110073..70fc1f7 100644
--- a/androidmk/Android.bp
+++ b/androidmk/Android.bp
@@ -41,7 +41,6 @@
         "androidmk-parser",
         "blueprint-parser",
         "bpfix-lib",
-        "soong-android",
     ],
 }
 
diff --git a/androidmk/androidmk/android.go b/androidmk/androidmk/android.go
index 0082d8b..8860984 100644
--- a/androidmk/androidmk/android.go
+++ b/androidmk/androidmk/android.go
@@ -15,9 +15,9 @@
 package androidmk
 
 import (
-	"android/soong/android"
 	mkparser "android/soong/androidmk/parser"
 	"fmt"
+	"sort"
 	"strings"
 
 	bpparser "github.com/google/blueprint/parser"
@@ -350,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)
diff --git a/apex/androidmk.go b/apex/androidmk.go
index 35622f0..f1194be 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -22,7 +22,6 @@
 
 	"android/soong/android"
 	"android/soong/cc"
-	"android/soong/java"
 
 	"github.com/google/blueprint/proptools"
 )
@@ -53,18 +52,49 @@
 		return moduleNames
 	}
 
+	var postInstallCommands []string
+	for _, fi := range a.filesInfo {
+		if a.linkToSystemLib && fi.transitiveDep && fi.AvailableToPlatform() {
+			// TODO(jiyong): pathOnDevice should come from fi.module, not being calculated here
+			linkTarget := filepath.Join("/system", fi.Path())
+			linkPath := filepath.Join(a.installDir.ToMakePath().String(), apexName, fi.Path())
+			mkdirCmd := "mkdir -p " + filepath.Dir(linkPath)
+			linkCmd := "ln -sfn " + linkTarget + " " + linkPath
+			postInstallCommands = append(postInstallCommands, mkdirCmd, linkCmd)
+		}
+	}
+	postInstallCommands = append(postInstallCommands, a.compatSymlinks...)
+
 	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)
+		linkToSystemLib := a.linkToSystemLib && fi.transitiveDep && fi.AvailableToPlatform()
+
+		var moduleName string
+		if linkToSystemLib {
+			moduleName = fi.moduleName
+		} else {
+			moduleName = fi.moduleName + "." + apexName + a.suffix
+		}
+
+		if !android.InList(moduleName, moduleNames) {
+			moduleNames = append(moduleNames, moduleName)
+		}
+
+		if linkToSystemLib {
+			// No need to copy the file since it's linked to the system file
+			continue
 		}
 
 		fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
-		fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
-		fmt.Fprintln(w, "LOCAL_MODULE :=", fi.moduleName)
+		if fi.moduleDir != "" {
+			fmt.Fprintln(w, "LOCAL_PATH :=", fi.moduleDir)
+		} else {
+			fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
+		}
+		fmt.Fprintln(w, "LOCAL_MODULE :=", moduleName)
 		// /apex/<apex_name>/{lib|framework|...}
 		pathWhenActivated := filepath.Join("$(PRODUCT_OUT)", "apex", apexName, fi.installDir)
 		if apexType == flattenedApex {
@@ -114,8 +144,11 @@
 				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.(*java.Library)
+			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
@@ -125,6 +158,12 @@
 			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 {
@@ -139,9 +178,19 @@
 			fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_cc_prebuilt.mk")
 		} else {
 			fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", fi.builtFile.Base())
-			// For flattened apexes, compat symlinks are attached to apex_manifest.json which is guaranteed for every apex
-			if a.primaryApexType && fi.builtFile == a.manifestPbOut && len(a.compatSymlinks) > 0 {
-				fmt.Fprintln(w, "LOCAL_POST_INSTALL_CMD :=", strings.Join(a.compatSymlinks, " && "))
+			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 apexType == flattenedApex && len(postInstallCommands) > 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(postInstallCommands, " && "))
+				}
 			}
 			fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
 		}
@@ -149,6 +198,27 @@
 	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) {
@@ -167,6 +237,7 @@
 				if len(moduleNames) > 0 {
 					fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES :=", strings.Join(moduleNames, " "))
 				}
+				a.writeRequiredModules(w)
 				fmt.Fprintln(w, "include $(BUILD_PHONY_PACKAGE)")
 
 			} else {
@@ -178,13 +249,14 @@
 				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.properties.Overrides, " "))
+				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.externalDeps) > 0 {
-					fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES +=", strings.Join(a.externalDeps, " "))
+				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 "+
@@ -200,6 +272,14 @@
 				if apexType == imageApex {
 					fmt.Fprintln(w, "ALL_MODULES.$(LOCAL_MODULE).BUNDLE :=", a.bundleModuleFile.String())
 				}
+
+				if a.installedFilesFile != nil {
+					goal := "droidcore"
+					distFile := name + "-installed-files.txt"
+					fmt.Fprintln(w, ".PHONY:", goal)
+					fmt.Fprintf(w, "$(call dist-for-goals,%s,%s:%s)\n",
+						goal, a.installedFilesFile.String(), distFile)
+				}
 			}
 		}}
 }
diff --git a/apex/apex.go b/apex/apex.go
index d5d12e4..cfd417d 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -57,8 +57,249 @@
 	certificateTag = dependencyTag{name: "certificate"}
 	usesTag        = dependencyTag{name: "uses"}
 	androidAppTag  = dependencyTag{name: "androidApp"}
+	apexAvailWl    = makeApexAvailableWhitelist()
 )
 
+// This is a map from apex to modules, which overrides the
+// apex_available setting for that particular module to make
+// it available for the apex regardless of its setting.
+// TODO(b/147364041): remove this
+func makeApexAvailableWhitelist() map[string][]string {
+	// The "Module separator"s below are employed to minimize merge conflicts.
+	m := make(map[string][]string)
+	//
+	// Module separator
+	//
+	m["com.android.adbd"] = []string{"adbd", "libcrypto"}
+	//
+	// Module separator
+	//
+	m["com.android.art"] = []string{
+		"jacocoagent",
+		"libadbconnection_server",
+		"libartd-disassembler",
+		"libbacktrace",
+		"libbase",
+		"libc++",
+		"libcrypto",
+		"libdexfile_support",
+		"libexpat",
+		"libicuuc",
+		"liblzma",
+		"libmeminfo",
+		"libprocinfo",
+		"libunwindstack",
+		"libvixl",
+		"libvixld",
+		"libz",
+		"libziparchive",
+		"prebuilt_libclang_rt",
+	}
+	//
+	// Module separator
+	//
+	m["com.android.bluetooth.updatable"] = []string{
+		"android.hardware.audio.common@5.0",
+		"android.hardware.bluetooth@1.0",
+		"android.hardware.bluetooth@1.1",
+		"android.hardware.bluetooth.a2dp@1.0",
+		"android.hardware.bluetooth.audio@2.0",
+		"android.hidl.safe_union@1.0",
+		"libbase",
+		"libbinderthreadstate",
+		"libbluetooth",
+		"libbluetooth_jni",
+		"libc++",
+		"libchrome",
+		"libcrypto",
+		"libcutils",
+		"libevent",
+		"libfmq",
+		"libhidlbase",
+		"libprocessgroup",
+		"libprotobuf-cpp-lite",
+		"libstatslog",
+		"libtinyxml2",
+		"libutils",
+		"libz",
+	}
+	//
+	// Module separator
+	//
+	m["com.android.conscrypt"] = []string{"boringssl_self_test", "libc++", "libcrypto", "libssl"}
+	//
+	// Module separator
+	//
+	m["com.android.cronet"] = []string{"org.chromium.net.cronet", "prebuilt_libcronet.80.0.3986.0"}
+	//
+	// Module separator
+	//
+	m["com.android.media"] = []string{
+		"android.hardware.cas@1.0",
+		"android.hardware.cas.native@1.0",
+		"android.hidl.allocator@1.0",
+		"android.hidl.memory@1.0",
+		"android.hidl.memory.token@1.0",
+		"android.hidl.token@1.0",
+		"android.hidl.token@1.0-utils",
+		"libaacextractor",
+		"libamrextractor",
+		"libbase",
+		"libbinderthreadstate",
+		"libc++",
+		"libcrypto",
+		"libcutils",
+		"libflacextractor",
+		"libhidlbase",
+		"libhidlmemory",
+		"libmidiextractor",
+		"libmkvextractor",
+		"libmp3extractor",
+		"libmp4extractor",
+		"libmpeg2extractor",
+		"liboggextractor",
+		"libprocessgroup",
+		"libutils",
+		"libwavextractor",
+		"updatable-media",
+	}
+	//
+	// Module separator
+	//
+	m["com.android.media.swcodec"] = []string{
+		"android.frameworks.bufferhub@1.0",
+		"android.hardware.configstore@1.0",
+		"android.hardware.configstore@1.1",
+		"android.hardware.configstore-utils",
+		"android.hardware.graphics.allocator@2.0",
+		"android.hardware.graphics.allocator@3.0",
+		"android.hardware.graphics.bufferqueue@1.0",
+		"android.hardware.graphics.bufferqueue@2.0",
+		"android.hardware.graphics.common@1.0",
+		"android.hardware.graphics.common@1.1",
+		"android.hardware.graphics.common@1.2",
+		"android.hardware.graphics.mapper@2.0",
+		"android.hardware.graphics.mapper@2.1",
+		"android.hardware.graphics.mapper@3.0",
+		"android.hardware.media@1.0",
+		"android.hardware.media.bufferpool@2.0",
+		"android.hardware.media.c2@1.0",
+		"android.hardware.media.omx@1.0",
+		"android.hidl.memory@1.0",
+		"android.hidl.memory.token@1.0",
+		"android.hidl.safe_union@1.0",
+		"android.hidl.token@1.0",
+		"android.hidl.token@1.0-utils",
+		"libavservices_minijail",
+		"libbacktrace",
+		"libbase",
+		"libbinderthreadstate",
+		"libc++",
+		"libcap",
+		"libcodec2",
+		"libcodec2_hidl@1.0",
+		"libcodec2_soft_aacdec",
+		"libcodec2_soft_aacenc",
+		"libcodec2_soft_amrnbdec",
+		"libcodec2_soft_amrnbenc",
+		"libcodec2_soft_amrwbdec",
+		"libcodec2_soft_amrwbenc",
+		"libcodec2_soft_av1dec_gav1",
+		"libcodec2_soft_avcdec",
+		"libcodec2_soft_avcenc",
+		"libcodec2_soft_common",
+		"libcodec2_soft_flacdec",
+		"libcodec2_soft_flacenc",
+		"libcodec2_soft_g711alawdec",
+		"libcodec2_soft_g711mlawdec",
+		"libcodec2_soft_gsmdec",
+		"libcodec2_soft_h263dec",
+		"libcodec2_soft_h263enc",
+		"libcodec2_soft_hevcdec",
+		"libcodec2_soft_hevcenc",
+		"libcodec2_soft_mp3dec",
+		"libcodec2_soft_mpeg2dec",
+		"libcodec2_soft_mpeg4dec",
+		"libcodec2_soft_mpeg4enc",
+		"libcodec2_soft_opusdec",
+		"libcodec2_soft_opusenc",
+		"libcodec2_soft_rawdec",
+		"libcodec2_soft_vorbisdec",
+		"libcodec2_soft_vp8dec",
+		"libcodec2_soft_vp8enc",
+		"libcodec2_soft_vp9dec",
+		"libcodec2_soft_vp9enc",
+		"libcodec2_vndk",
+		"libc_scudo",
+		"libcutils",
+		"libdexfile_support",
+		"libEGL",
+		"libfmq",
+		"libgraphicsenv",
+		"libhardware",
+		"libhidlbase",
+		"libhidlmemory",
+		"libion",
+		"liblzma",
+		"libmedia_codecserviceregistrant",
+		"libminijail",
+		"libnativebridge_lazy",
+		"libnativeloader_lazy",
+		"libopus",
+		"libprocessgroup",
+		"libscudo_wrapper",
+		"libsfplugin_ccodec_utils",
+		"libstagefright_amrnb_common",
+		"libstagefright_bufferpool@2.0.1",
+		"libstagefright_bufferqueue_helper",
+		"libstagefright_enc_common",
+		"libstagefright_flacdec",
+		"libstagefright_foundation",
+		"libsync",
+		"libui",
+		"libunwindstack",
+		"libutils",
+		"libvorbisidec",
+		"libvpx",
+		"mediaswcodec",
+		"prebuilt_libclang_rt",
+	}
+	//
+	// Module separator
+	//
+	m["com.android.runtime"] = []string{
+		"libbase",
+		"libc++",
+		"libdexfile_support",
+		"liblzma",
+		"libunwindstack",
+		"prebuilt_libclang_rt",
+	}
+	//
+	// Module separator
+	//
+	m["com.android.resolv"] = []string{"libcrypto", "libnetd_resolv", "libssl"}
+	//
+	// Module separator
+	//
+	m["com.android.tethering"] = []string{"libbase", "libc++", "libnativehelper_compat_libc++"}
+	//
+	// Module separator
+	//
+	m["com.android.vndk"] = []string{
+		"libbacktrace",
+		"libbinderthreadstate",
+		"libblas",
+		"libcompiler_rt",
+		"libgui",
+		"libunwind",
+	}
+	//
+	// Module separator
+	//
+	return m
+}
+
 func init() {
 	android.RegisterModuleType("apex", BundleFactory)
 	android.RegisterModuleType("apex_test", testApexBundleFactory)
@@ -105,7 +346,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 {
@@ -333,12 +575,15 @@
 	// also built with the SDKs specified here.
 	Uses_sdks []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
+	// 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 {
@@ -368,6 +613,13 @@
 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
@@ -451,20 +703,58 @@
 	// 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(builtFile android.Path, moduleName string, installDir string, class apexFileClass, module android.Module) apexFile {
-	return apexFile{
+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() == ""
+	return af.builtFile != nil && af.builtFile.String() != ""
+}
+
+// Path() returns path of this apex file relative to the APEX root
+func (af *apexFile) Path() string {
+	return filepath.Join(af.installDir, af.builtFile.Base())
+}
+
+// SymlinkPaths() returns paths of the symlinks (if any) relative to the APEX root
+func (af *apexFile) SymlinkPaths() []string {
+	var ret []string
+	for _, symlink := range af.symlinks {
+		ret = append(ret, filepath.Join(af.installDir, symlink))
+	}
+	return ret
+}
+
+func (af *apexFile) AvailableToPlatform() bool {
+	if af.module == nil {
+		return false
+	}
+	if am, ok := af.module.(android.ApexModule); ok {
+		return am.AvailableFor(android.AvailableToPlatform)
+	}
+	return false
 }
 
 type apexBundle struct {
@@ -475,9 +765,11 @@
 
 	properties            apexBundleProperties
 	targetProperties      apexTargetBundleProperties
-	vndkProperties        apexVndkProperties
 	overridableProperties overridableProperties
 
+	// specific to apex_vndk modules
+	vndkProperties apexVndkProperties
+
 	bundleModuleFile android.WritablePath
 	outputFile       android.WritablePath
 	installDir       android.InstallPath
@@ -495,28 +787,37 @@
 	// 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
 
 	testApex        bool
 	vndkApex        bool
 	artApex         bool
 	primaryApexType bool
 
-	// intermediate path for apex_manifest.json
-	manifestJsonOut     android.WritablePath
-	manifestJsonFullOut android.WritablePath
-	manifestPbOut       android.WritablePath
+	manifestJsonOut android.WritablePath
+	manifestPbOut   android.WritablePath
 
-	// list of commands to create symlinks for backward compatibility
+	// 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.json(for flattened build)
+	// 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
+
+	installedFilesFile android.WritablePath
+
+	// Whether to create symlink to the system file instead of having a file
+	// inside the apex or not
+	linkToSystemLib bool
 }
 
 func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext,
@@ -678,6 +979,12 @@
 	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")
 		return
@@ -711,7 +1018,13 @@
 }
 
 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
 	}
@@ -731,6 +1044,10 @@
 	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)
@@ -778,8 +1095,12 @@
 	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(ccMod *cc.Module, config android.Config, handleSpecialLibs bool) apexFile {
+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.
 	var dirInApex string
@@ -793,7 +1114,7 @@
 	if ccMod.Target().NativeBridge == android.NativeBridgeEnabled {
 		dirInApex = filepath.Join(dirInApex, ccMod.Target().NativeBridgeRelativePath)
 	}
-	if handleSpecialLibs && cc.InstallToBootstrap(ccMod.BaseModuleName(), config) {
+	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
@@ -808,26 +1129,26 @@
 	}
 
 	fileToCopy := ccMod.OutputFile().Path()
-	return newApexFile(fileToCopy, ccMod.Name(), dirInApex, nativeSharedLib, ccMod)
+	return newApexFile(ctx, fileToCopy, ccMod.Name(), dirInApex, nativeSharedLib, ccMod)
 }
 
-func apexFileForExecutable(cc *cc.Module) apexFile {
+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(fileToCopy, cc.Name(), dirInApex, nativeExecutable, cc)
+	af := newApexFile(ctx, fileToCopy, cc.Name(), dirInApex, nativeExecutable, cc)
 	af.symlinks = cc.Symlinks()
 	return af
 }
 
-func apexFileForPyBinary(py *python.Module) apexFile {
+func apexFileForPyBinary(ctx android.BaseModuleContext, py *python.Module) apexFile {
 	dirInApex := "bin"
 	fileToCopy := py.HostToolPath().Path()
-	return newApexFile(fileToCopy, py.Name(), dirInApex, pyBinary, py)
+	return newApexFile(ctx, fileToCopy, py.Name(), dirInApex, pyBinary, py)
 }
-func apexFileForGoBinary(ctx android.ModuleContext, depName string, gb bootstrap.GoBinaryTool) apexFile {
+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 {
@@ -838,45 +1159,42 @@
 	// 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(fileToCopy, depName, dirInApex, goBinary, nil)
+	return newApexFile(ctx, fileToCopy, depName, dirInApex, goBinary, nil)
 }
 
-func apexFileForShBinary(sh *android.ShBinary) apexFile {
+func apexFileForShBinary(ctx android.BaseModuleContext, sh *android.ShBinary) apexFile {
 	dirInApex := filepath.Join("bin", sh.SubDir())
 	fileToCopy := sh.OutputFile()
-	af := newApexFile(fileToCopy, sh.Name(), dirInApex, shBinary, sh)
+	af := newApexFile(ctx, fileToCopy, sh.Name(), dirInApex, shBinary, sh)
 	af.symlinks = sh.Symlinks()
 	return af
 }
 
-func apexFileForJavaLibrary(java *java.Library) apexFile {
-	dirInApex := "javalib"
-	fileToCopy := java.DexJarFile()
-	return newApexFile(fileToCopy, java.Name(), dirInApex, javaSharedLib, java)
+// TODO(b/146586360): replace javaLibrary(in apex/apex.go) with java.Dependency
+type javaLibrary interface {
+	android.Module
+	java.Dependency
 }
 
-func apexFileForPrebuiltJavaLibrary(java *java.Import) apexFile {
+func apexFileForJavaLibrary(ctx android.BaseModuleContext, lib javaLibrary) apexFile {
 	dirInApex := "javalib"
-	// The output is only one, but for some reason, ImplementationJars returns Paths, not Path
-	implJars := java.ImplementationJars()
-	if len(implJars) != 1 {
-		panic(fmt.Errorf("java.ImplementationJars() must return single Path, but got: %s",
-			strings.Join(implJars.Strings(), ", ")))
-	}
-	fileToCopy := implJars[0]
-	return newApexFile(fileToCopy, java.Name(), dirInApex, javaSharedLib, java)
+	fileToCopy := lib.DexJar()
+	af := newApexFile(ctx, fileToCopy, lib.Name(), dirInApex, javaSharedLib, lib)
+	af.jacocoReportClassesFile = lib.JacocoReportClassesFile()
+	return af
 }
 
-func apexFileForPrebuiltEtc(prebuilt android.PrebuiltEtcModule, depName string) apexFile {
+func apexFileForPrebuiltEtc(ctx android.BaseModuleContext, prebuilt android.PrebuiltEtcModule, depName string) apexFile {
 	dirInApex := filepath.Join("etc", prebuilt.SubDir())
 	fileToCopy := prebuilt.OutputFile()
-	return newApexFile(fileToCopy, depName, dirInApex, etc, prebuilt)
+	return newApexFile(ctx, fileToCopy, depName, dirInApex, etc, prebuilt)
 }
 
-func apexFileForAndroidApp(aapp interface {
+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() {
@@ -884,7 +1202,9 @@
 	}
 	dirInApex := filepath.Join(appDir, pkgName)
 	fileToCopy := aapp.OutputFile()
-	return newApexFile(fileToCopy, aapp.Name(), dirInApex, app, aapp)
+	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`.
@@ -905,6 +1225,10 @@
 		} else {
 			a.suffix = ""
 			a.primaryApexType = true
+
+			if ctx.Config().InstallExtraFlattenedApexes() {
+				a.requiredDeps = append(a.requiredDeps, a.Name()+flattenedSuffix)
+			}
 		}
 	case zipApex:
 		if proptools.String(a.properties.Payload_type) == "zip" {
@@ -962,25 +1286,28 @@
 		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 {
 					if cc.HasStubsVariants() {
 						provideNativeLibs = append(provideNativeLibs, cc.OutputFile().Path().Base())
 					}
-					filesInfo = append(filesInfo, apexFileForNativeLibrary(cc, ctx.Config(), handleSpecialLibs))
+					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 {
-					filesInfo = append(filesInfo, apexFileForExecutable(cc))
+					filesInfo = append(filesInfo, apexFileForExecutable(ctx, cc))
 					return true // track transitive dependencies
 				} else if sh, ok := child.(*android.ShBinary); ok {
-					filesInfo = append(filesInfo, apexFileForShBinary(sh))
+					filesInfo = append(filesInfo, apexFileForShBinary(ctx, sh))
 				} else if py, ok := child.(*python.Module); ok && py.HostToolPath().Valid() {
-					filesInfo = append(filesInfo, apexFileForPyBinary(py))
+					filesInfo = append(filesInfo, apexFileForPyBinary(ctx, py))
 				} else if gb, ok := child.(bootstrap.GoBinaryTool); ok && a.Host() {
 					filesInfo = append(filesInfo, apexFileForGoBinary(ctx, depName, gb))
 				} else {
@@ -988,36 +1315,46 @@
 				}
 			case javaLibTag:
 				if javaLib, ok := child.(*java.Library); ok {
-					af := apexFileForJavaLibrary(javaLib)
+					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, af)
 						return true // track transitive dependencies
 					}
-				} else if javaLib, ok := child.(*java.Import); ok {
-					af := apexFileForPrebuiltJavaLibrary(javaLib)
+				} else if sdkLib, ok := child.(*java.SdkLibrary); ok {
+					af := apexFileForJavaLibrary(ctx, sdkLib)
 					if !af.Ok() {
-						ctx.PropertyErrorf("java_libs", "%q does not have a jar output", depName)
-					} else {
-						filesInfo = append(filesInfo, af)
+						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 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(ap, pkgName))
+					filesInfo = append(filesInfo, apexFileForAndroidApp(ctx, ap, pkgName))
 					return true // track transitive dependencies
 				} else if ap, ok := child.(*java.AndroidAppImport); ok {
-					filesInfo = append(filesInfo, apexFileForAndroidApp(ap, pkgName))
+					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.PrebuiltEtcModule); ok {
-					filesInfo = append(filesInfo, apexFileForPrebuiltEtc(prebuilt, depName))
+					filesInfo = append(filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, depName))
 				} else {
 					ctx.PropertyErrorf("prebuilts", "%q is not a prebuilt_etc module", depName)
 				}
@@ -1033,7 +1370,7 @@
 						return true
 					} else {
 						// Single-output test module (where `test_per_src: false`).
-						af := apexFileForExecutable(ccTest)
+						af := apexFileForExecutable(ctx, ccTest)
 						af.class = nativeTest
 						filesInfo = append(filesInfo, af)
 					}
@@ -1074,7 +1411,7 @@
 							// don't include it in this APEX
 							return false
 						}
-						if !a.Host() && (cc.IsStubs() || cc.HasStubsVariants()) {
+						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
@@ -1082,33 +1419,39 @@
 							//
 							// 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 !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(cc, ctx.Config(), handleSpecialLibs)
+						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
 					}
 				} else if cc.IsTestPerSrcDepTag(depTag) {
 					if cc, ok := child.(*cc.Module); ok {
-						af := apexFileForExecutable(cc)
+						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
+						// these are not considered transitive dep
+						af.transitiveDep = false
 						filesInfo = append(filesInfo, af)
 						return true // track transitive dependencies
 					}
 				} else if java.IsJniDepTag(depTag) {
-					// Do nothing for JNI dep. JNI libraries are always embedded in APK-in-APEX.
+					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)
 				}
@@ -1125,7 +1468,7 @@
 			dirInApex := filepath.Join("javalib", arch.String())
 			for _, f := range files {
 				localModule := "javalib_" + arch.String() + "_" + filepath.Base(f.String())
-				af := newApexFile(f, localModule, dirInApex, etc, nil)
+				af := newApexFile(ctx, f, localModule, dirInApex, etc, nil)
 				filesInfo = append(filesInfo, af)
 			}
 		}
@@ -1138,15 +1481,22 @@
 
 	// remove duplicates in filesInfo
 	removeDup := func(filesInfo []apexFile) []apexFile {
-		encountered := make(map[string]bool)
-		result := []apexFile{}
+		encountered := make(map[string]apexFile)
 		for _, f := range filesInfo {
 			dest := filepath.Join(f.installDir, f.builtFile.Base())
-			if !encountered[dest] {
-				encountered[dest] = true
-				result = append(result, f)
+			if e, ok := encountered[dest]; !ok {
+				encountered[dest] = f
+			} else {
+				// If a module is directly included and also transitively depended on
+				// consider it as directly included.
+				e.transitiveDep = e.transitiveDep && f.transitiveDep
+				encountered[dest] = e
 			}
 		}
+		var result []apexFile
+		for _, v := range encountered {
+			result = append(result, v)
+		}
 		return result
 	}
 	filesInfo = removeDup(filesInfo)
@@ -1157,23 +1507,22 @@
 	})
 
 	// check apex_available requirements
-	if !ctx.Host() {
+	if !ctx.Host() && !a.testApex {
 		for _, fi := range filesInfo {
 			if am, ok := fi.module.(android.ApexModule); ok {
-				if !am.AvailableFor(ctx.ModuleName()) {
+				// vndk {enabled:true} implies visibility to the vndk apex
+				if ccm, ok := fi.module.(*cc.Module); ok && ccm.IsVndk() && a.vndkApex {
+					continue
+				}
+
+				if !am.AvailableFor(ctx.ModuleName()) && !whitelistedApexAvailable(ctx.ModuleName(), a.vndkApex, fi.module) {
 					ctx.ModuleErrorf("requires %q that is not available for the APEX", fi.module.Name())
-					return
+					// 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 = filesInfo[i].moduleName + "." + a.Name() + a.suffix
-	}
-
 	a.installDir = android.PathForModuleInstall(ctx, "apex")
 	a.filesInfo = filesInfo
 
@@ -1193,6 +1542,14 @@
 			return
 		}
 	}
+	// Optimization. If we are building bundled APEX, for the files that are gathered due to the
+	// transitive dependencies, don't place them inside the APEX, but place a symlink pointing
+	// the same library in the system partition, thus effectively sharing the same libraries
+	// across the APEX boundary. For unbundled APEX, all the gathered files are actually placed
+	// in the APEX.
+	a.linkToSystemLib = !ctx.Config().UnbundledBuild() &&
+		a.installable() &&
+		!proptools.Bool(a.properties.Use_vendor)
 
 	// prepare apex_manifest.json
 	a.buildManifest(ctx, provideNativeLibs, requireNativeLibs)
@@ -1204,8 +1561,28 @@
 		a.buildUnflattenedApex(ctx)
 	}
 
-	apexName := proptools.StringDefault(a.properties.Apex_name, a.Name())
-	a.compatSymlinks = makeCompatSymlinks(apexName, ctx)
+	a.compatSymlinks = makeCompatSymlinks(a.BaseModuleName(), ctx)
+
+	a.buildApexDependencyInfo(ctx)
+}
+
+func whitelistedApexAvailable(apex string, is_vndk bool, module android.Module) bool {
+	key := apex
+	key = strings.Replace(key, "test_", "", 1)
+	key = strings.Replace(key, "com.android.art.debug", "com.android.art", 1)
+	key = strings.Replace(key, "com.android.art.release", "com.android.art", 1)
+
+	moduleName := module.Name()
+	if strings.Contains(moduleName, "prebuilt_libclang_rt") {
+		// This module has variants that depend on the product being built.
+		moduleName = "prebuilt_libclang_rt"
+	}
+
+	if val, ok := apexAvailWl[key]; ok && android.InList(moduleName, val) {
+		return true
+	}
+
+	return false
 }
 
 func newApexBundle() *apexBundle {
@@ -1219,7 +1596,7 @@
 	android.InitAndroidMultiTargetsArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon)
 	android.InitDefaultableModule(module)
 	android.InitSdkAwareModule(module)
-	android.InitOverridableModule(module, &module.properties.Overrides)
+	android.InitOverridableModule(module, &module.overridableProperties.Overrides)
 	return module
 }
 
@@ -1259,6 +1636,7 @@
 	module.AddProperties(
 		&apexBundleProperties{},
 		&apexTargetBundleProperties{},
+		&overridableProperties{},
 	)
 
 	android.InitDefaultsModule(module)
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 509d760..1a9b95c 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -91,71 +91,12 @@
 	config.TestProductVariables.Binder32bit = proptools.BoolPtr(true)
 }
 
+func withUnbundledBuild(fs map[string][]byte, config android.Config) {
+	config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true)
+}
+
 func testApexContext(t *testing.T, bp string, handlers ...testCustomizer) (*android.TestContext, android.Config) {
-	config := android.TestArchConfig(buildDir, nil)
-	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)
-	config.TestProductVariables.Platform_vndk_version = proptools.StringPtr("VER")
-
-	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)
-
-	ctx.RegisterModuleType("cc_library", cc.LibraryFactory)
-	ctx.RegisterModuleType("cc_library_shared", cc.LibrarySharedFactory)
-	ctx.RegisterModuleType("cc_library_headers", cc.LibraryHeaderFactory)
-	ctx.RegisterModuleType("cc_prebuilt_library_shared", cc.PrebuiltSharedLibraryFactory)
-	ctx.RegisterModuleType("cc_prebuilt_library_static", cc.PrebuiltStaticLibraryFactory)
-	ctx.RegisterModuleType("cc_binary", cc.BinaryFactory)
-	ctx.RegisterModuleType("cc_object", cc.ObjectFactory)
-	ctx.RegisterModuleType("cc_defaults", func() android.Module {
-		return cc.DefaultsFactory()
-	})
-	ctx.RegisterModuleType("cc_test", cc.TestFactory)
-	ctx.RegisterModuleType("llndk_library", cc.LlndkLibraryFactory)
-	ctx.RegisterModuleType("vndk_prebuilt_shared", cc.VndkPrebuiltSharedFactory)
-	ctx.RegisterModuleType("vndk_libraries_txt", cc.VndkLibrariesTxtFactory)
-	ctx.RegisterModuleType("toolchain_library", cc.ToolchainLibraryFactory)
-	ctx.RegisterModuleType("prebuilt_etc", android.PrebuiltEtcFactory)
-	ctx.RegisterModuleType("sh_binary", android.ShBinaryFactory)
-	ctx.RegisterModuleType("android_app_certificate", java.AndroidAppCertificateFactory)
-	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
-	ctx.RegisterModuleType("java_library", java.LibraryFactory)
-	ctx.RegisterModuleType("java_import", java.ImportFactory)
-	ctx.RegisterModuleType("java_system_modules", java.SystemModulesFactory)
-	ctx.RegisterModuleType("android_app", java.AndroidAppFactory)
-	ctx.RegisterModuleType("android_app_import", java.AndroidAppImportFactory)
-	ctx.RegisterModuleType("override_android_app", java.OverrideAndroidAppModuleFactory)
-
-	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
-	ctx.PreArchMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.BottomUp("prebuilts", android.PrebuiltMutator).Parallel()
-	})
-	ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.BottomUp("image", android.ImageMutator).Parallel()
-		ctx.BottomUp("link", cc.LinkageMutator).Parallel()
-		ctx.BottomUp("vndk", cc.VndkMutator).Parallel()
-		ctx.BottomUp("test_per_src", cc.TestPerSrcMutator).Parallel()
-		ctx.BottomUp("version", cc.VersionMutator).Parallel()
-		ctx.BottomUp("begin", cc.BeginMutator).Parallel()
-	})
-	ctx.PreDepsMutators(RegisterPreDepsMutators)
-	ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators)
-	ctx.PostDepsMutators(RegisterPostDepsMutators)
-	ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.TopDown("prebuilt_select", android.PrebuiltSelectModuleMutator).Parallel()
-		ctx.BottomUp("prebuilt_postdeps", android.PrebuiltPostDepsMutator).Parallel()
-	})
-
-	ctx.Register()
+	android.ClearApexDependency()
 
 	bp = bp + `
 		toolchain_library {
@@ -271,10 +212,10 @@
 			],
 		}
 	`
+
 	bp = bp + java.GatherRequiredDepsForTest()
 
 	fs := map[string][]byte{
-		"Android.bp":                                          []byte(bp),
 		"a.java":                                              nil,
 		"PrebuiltAppFoo.apk":                                  nil,
 		"PrebuiltAppFooPriv.apk":                              nil,
@@ -282,45 +223,92 @@
 		"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,
+		"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,
 	}
 
 	for _, handler := range handlers {
-		handler(fs, config)
+		// 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)
 	}
 
-	ctx.MockFileSystem(fs)
+	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)
+	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_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
 }
@@ -388,7 +376,7 @@
 					binaries: ["foo",],
 				}
 			},
-			java_libs: ["myjar", "myprebuiltjar"],
+			java_libs: ["myjar"],
 		}
 
 		apex {
@@ -418,6 +406,11 @@
 			shared_libs: ["mylib2"],
 			system_shared_libs: [],
 			stl: "none",
+			// TODO: remove //apex_available:platform
+			apex_available: [
+				"//apex_available:platform",
+				"myapex",
+			],
 		}
 
 		cc_binary {
@@ -437,6 +430,7 @@
 			system_shared_libs: [],
 			static_executable: true,
 			stl: "none",
+			apex_available: [ "myapex" ],
 		}
 
 		cc_library {
@@ -445,6 +439,11 @@
 			system_shared_libs: [],
 			stl: "none",
 			notice: "custom_notice",
+			// TODO: remove //apex_available:platform
+			apex_available: [
+				"//apex_available:platform",
+				"myapex",
+			],
 		}
 
 		java_library {
@@ -454,6 +453,12 @@
 			system_modules: "none",
 			compile_dex: true,
 			static_libs: ["myotherjar"],
+			libs: ["mysharedjar"],
+			// TODO: remove //apex_available:platform
+			apex_available: [
+				"//apex_available:platform",
+				"myapex",
+			],
 		}
 
 		java_library {
@@ -464,10 +469,12 @@
 			compile_dex: true,
 		}
 
-		java_import {
-			name: "myprebuiltjar",
-			jars: ["prebuilt.jar"],
-			installable: true,
+		java_library {
+			name: "mysharedjar",
+			srcs: ["foo/bar/MyClass.java"],
+			sdk_version: "none",
+			system_modules: "none",
+			compile_dex: true,
 		}
 	`)
 
@@ -484,34 +491,36 @@
 	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")
-	ensureListContains(t, ctx.ModuleVariantsForTests("myprebuiltjar"), "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")
-	ensureContains(t, copyCmds, "image.apex/javalib/myprebuiltjar.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 or _common
-	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("myprebuiltjar"), "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
 	found_foo := false
 	for _, cmd := range strings.Split(copyCmds, " && ") {
-		if strings.HasPrefix(cmd, "ln -s foo64") {
+		if strings.HasPrefix(cmd, "ln -sfn foo64") {
 			if strings.HasSuffix(cmd, "bin/foo") {
 				found_foo = true
 			} else if strings.HasSuffix(cmd, "bin/foo_link_64") {
@@ -531,6 +540,71 @@
 	}
 	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",
+			apex_available: [ "myapex" ],
+		}
+
+		java_library {
+			name: "myjar",
+			srcs: ["foo/bar/MyClass.java"],
+			sdk_version: "none",
+			system_modules: "none",
+			compile_dex: true,
+			apex_available: [ "myapex" ],
+		}
+
+		android_app {
+			name: "AppFoo",
+			srcs: ["foo/bar/MyClass.java"],
+			sdk_version: "none",
+			system_modules: "none",
+			apex_available: [ "myapex" ],
+		}
+	`)
+	ensureExactContents(t, ctx, "myapex", []string{
+		"etc/myetc",
+		"javalib/myjar.jar",
+		"lib64/mylib.so",
+		"app/AppFoo/AppFoo.apk",
+	})
 }
 
 func TestApexManifest(t *testing.T) {
@@ -548,9 +622,10 @@
 	`)
 
 	module := ctx.ModuleForTests("myapex", "android_common_myapex_image")
-	module.Output("apex_manifest.pb")
-	module.Output("apex_manifest.json")
-	module.Output("apex_manifest_full.json")
+	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) {
@@ -574,6 +649,7 @@
 			shared_libs: ["mylib2"],
 			system_shared_libs: [],
 			stl: "none",
+			apex_available: [ "myapex" ],
 		}
 
 		cc_library {
@@ -581,6 +657,7 @@
 			srcs: ["mylib.cpp"],
 			system_shared_libs: [],
 			stl: "none",
+			apex_available: [ "myapex" ],
 		}
 	`)
 
@@ -591,10 +668,10 @@
 	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")
@@ -621,6 +698,7 @@
 			shared_libs: ["mylib2", "mylib3"],
 			system_shared_libs: [],
 			stl: "none",
+			apex_available: [ "myapex" ],
 		}
 
 		cc_library {
@@ -643,6 +721,7 @@
 			stubs: {
 				versions: ["10", "11", "12"],
 			},
+			apex_available: [ "myapex" ],
 		}
 
 		cc_library {
@@ -650,6 +729,7 @@
 			srcs: ["mylib.cpp"],
 			system_shared_libs: [],
 			stl: "none",
+			apex_available: [ "myapex" ],
 		}
 	`)
 
@@ -665,36 +745,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, `
 		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",
 		}
@@ -705,6 +791,7 @@
 			shared_libs: ["libfoo#10"],
 			system_shared_libs: [],
 			stl: "none",
+			apex_available: [ "myapex2" ],
 		}
 
 		cc_library {
@@ -727,7 +814,7 @@
 
 	`)
 
-	apexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").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
@@ -739,17 +826,23 @@
 	// 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) {
@@ -780,6 +873,7 @@
 			runtime_libs: ["libfoo", "libbar"],
 			system_shared_libs: [],
 			stl: "none",
+			apex_available: [ "myapex" ],
 		}
 
 		cc_library {
@@ -797,6 +891,7 @@
 			srcs: ["mylib.cpp"],
 			system_shared_libs: [],
 			stl: "none",
+			apex_available: [ "myapex" ],
 		}
 
 	`)
@@ -841,6 +936,7 @@
 			shared_libs: ["libbar"],
 			system_shared_libs: [],
 			stl: "none",
+			apex_available: [ "myapex" ],
 		}
 
 		cc_library {
@@ -891,6 +987,7 @@
 			srcs: ["mylib.cpp"],
 			shared_libs: ["libdl#27"],
 			stl: "none",
+			apex_available: [ "myapex" ],
 		}
 
 		cc_library_shared {
@@ -898,6 +995,7 @@
 			srcs: ["mylib.cpp"],
 			shared_libs: ["libdl#27"],
 			stl: "none",
+			apex_available: [ "myapex" ],
 		}
 
 		cc_library {
@@ -920,6 +1018,10 @@
 			stubs: {
 				versions: ["27", "28", "29"],
 			},
+			apex_available: [
+				"//apex_available:platform",
+				"myapex"
+			],
 		}
 
 		cc_library {
@@ -931,6 +1033,10 @@
 			stubs: {
 				versions: ["27", "28", "29"],
 			},
+			apex_available: [
+				"//apex_available:platform",
+				"myapex"
+			],
 		}
 
 		cc_library {
@@ -952,45 +1058,45 @@
 	// 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) {
@@ -1022,6 +1128,7 @@
 			relative_install_path: "foo/bar",
 			system_shared_libs: [],
 			stl: "none",
+			apex_available: [ "myapex" ],
 		}
 
 		cc_binary {
@@ -1031,6 +1138,7 @@
 			system_shared_libs: [],
 			static_executable: true,
 			stl: "none",
+			apex_available: [ "myapex" ],
 		}
 	`)
 
@@ -1075,6 +1183,7 @@
 			system_shared_libs: [],
 			vendor_available: true,
 			stl: "none",
+			apex_available: [ "myapex" ],
 		}
 
 		cc_library {
@@ -1083,6 +1192,7 @@
 			system_shared_libs: [],
 			vendor_available: true,
 			stl: "none",
+			apex_available: [ "myapex" ],
 		}
 	`, func(fs map[string][]byte, config android.Config) {
 		setUseVendorWhitelistForTest(config, []string{"myapex"})
@@ -1097,12 +1207,12 @@
 	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.VER_shared_myapex/mylib.so")
-	ensureContains(t, inputsString, "android_arm64_armv8-a_vendor.VER_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) {
@@ -1183,6 +1293,10 @@
 			stubs: {
 				versions: ["1", "2", "3"],
 			},
+			apex_available: [
+				"//apex_available:platform",
+				"myapex",
+			],
 		}
 
 		cc_binary {
@@ -1195,10 +1309,10 @@
 		}
 	`)
 
-	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) {
@@ -1216,6 +1330,7 @@
 			srcs: ["mylib.cpp"],
 			system_shared_libs: [],
 			stl: "none",
+			apex_available: [ "myapex_keytest" ],
 		}
 
 		apex_key {
@@ -1256,6 +1371,134 @@
 	}
 }
 
+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, `
 		apex {
@@ -1281,23 +1524,29 @@
 			srcs: ["mylib.cpp"],
 			system_shared_libs: [],
 			stl: "none",
+			// TODO: remove //apex_available:platform
+			apex_available: [
+				"//apex_available:platform",
+				"myapex",
+				"otherapex",
+			],
 		}
 	`)
 
 	// non-APEX variant does not have __ANDROID_APEX(_NAME)__ defined
-	mylibCFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_core_static").Rule("cc").Args["cFlags"]
+	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(_NAME)__ defined
-	mylibCFlags = ctx.ModuleForTests("mylib", "android_arm64_armv8-a_core_static_myapex").Rule("cc").Args["cFlags"]
+	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(_NAME)__ defined
-	mylibCFlags = ctx.ModuleForTests("mylib", "android_arm64_armv8-a_core_static_otherapex").Rule("cc").Args["cFlags"]
+	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__")
@@ -1334,6 +1583,7 @@
 			stubs: {
 				versions: ["1", "2", "3"],
 			},
+			apex_available: [ "myapex" ],
 		}
 
 		cc_library {
@@ -1345,52 +1595,74 @@
 		}
 	`)
 
-	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) {
+type fileInApex struct {
+	path   string // path in apex
+	isLink bool
+}
+
+func getFiles(t *testing.T, ctx *android.TestContext, moduleName string) []fileInApex {
 	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)
-	}
+	var ret []fileInApex
 	for _, cmd := range strings.Split(copyCmds, "&&") {
 		cmd = strings.TrimSpace(cmd)
 		if cmd == "" {
 			continue
 		}
 		terms := strings.Split(cmd, " ")
+		var dst string
+		var isLink bool
 		switch terms[0] {
 		case "mkdir":
 		case "cp":
-			if len(terms) != 3 {
+			if len(terms) != 3 && len(terms) != 4 {
 				t.Fatal("copyCmds contains invalid cp command", cmd)
 			}
-			dst := terms[2]
+			dst = terms[len(terms)-1]
+			isLink = false
+		case "ln":
+			if len(terms) != 3 && len(terms) != 4 {
+				// ln LINK TARGET or ln -s LINK TARGET
+				t.Fatal("copyCmds contains invalid ln command", cmd)
+			}
+			dst = terms[len(terms)-1]
+			isLink = true
+		default:
+			t.Fatalf("copyCmds should contain mkdir/cp commands only: %q", cmd)
+		}
+		if dst != "" {
 			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)
+			ret = append(ret, fileInApex{path: dstFile, isLink: isLink})
 		}
 	}
+	return ret
+}
+
+func ensureExactContents(t *testing.T, ctx *android.TestContext, moduleName string, files []string) {
+	var failed bool
+	var surplus []string
+	filesMatched := make(map[string]bool)
+	for _, file := range getFiles(t, ctx, moduleName) {
+		for _, expected := range files {
+			if matched, _ := path.Match(expected, file.path); matched {
+				filesMatched[expected] = true
+				return
+			}
+		}
+		surplus = append(surplus, file.path)
+	}
 
 	if len(surplus) > 0 {
 		sort.Strings(surplus)
@@ -1436,6 +1708,7 @@
 			},
 			system_shared_libs: [],
 			stl: "none",
+			apex_available: [ "myapex" ],
 		}
 
 		cc_library {
@@ -1448,6 +1721,7 @@
 			},
 			system_shared_libs: [],
 			stl: "none",
+			apex_available: [ "myapex" ],
 		}
 	`+vndkLibrariesTxtFiles("current"))
 
@@ -1460,7 +1734,6 @@
 		"etc/vndkcore.libraries.VER.txt",
 		"etc/vndksp.libraries.VER.txt",
 		"etc/vndkprivate.libraries.VER.txt",
-		"etc/vndkcorevariant.libraries.VER.txt",
 	})
 }
 
@@ -1486,6 +1759,7 @@
 			},
 			system_shared_libs: [],
 			stl: "none",
+			apex_available: [ "myapex" ],
 		}
 
 		cc_prebuilt_library_shared {
@@ -1503,6 +1777,7 @@
 			},
 			system_shared_libs: [],
 			stl: "none",
+			apex_available: [ "myapex" ],
 		}
 		`+vndkLibrariesTxtFiles("current"),
 		withFiles(map[string][]byte{
@@ -1521,7 +1796,7 @@
 func vndkLibrariesTxtFiles(vers ...string) (result string) {
 	for _, v := range vers {
 		if v == "current" {
-			for _, txt := range []string{"llndk", "vndkcore", "vndksp", "vndkprivate", "vndkcorevariant"} {
+			for _, txt := range []string{"llndk", "vndkcore", "vndksp", "vndkprivate"} {
 				result += `
 					vndk_libraries_txt {
 						name: "` + txt + `.libraries.txt",
@@ -1573,6 +1848,7 @@
 					srcs: ["libvndk27_arm64.so"],
 				},
 			},
+			apex_available: [ "myapex_v27" ],
 		}
 
 		vndk_prebuilt_shared {
@@ -1709,6 +1985,7 @@
 			},
 			system_shared_libs: [],
 			stl: "none",
+			apex_available: [ "myapex" ],
 		}
 		`+vndkLibrariesTxtFiles("current"),
 		withTargets(map[android.OsType][]android.Target{
@@ -1801,6 +2078,7 @@
 					srcs: ["libvndk27binder32.so"],
 				}
 			},
+			apex_available: [ "myapex_v27" ],
 		}
 		`+vndkLibrariesTxtFiles("27"),
 		withFiles(map[string][]byte{
@@ -1866,6 +2144,7 @@
 			srcs: ["mylib.cpp"],
 			system_shared_libs: [],
 			stl: "none",
+			apex_available: [ "myapex_nodep" ],
 		}
 
 		cc_library {
@@ -1874,6 +2153,11 @@
 			shared_libs: ["libfoo"],
 			system_shared_libs: [],
 			stl: "none",
+			apex_available: [
+				"myapex_dep",
+				"myapex_provider",
+				"myapex_selfcontained",
+			],
 		}
 
 		cc_library {
@@ -1884,6 +2168,10 @@
 			},
 			system_shared_libs: [],
 			stl: "none",
+			apex_available: [
+				"myapex_provider",
+				"myapex_selfcontained",
+			],
 		}
 	`)
 
@@ -1956,6 +2244,10 @@
 			srcs: ["mylib.cpp"],
 			system_shared_libs: [],
 			stl: "none",
+			apex_available: [
+					"//apex_available:platform",
+				  "myapex",
+		  ],
 		}
 	`)
 
@@ -1971,13 +2263,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!")
@@ -2007,6 +2299,11 @@
 			srcs: ["mylib.cpp"],
 			system_shared_libs: [],
 			stl: "none",
+			// TODO: remove //apex_available:platform
+			apex_available: [
+				"//apex_available:platform",
+				"myapex",
+			],
 		}
 	`)
 
@@ -2022,13 +2319,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!")
@@ -2075,6 +2372,11 @@
 			srcs: ["mylib.cpp"],
 			system_shared_libs: [],
 			stl: "none",
+			// TODO: remove //apex_available:platform
+			apex_available: [
+				"//apex_available:platform",
+				"myapex",
+			],
 		}
 
 		cc_library {
@@ -2083,6 +2385,11 @@
 			system_shared_libs: [],
 			stl: "none",
 			compile_multilib: "first",
+			// TODO: remove //apex_available:platform
+			apex_available: [
+				"//apex_available:platform",
+				"myapex",
+			],
 		}
 
 		cc_library {
@@ -2101,19 +2408,19 @@
 	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) {
@@ -2381,7 +2688,7 @@
 	p := ctx.ModuleForTests("myapex.prebuilt", "android_common").Module().(*Prebuilt)
 
 	expected := []string{"myapex"}
-	actual := android.AndroidMkEntriesForTest(t, config, "", p).EntryMap["LOCAL_OVERRIDES_MODULES"]
+	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)
 	}
@@ -2453,11 +2760,34 @@
 	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.json.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 {
@@ -2486,6 +2816,7 @@
 			shared_libs: ["libcommon"],
 			system_shared_libs: [],
 			stl: "none",
+			apex_available: [ "myapex" ],
 		}
 
 		cc_library {
@@ -2493,6 +2824,12 @@
 			srcs: ["mylib_common.cpp"],
 			system_shared_libs: [],
 			stl: "none",
+			// TODO: remove //apex_available:platform
+			apex_available: [
+				"//apex_available:platform",
+				"commonapex",
+				"myapex",
+			],
 		}
 	`)
 
@@ -2504,8 +2841,8 @@
 	apexRule2 := module2.Rule("apexRule")
 	copyCmds2 := apexRule2.Args["copy_commands"]
 
-	ensureListContains(t, ctx.ModuleVariantsForTests("mylib"), "android_arm64_armv8-a_core_shared_myapex")
-	ensureListContains(t, ctx.ModuleVariantsForTests("libcommon"), "android_arm64_armv8-a_core_shared_commonapex")
+	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")
@@ -2644,6 +2981,7 @@
 			sdk_version: "none",
 			system_modules: "none",
 			jni_libs: ["libjni"],
+			apex_available: [ "myapex" ],
 		}
 
 		android_app {
@@ -2652,6 +2990,7 @@
 			sdk_version: "none",
 			system_modules: "none",
 			privileged: true,
+			apex_available: [ "myapex" ],
 		}
 
 		cc_library_shared {
@@ -2671,7 +3010,7 @@
 
 	// JNI libraries are embedded inside APK
 	appZipRule := ctx.ModuleForTests("AppFoo", "android_common_myapex").Rule("zip")
-	libjniOutput := ctx.ModuleForTests("libjni", "android_arm64_armv8-a_core_shared_myapex").Module().(*cc.Module).OutputFile()
+	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") {
@@ -2726,6 +3065,37 @@
 	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"],
+			apex_available: [ "myapex" ],
+		}
+
+	`)
+
+	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`, `
@@ -2883,10 +3253,10 @@
 	}`)
 
 	// check that libfoo and libbar are created only for myapex, but not for the platform
-	ensureListContains(t, ctx.ModuleVariantsForTests("libfoo"), "android_arm64_armv8-a_core_shared_myapex")
-	ensureListNotContains(t, ctx.ModuleVariantsForTests("libfoo"), "android_arm64_armv8-a_core_shared")
-	ensureListContains(t, ctx.ModuleVariantsForTests("libbar"), "android_arm64_armv8-a_core_shared_myapex")
-	ensureListNotContains(t, ctx.ModuleVariantsForTests("libbar"), "android_arm64_armv8-a_core_shared")
+	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 {
@@ -2908,8 +3278,8 @@
 	}`)
 
 	// check that libfoo is created only for the platform
-	ensureListNotContains(t, ctx.ModuleVariantsForTests("libfoo"), "android_arm64_armv8-a_core_shared_myapex")
-	ensureListContains(t, ctx.ModuleVariantsForTests("libfoo"), "android_arm64_armv8-a_core_shared")
+	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 {
@@ -2935,11 +3305,11 @@
 	}`)
 
 	// shared variant of libfoo is only available to myapex
-	ensureListContains(t, ctx.ModuleVariantsForTests("libfoo"), "android_arm64_armv8-a_core_shared_myapex")
-	ensureListNotContains(t, ctx.ModuleVariantsForTests("libfoo"), "android_arm64_armv8-a_core_shared")
+	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_core_static_myapex")
-	ensureListContains(t, ctx.ModuleVariantsForTests("libfoo"), "android_arm64_armv8-a_core_static")
+	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) {
@@ -2948,12 +3318,14 @@
 			name: "myapex",
 			key: "myapex.key",
 			apps: ["app"],
+			overrides: ["oldapex"],
 		}
 
 		override_apex {
 			name: "override_myapex",
 			base: "myapex",
 			apps: ["override_app"],
+			overrides: ["unknownapex"],
 		}
 
 		apex_key {
@@ -2968,6 +3340,7 @@
 			package_name: "foo",
 			sdk_version: "none",
 			system_modules: "none",
+			apex_available: [ "myapex" ],
 		}
 
 		override_android_app {
@@ -2977,6 +3350,15 @@
 		}
 	`)
 
+	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"]
@@ -2997,12 +3379,234 @@
 	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())
+	ensureNotContains(t, args["opt_flags"], "--no_hashtree")
+}
+
+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"],
+			apex_available: [ "myapex" ],
+		}
+	`, 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"],
+			apex_available: [ "myapex" ],
+		}
+	`)
+
+	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 TestSymlinksFromApexToSystem(t *testing.T) {
+	bp := `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			native_shared_libs: ["mylib"],
+			java_libs: ["myjar"],
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "mylib",
+			srcs: ["mylib.cpp"],
+			shared_libs: ["myotherlib"],
+			system_shared_libs: [],
+			stl: "none",
+			apex_available: [
+				"myapex",
+				"//apex_available:platform",
+			],
+		}
+
+		cc_library {
+			name: "myotherlib",
+			srcs: ["mylib.cpp"],
+			system_shared_libs: [],
+			stl: "none",
+			apex_available: [
+				"myapex",
+				"//apex_available:platform",
+			],
+		}
+
+		java_library {
+			name: "myjar",
+			srcs: ["foo/bar/MyClass.java"],
+			sdk_version: "none",
+			system_modules: "none",
+			libs: ["myotherjar"],
+			compile_dex: true,
+			apex_available: [
+				"myapex",
+				"//apex_available:platform",
+			],
+		}
+
+		java_library {
+			name: "myotherjar",
+			srcs: ["foo/bar/MyClass.java"],
+			sdk_version: "none",
+			system_modules: "none",
+			apex_available: [
+				"myapex",
+				"//apex_available:platform",
+			],
+		}
+	`
+
+	ensureRealfileExists := func(t *testing.T, files []fileInApex, file string) {
+		for _, f := range files {
+			if f.path == file {
+				if f.isLink {
+					t.Errorf("%q is not a real file", file)
+				}
+				return
+			}
+		}
+		t.Errorf("%q is not found", file)
+	}
+
+	ensureSymlinkExists := func(t *testing.T, files []fileInApex, file string) {
+		for _, f := range files {
+			if f.path == file {
+				if !f.isLink {
+					t.Errorf("%q is not a symlink", file)
+				}
+				return
+			}
+		}
+		t.Errorf("%q is not found", file)
+	}
+
+	ctx, _ := testApex(t, bp, withUnbundledBuild)
+	files := getFiles(t, ctx, "myapex")
+	ensureRealfileExists(t, files, "javalib/myjar.jar")
+	ensureRealfileExists(t, files, "lib64/mylib.so")
+	ensureRealfileExists(t, files, "lib64/myotherlib.so")
+
+	ctx, _ = testApex(t, bp)
+	files = getFiles(t, ctx, "myapex")
+	ensureRealfileExists(t, files, "javalib/myjar.jar")
+	ensureRealfileExists(t, files, "lib64/mylib.so")
+	ensureSymlinkExists(t, files, "lib64/myotherlib.so") // this is symlink
+}
+
 func TestMain(m *testing.M) {
 	run := func() int {
 		setUp()
diff --git a/apex/builder.go b/apex/builder.go
index f199bd4..8209f69 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -43,7 +43,7 @@
 			if !ctx.Config().FrameworksBaseDirExists(ctx) {
 				return filepath.Join(prebuiltDir, runtime.GOOS, "bin", tool)
 			} else {
-				return pctx.HostBinToolPath(ctx, tool).String()
+				return ctx.Config().HostToolPath(ctx, tool).String()
 			}
 		})
 	}
@@ -103,9 +103,9 @@
 			`(. ${out}.copy_commands) && ` +
 			`APEXER_TOOL_PATH=${tool_path} ` +
 			`${apexer} --force --manifest ${manifest} ` +
-			`--manifest_json ${manifest_json} --manifest_json_full ${manifest_json_full} ` +
 			`--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}",
@@ -114,22 +114,20 @@
 		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", "manifest_json", "manifest_json_full",
-	)
+	}, "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} --manifest_json_full ${manifest_json_full} ` +
+			`${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", "manifest_json_full")
+	}, "tool_path", "image_dir", "copy_commands", "manifest")
 
 	apexProtoConvertRule = pctx.AndroidStaticRule("apexProtoConvertRule",
 		blueprint.RuleParams{
@@ -141,6 +139,7 @@
 		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}"},
@@ -169,7 +168,7 @@
 func (a *apexBundle) buildManifest(ctx android.ModuleContext, provideNativeLibs, requireNativeLibs []string) {
 	manifestSrc := android.PathForModuleSrc(ctx, proptools.StringDefault(a.properties.Manifest, "apex_manifest.json"))
 
-	a.manifestJsonFullOut = android.PathForModuleOut(ctx, "apex_manifest_full.json")
+	manifestJsonFullOut := android.PathForModuleOut(ctx, "apex_manifest_full.json")
 
 	// put dependency({provide|require}NativeLibs) in apex_manifest.json
 	provideNativeLibs = android.SortedUniqueStrings(provideNativeLibs)
@@ -184,7 +183,7 @@
 	ctx.Build(pctx, android.BuildParams{
 		Rule:   apexManifestRule,
 		Input:  manifestSrc,
-		Output: a.manifestJsonFullOut,
+		Output: manifestJsonFullOut,
 		Args: map[string]string{
 			"provideNativeLibs": strings.Join(provideNativeLibs, " "),
 			"requireNativeLibs": strings.Join(requireNativeLibs, " "),
@@ -192,20 +191,22 @@
 		},
 	})
 
-	// 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:  a.manifestJsonFullOut,
-		Output: a.manifestJsonOut,
-	})
+	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:  a.manifestJsonFullOut,
+		Input:  manifestJsonFullOut,
 		Output: a.manifestPbOut,
 	})
 }
@@ -232,6 +233,19 @@
 	return android.BuildNoticeOutput(ctx, a.installDir, apexFileName, android.FirstUniquePaths(noticeFiles)).HtmlGzOutput
 }
 
+func (a *apexBundle) buildInstalledFilesFile(ctx android.ModuleContext, builtApex android.Path, imageDir android.Path) android.OutputPath {
+	output := android.PathForModuleOut(ctx, "installed-files.txt")
+	rule := android.NewRuleBuilder()
+	rule.Command().
+		Implicit(builtApex).
+		Text("(cd " + imageDir.String() + " ; ").
+		Text("find . -type f -printf \"%s %p\\n\") ").
+		Text(" | sort -nr > ").
+		Output(output)
+	rule.Build(pctx, ctx, "installed-files."+a.Name(), "Installed files")
+	return output.OutputPath
+}
+
 func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext) {
 	var abis []string
 	for _, target := range ctx.MultiTargets() {
@@ -244,32 +258,41 @@
 
 	apexType := a.properties.ApexType
 	suffix := apexType.suffix()
+	var implicitInputs []android.Path
 	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.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)
+	// TODO(jiyong): construct the copy rules using RuleBuilder
+	var copyCommands []string
+	for _, fi := range a.filesInfo {
+		destPath := android.PathForModuleOut(ctx, "image"+suffix, fi.Path()).String()
+		copyCommands = append(copyCommands, "mkdir -p "+filepath.Dir(destPath))
+		if a.linkToSystemLib && fi.transitiveDep && fi.AvailableToPlatform() {
+			// TODO(jiyong): pathOnDevice should come from fi.module, not being calculated here
+			pathOnDevice := filepath.Join("/system", fi.Path())
+			copyCommands = append(copyCommands, "ln -sfn "+pathOnDevice+" "+destPath)
+		} else {
+			copyCommands = append(copyCommands, "cp -f "+fi.builtFile.String()+" "+destPath)
+			implicitInputs = append(implicitInputs, fi.builtFile)
+		}
+		// create additional symlinks pointing the file inside the APEX
+		for _, symlinkPath := range fi.SymlinkPaths() {
+			symlinkDest := android.PathForModuleOut(ctx, "image"+suffix, symlinkPath).String()
+			copyCommands = append(copyCommands, "ln -sfn "+filepath.Base(destPath)+" "+symlinkDest)
 		}
 	}
-	emitCommands = append(emitCommands, "sort -o "+imageContentFile.String()+" "+imageContentFile.String())
 
-	implicitInputs := append(android.Paths(nil), filesToCopy...)
-	implicitInputs = append(implicitInputs, a.manifestPbOut, a.manifestJsonFullOut, a.manifestJsonOut)
+	// TODO(jiyong): use RuleBuilder
+	var emitCommands []string
+	imageContentFile := android.PathForModuleOut(ctx, "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 _, fi := range a.filesInfo {
+		emitCommands = append(emitCommands, "echo './"+fi.Path()+"' >> "+imageContentFile.String())
+	}
+	emitCommands = append(emitCommands, "sort -o "+imageContentFile.String()+" "+imageContentFile.String())
+	implicitInputs = append(implicitInputs, a.manifestPbOut)
 
 	if a.properties.Whitelisted_files != nil {
 		ctx.Build(pctx, android.BuildParams{
@@ -303,6 +326,7 @@
 	outHostBinDir := android.PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "bin").String()
 	prebuiltSdkToolsBinDir := filepath.Join("prebuilts", "sdk", "tools", runtime.GOOS, "bin")
 
+	imageDir := android.PathForModuleOut(ctx, "image"+suffix)
 	if apexType == imageApex {
 		// files and dirs that will be created in APEX
 		var readOnlyPaths = []string{"apex_manifest.json", "apex_manifest.pb"}
@@ -346,8 +370,8 @@
 		implicitInputs = append(implicitInputs, cannedFsConfig, a.fileContexts, a.private_key_file, a.public_key_file)
 		optFlags = append(optFlags, "--pubkey "+a.public_key_file.String())
 
-		manifestPackageName, overridden := ctx.DeviceConfig().OverrideManifestPackageNameFor(a.Name())
-		if overridden {
+		manifestPackageName := a.getOverrideManifestPackageName(ctx)
+		if manifestPackageName != "" {
 			optFlags = append(optFlags, "--override_apk_package_name "+manifestPackageName)
 		}
 
@@ -375,7 +399,11 @@
 			optFlags = append(optFlags, "--assets_dir "+filepath.Dir(noticeFile.String()))
 		}
 
-		if !ctx.Config().UnbundledBuild() && a.installable() {
+		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 !proptools.Bool(a.properties.Legacy_android10_support) || 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.
@@ -388,22 +416,25 @@
 			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_json_full": a.manifestJsonFullOut.String(),
-				"manifest_json":      a.manifestJsonOut.String(),
-				"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, " "),
+				"tool_path":        outHostBinDir + ":" + prebuiltSdkToolsBinDir,
+				"image_dir":        imageDir.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, " "),
 			},
 		})
 
@@ -434,11 +465,10 @@
 			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(),
-				"manifest_json_full": a.manifestJsonFullOut.String(),
+				"tool_path":     outHostBinDir + ":" + prebuiltSdkToolsBinDir,
+				"image_dir":     imageDir.String(),
+				"copy_commands": strings.Join(copyCommands, " && "),
+				"manifest":      a.manifestPbOut.String(),
 			},
 		})
 	}
@@ -464,6 +494,9 @@
 		ctx.InstallFile(a.installDir, a.Name()+suffix, a.outputFile)
 	}
 	a.buildFilesInfo(ctx)
+
+	// installed-files.txt is dist'ed
+	a.installedFilesFile = a.buildInstalledFilesFile(ctx, a.outputFile, imageDir)
 }
 
 func (a *apexBundle) buildFlattenedApex(ctx android.ModuleContext) {
@@ -476,7 +509,7 @@
 	apexName := proptools.StringDefault(a.properties.Apex_name, ctx.ModuleName())
 	a.outputFile = android.PathForModuleInstall(&factx, "apex", apexName)
 
-	if a.installable() {
+	if a.installable() && a.GetOverriddenBy() == "" {
 		installPath := android.PathForModuleInstall(ctx, "apex", apexName)
 		devicePath := android.InstallPathToOnDevicePath(ctx, installPath)
 		addFlattenedFileContextsInfos(ctx, apexName+":"+devicePath+":"+a.fileContexts.String())
@@ -485,24 +518,25 @@
 }
 
 func (a *apexBundle) setCertificateAndPrivateKey(ctx android.ModuleContext) {
-	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
+	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.json and apex_pubkey are also copied along
+		// 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(a.manifestJsonOut, "apex_manifest.json."+a.Name()+a.suffix, ".", etc, nil))
-		a.filesInfo = append(a.filesInfo, newApexFile(a.manifestPbOut, "apex_manifest.pb."+a.Name()+a.suffix, ".", etc, nil))
+		a.filesInfo = append(a.filesInfo, newApexFile(ctx, a.manifestPbOut, "apex_manifest.pb", ".", etc, nil))
 
 		// rename to apex_pubkey
 		copiedPubkey := android.PathForModuleOut(ctx, "apex_pubkey")
@@ -511,7 +545,7 @@
 			Input:  a.public_key_file,
 			Output: copiedPubkey,
 		})
-		a.filesInfo = append(a.filesInfo, newApexFile(copiedPubkey, "apex_pubkey."+a.Name()+a.suffix, ".", etc, nil))
+		a.filesInfo = append(a.filesInfo, newApexFile(ctx, copiedPubkey, "apex_pubkey", ".", etc, nil))
 
 		if a.properties.ApexType == flattenedApex {
 			apexName := proptools.StringDefault(a.properties.Apex_name, a.Name())
@@ -525,3 +559,69 @@
 		}
 	}
 }
+
+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/prebuilt.go b/apex/prebuilt.go
index db3b5ef..d089c28 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -33,6 +33,10 @@
 	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 {
@@ -178,11 +182,16 @@
 		ctx.InstallFile(p.installDir, p.installFilename, p.inputApex)
 	}
 
-	// TODO(b/143192278): Add compat symlinks for prebuilt_apex
+	// 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{
+func (p *Prebuilt) AndroidMkEntries() []android.AndroidMkEntries {
+	return []android.AndroidMkEntries{android.AndroidMkEntries{
 		Class:      "ETC",
 		OutputFile: android.OptionalPathForPath(p.inputApex),
 		Include:    "$(BUILD_PREBUILT)",
@@ -192,7 +201,10 @@
 				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
index 15f7f87..f2e913e 100644
--- a/apex/vndk.go
+++ b/apex/vndk.go
@@ -26,7 +26,8 @@
 )
 
 const (
-	vndkApexNamePrefix = "com.android.vndk.v"
+	vndkApexName       = "com.android.vndk"
+	vndkApexNamePrefix = vndkApexName + ".v"
 )
 
 // apex_vndk creates a special variant of apex modules which contains only VNDK libraries.
@@ -104,29 +105,52 @@
 	}
 }
 
-func makeCompatSymlinks(apexName string, ctx android.ModuleContext) (symlinks []string) {
+// 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) {
-		outDir := filepath.Join("$(PRODUCT_OUT)", dir)
-		link := filepath.Join(outDir, linkName)
-		symlinks = append(symlinks, "mkdir -p "+outDir+" && rm -rf "+link+" && ln -sf "+target+" "+link)
+		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(apexName, vndkApexNamePrefix) {
+	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
-		vndkVersion := strings.TrimPrefix(apexName, vndkApexNamePrefix)
+		apexName := vndkApexNamePrefix + vndkVersion
 		if ctx.Config().Android64() {
-			addSymlink("/apex/"+apexName+"/lib64", "/system/lib64", "vndk-sp-"+vndkVersion)
-			addSymlink("/apex/"+apexName+"/lib64", "/system/lib64", "vndk-"+vndkVersion)
+			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", "/system/lib", "vndk-sp-"+vndkVersion)
-			addSymlink("/apex/"+apexName+"/lib", "/system/lib", "vndk-"+vndkVersion)
+			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_test.go b/bpf/bpf_test.go
index 73b90ba..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)
+	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()
+	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/build_kzip.bash b/build_kzip.bash
index 1e0d48f..329825a 100755
--- a/build_kzip.bash
+++ b/build_kzip.bash
@@ -6,17 +6,31 @@
 # 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
+#   KYTHE_KZIP_ENCODING   proto or json (proto is default)
 #   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)}
+: ${KYTHE_KZIP_ENCODING:=proto}
 
 # The extraction might fail for some source files, so run with -k and then check that
 # sufficiently many files were generated.
-build/soong/soong_ui.bash --build-mode --all-modules --dir=$PWD -k merge_zips xref_cxx xref_java
 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}")
+declare -r go_extractor=$(realpath prebuilts/build-tools/linux-x86/bin/go_extractor)
+declare -r go_root=$(realpath prebuilts/go/linux-x86)
+for dir in blueprint soong; do
+  (cd "build/$dir";
+   "$go_extractor" --goroot="$go_root" --rules=vnames.go.json --canonicalize_package_corpus \
+    --output "${abspath_out}/soong/build_${dir}.go.kzip" ./...
+  )
+done
+
 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; }
 
diff --git a/cc/androidmk.go b/cc/androidmk.go
index ff181d8..c9cd01c 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -25,6 +25,7 @@
 
 var (
 	nativeBridgeSuffix = ".native_bridge"
+	productSuffix      = ".product"
 	vendorSuffix       = ".vendor"
 	recoverySuffix     = ".recovery"
 )
@@ -37,7 +38,7 @@
 	Os() android.OsType
 	Host() bool
 	UseVndk() bool
-	vndkVersion() string
+	VndkVersion() string
 	static() bool
 	InRecovery() bool
 }
@@ -92,7 +93,7 @@
 				if c.UseVndk() {
 					fmt.Fprintln(w, "LOCAL_USE_VNDK := true")
 					if c.IsVndk() && !c.static() {
-						fmt.Fprintln(w, "LOCAL_SOONG_VNDK_VERSION := "+c.vndkVersion())
+						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() {
@@ -216,6 +217,9 @@
 			fmt.Fprintln(w, "LOCAL_NO_NOTICE_FILE := true")
 			fmt.Fprintln(w, "LOCAL_VNDK_DEPEND_ON_CORE_VARIANT := true")
 		}
+		if library.checkSameCoreVariant {
+			fmt.Fprintln(w, "LOCAL_CHECK_SAME_VNDK_VARIANTS := true")
+		}
 	})
 
 	if library.shared() && !library.buildStubs() {
@@ -283,6 +287,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)
@@ -303,6 +310,9 @@
 		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)
@@ -317,6 +327,11 @@
 			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())
diff --git a/cc/binary.go b/cc/binary.go
index 617d4dd..ba6ed5f 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -56,8 +56,12 @@
 }
 
 func init() {
-	android.RegisterModuleType("cc_binary", BinaryFactory)
-	android.RegisterModuleType("cc_binary_host", binaryHostFactory)
+	RegisterBinaryBuildComponents(android.InitRegistrationContext)
+}
+
+func RegisterBinaryBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("cc_binary", BinaryFactory)
+	ctx.RegisterModuleType("cc_binary_host", binaryHostFactory)
 }
 
 // cc_binary produces a binary that is runnable on a device.
diff --git a/cc/builder.go b/cc/builder.go
index 1ec323f..5f0da5f 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -238,9 +238,13 @@
 	_ = pctx.SourcePathVariable("kytheVnames", "build/soong/vnames.json")
 	_ = pctx.VariableFunc("kytheCorpus",
 		func(ctx android.PackageVarContext) string { return ctx.Config().XrefCorpusName() })
+	_ = pctx.VariableFunc("kytheCuEncoding",
+		func(ctx android.PackageVarContext) string { return ctx.Config().XrefCuEncoding() })
 	kytheExtract = pctx.StaticRule("kythe",
 		blueprint.RuleParams{
-			Command:     "rm -f $out && KYTHE_CORPUS=${kytheCorpus} KYTHE_OUTPUT_FILE=$out KYTHE_VNAMES=$kytheVnames $cxxExtractor $cFlags $in ",
+			Command: `rm -f $out && ` +
+				`KYTHE_CORPUS=${kytheCorpus} KYTHE_OUTPUT_FILE=$out KYTHE_VNAMES=$kytheVnames KYTHE_KZIP_ENCODING=${kytheCuEncoding} ` +
+				`$cxxExtractor $cFlags $in `,
 			CommandDeps: []string{"$cxxExtractor", "$kytheVnames"},
 		},
 		"cFlags")
diff --git a/cc/cc.go b/cc/cc.go
index f306a00..022e350 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) {
+	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("image", android.ImageMutator).Parallel()
 		ctx.BottomUp("link", LinkageMutator).Parallel()
-		ctx.BottomUp("ndk_api", ndkApiMutator).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()
 
@@ -80,7 +85,6 @@
 	})
 
 	android.RegisterSingletonType("kythe_extract_all", kytheExtractAllFactory)
-	pctx.Import("android/soong/cc/config")
 }
 
 type Deps struct {
@@ -95,6 +99,7 @@
 
 	GeneratedSources []string
 	GeneratedHeaders []string
+	GeneratedDeps    []string
 
 	ReexportGeneratedHeaders []string
 
@@ -121,14 +126,16 @@
 	// Paths to generated source files
 	GeneratedSources android.Paths
 	GeneratedHeaders android.Paths
+	GeneratedDeps    android.Paths
 
-	Flags                []string
-	IncludeDirs          android.Paths
-	SystemIncludeDirs    android.Paths
-	ReexportedDirs       android.Paths
-	ReexportedSystemDirs android.Paths
-	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
@@ -208,8 +215,9 @@
 	PreventInstall            bool     `blueprint:"mutated"`
 	ApexesProvidingSharedLibs []string `blueprint:"mutated"`
 
-	VndkVersion string `blueprint:"mutated"`
-	SubName     string `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
@@ -218,10 +226,10 @@
 	// Make this module available when building for recovery
 	Recovery_available *bool
 
-	// Set by ImageMutator
+	// Set by imageMutator
 	CoreVariantNeeded     bool     `blueprint:"mutated"`
 	RecoveryVariantNeeded bool     `blueprint:"mutated"`
-	VendorVariants        []string `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.
@@ -235,20 +243,24 @@
 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
@@ -276,6 +288,8 @@
 	isVndk() bool
 	isVndkSp() bool
 	isVndkExt() bool
+	inProduct() bool
+	inVendor() bool
 	inRecovery() bool
 	shouldCreateSourceAbiDump() bool
 	selectedStl() string
@@ -284,6 +298,7 @@
 	isPgoCompile() bool
 	isNDKStubLibrary() bool
 	useClangLld(actx ModuleContext) bool
+	isForPlatform() bool
 	apexName() string
 	hasStubsVariants() bool
 	isStubs() bool
@@ -383,6 +398,13 @@
 	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
@@ -409,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
@@ -445,6 +470,9 @@
 	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 {
@@ -672,7 +700,7 @@
 }
 
 func (c *Module) VndkVersion() string {
-	return c.vndkVersion()
+	return c.Properties.VndkVersion
 }
 
 func (c *Module) Init() android.Module {
@@ -744,6 +772,12 @@
 	return false
 }
 
+// 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 != ""
 }
@@ -752,7 +786,7 @@
 	return c.coverage.Properties.IsCoverageVariant
 }
 
-func (c *Module) isNdk() bool {
+func (c *Module) IsNdk() bool {
 	return inList(c.Name(), ndkMigratedLibs)
 }
 
@@ -779,10 +813,6 @@
 	return false
 }
 
-func (c *Module) vndkVersion() string {
-	return c.Properties.VndkVersion
-}
-
 func (c *Module) isPgoCompile() bool {
 	if pgo := c.pgo; pgo != nil {
 		return pgo.Properties.PgoCompile
@@ -822,12 +852,32 @@
 	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)
 }
 
+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
+}
+
+// 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()
 }
@@ -895,6 +945,13 @@
 	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", "libdl_android", "linker":
@@ -929,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 {
@@ -965,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)
 	}
@@ -985,7 +1043,7 @@
 }
 
 func (ctx *moduleContextImpl) isNdk() bool {
-	return ctx.mod.isNdk()
+	return ctx.mod.IsNdk()
 }
 
 func (ctx *moduleContextImpl) isLlndk(config android.Config) bool {
@@ -1024,6 +1082,14 @@
 	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()
 }
@@ -1047,11 +1113,7 @@
 		// Host modules do not need ABI dumps.
 		return false
 	}
-	if !ctx.mod.IsForPlatform() {
-		// APEX variants do not need ABI dumps.
-		return false
-	}
-	if ctx.isStubs() {
+	if ctx.isStubs() || ctx.isNDKStubLibrary() {
 		// Stubs do not need ABI dumps.
 		return false
 	}
@@ -1077,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()
 }
@@ -1202,11 +1268,43 @@
 	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.
 	//
@@ -1226,21 +1324,17 @@
 		c.Properties.SubName += nativeBridgeSuffix
 	}
 
-	if _, ok := c.linker.(*vndkPrebuiltLibraryDecorator); ok {
+	_, 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 _, ok := c.linker.(*llndkStubDecorator); ok || (c.UseVndk() && c.HasVendorVariant()) {
-		// .vendor.{version} suffix is added only when we will have two variants: core and vendor.
-		// The suffix is not added for vendor-only module.
-		c.Properties.SubName += vendorSuffix
-		vendorVersion := actx.DeviceConfig().VndkVersion()
-		if vendorVersion == "current" {
-			vendorVersion = actx.DeviceConfig().PlatformVndkVersion()
-		}
-		if c.Properties.VndkVersion != vendorVersion {
-			c.Properties.SubName += "." + c.Properties.VndkVersion
-		}
 	} else if c.InRecovery() && !c.OnlyInRecovery() {
 		c.Properties.SubName += recoverySuffix
 	}
@@ -1906,6 +2000,7 @@
 		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) {
@@ -1929,11 +2024,15 @@
 			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()
 					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.Strings()...)
@@ -2046,7 +2145,8 @@
 			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.exportedDeps()...)
+					depPaths.GeneratedHeaders = append(depPaths.GeneratedHeaders, i.exportedGeneratedHeaders()...)
+					depPaths.GeneratedDeps = append(depPaths.GeneratedDeps, i.exportedDeps()...)
 					depPaths.Flags = append(depPaths.Flags, i.exportedFlags()...)
 
 					if t.ReexportFlags {
@@ -2182,15 +2282,7 @@
 			} 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.
-				ret := libName + vendorSuffix
-				vendorVersion := ctx.DeviceConfig().VndkVersion()
-				if vendorVersion == "current" {
-					vendorVersion = ctx.DeviceConfig().PlatformVndkVersion()
-				}
-				if c.Properties.VndkVersion != vendorVersion {
-					ret += "." + c.Properties.VndkVersion
-				}
-				return ret
+				return libName + c.getNameSuffixWithVndkVersion(ctx)
 			} else if (ctx.Platform() || ctx.ProductSpecific()) && isVendorPublicLib {
 				return libName + vendorPublicLibrarySuffix
 			} else if ccDep.InRecovery() && !ccDep.OnlyInRecovery() {
@@ -2244,15 +2336,19 @@
 	depPaths.IncludeDirs = android.FirstUniquePaths(depPaths.IncludeDirs)
 	depPaths.SystemIncludeDirs = android.FirstUniquePaths(depPaths.SystemIncludeDirs)
 	depPaths.GeneratedHeaders = android.FirstUniquePaths(depPaths.GeneratedHeaders)
+	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
 }
 
@@ -2341,6 +2437,9 @@
 			}
 			return "native:vndk_private"
 		}
+		if c.inProduct() {
+			return "native:product"
+		}
 		return "native:vendor"
 	} else if c.InRecovery() {
 		return "native:recovery"
@@ -2382,15 +2481,29 @@
 }
 
 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) 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) {
@@ -2459,7 +2572,6 @@
 		&PgoProperties{},
 		&XomProperties{},
 		&android.ProtoProperties{},
-		&android.ApexProperties{},
 	)
 
 	android.InitDefaultsModule(module)
@@ -2467,12 +2579,6 @@
 	return module
 }
 
-const (
-	// VendorVariationPrefix is the variant prefix used for /vendor code that compiles
-	// against the VNDK.
-	VendorVariationPrefix = "vendor."
-)
-
 func squashVendorSrcs(m *Module) {
 	if lib, ok := m.compiler.(*libraryDecorator); ok {
 		lib.baseCompiler.Properties.Srcs = append(lib.baseCompiler.Properties.Srcs,
@@ -2544,52 +2650,65 @@
 	var recoveryVariantNeeded bool = false
 
 	var vendorVariants []string
+	var productVariants []string
 
 	platformVndkVersion := mctx.DeviceConfig().PlatformVndkVersion()
-	deviceVndkVersion := mctx.DeviceConfig().VndkVersion()
-	if deviceVndkVersion == "current" {
-		deviceVndkVersion = platformVndkVersion
+	boardVndkVersion := mctx.DeviceConfig().VndkVersion()
+	productVndkVersion := mctx.DeviceConfig().ProductVndkVersion()
+	if boardVndkVersion == "current" {
+		boardVndkVersion = platformVndkVersion
+	}
+	if productVndkVersion == "current" {
+		productVndkVersion = platformVndkVersion
 	}
 
-	if mctx.DeviceConfig().VndkVersion() == "" {
+	if boardVndkVersion == "" {
 		// If the device isn't compiling against the VNDK, we always
 		// use the core mode.
 		coreVariantNeeded = true
-	} else if m.Target().NativeBridge == android.NativeBridgeEnabled {
-		// Skip creating vendor variants for natvie bridge modules
-		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.
+		// 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,
-			deviceVndkVersion,
+			boardVndkVersion,
+		)
+		productVariants = append(productVariants,
+			platformVndkVersion,
+			productVndkVersion,
 		)
 	} else if _, ok := m.linker.(*llndkHeadersDecorator); ok {
 		// ... and LL-NDK headers as well
 		vendorVariants = append(vendorVariants,
 			platformVndkVersion,
-			deviceVndkVersion,
+			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.
 		vendorVariants = append(vendorVariants, lib.version())
 	} else if m.HasVendorVariant() && !vendorSpecific {
-		// This will be available in both /system and /vendor
-		// or a /system directory that is available to vendor.
+		// This will be available in /system, /vendor and /product
+		// or a /system directory that is available to vendor and product.
 		coreVariantNeeded = true
 		vendorVariants = append(vendorVariants, platformVndkVersion)
+		productVariants = append(productVariants, platformVndkVersion)
 		// VNDK modules must not create BOARD_VNDK_VERSION variant because its
 		// code is PLATFORM_VNDK_VERSION.
 		// On the other hand, vendor_available modules which are not VNDK should
 		// also build BOARD_VNDK_VERSION because it's installed in /vendor.
+		// vendor_available modules are also available to /product.
 		if !m.IsVndk() {
-			vendorVariants = append(vendorVariants, deviceVndkVersion)
+			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
-		vendorVariants = append(vendorVariants, deviceVndkVersion)
+		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
@@ -2597,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
 	}
@@ -2606,16 +2738,12 @@
 		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)
 	}
 
-	for _, variant := range android.FirstUniqueStrings(vendorVariants) {
-		m.Properties.VendorVariants = append(m.Properties.VendorVariants, VendorVariationPrefix+variant)
+	for _, variant := range android.FirstUniqueStrings(productVariants) {
+		m.Properties.ExtraVariants = append(m.Properties.ExtraVariants, ProductVariationPrefix+variant)
 	}
 
 	m.Properties.RecoveryVariantNeeded = recoveryVariantNeeded
@@ -2631,7 +2759,7 @@
 }
 
 func (c *Module) ExtraImageVariations(ctx android.BaseModuleContext) []string {
-	return c.Properties.VendorVariants
+	return c.Properties.ExtraVariants
 }
 
 func (c *Module) SetImageVariation(ctx android.BaseModuleContext, variant string, module android.Module) {
@@ -2640,8 +2768,13 @@
 		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)
 	}
 }
 
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 0cbdd52..332cc45 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -52,15 +52,10 @@
 	os.Exit(run())
 }
 
-func testCcWithConfig(t *testing.T, bp string, config android.Config) *android.TestContext {
+func testCcWithConfig(t *testing.T, config android.Config) *android.TestContext {
 	t.Helper()
-	return testCcWithConfigForOs(t, bp, config, android.Android)
-}
-
-func testCcWithConfigForOs(t *testing.T, bp string, config android.Config, os android.OsType) *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)
@@ -72,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 {
@@ -111,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.VER_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) {
@@ -131,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
@@ -168,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 {
@@ -285,16 +295,19 @@
 func checkVndkLibrariesOutput(t *testing.T, ctx *android.TestContext, module string, expected []string) {
 	t.Helper()
 	vndkLibraries := ctx.ModuleForTests(module, "")
-	output := insertVndkVersion(module, "VER")
+
+	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) {
-	config := android.TestArchConfig(buildDir, nil)
-	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
-	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
-
-	ctx := testCcWithConfig(t, `
+	bp := `
 		cc_library {
 			name: "libvndk",
 			vendor_available: true,
@@ -354,7 +367,13 @@
 		vndk_libraries_txt {
 			name: "vndkcorevariant.libraries.txt",
 		}
-	`, config)
+	`
+
+	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, "")
@@ -376,8 +395,8 @@
 	vndkCoreLib2ndPath := filepath.Join(vndkLib2ndPath, "shared", "vndk-core")
 	vndkSpLib2ndPath := filepath.Join(vndkLib2ndPath, "shared", "vndk-sp")
 
-	variant := "android_arm64_armv8-a_vendor.VER_shared"
-	variant2nd := "android_arm_armv7-a-neon_vendor.VER_shared"
+	variant := "android_vendor.VER_arm64_armv8-a_shared"
+	variant2nd := "android_vendor.VER_arm_armv7-a-neon_shared"
 
 	checkVndkSnapshot(t, ctx, "libvndk", "libvndk.so", vndkCoreLibPath, variant)
 	checkVndkSnapshot(t, ctx, "libvndk", "libvndk.so", vndkCoreLib2ndPath, variant2nd)
@@ -412,28 +431,22 @@
 }
 
 func TestVndkLibrariesTxtAndroidMk(t *testing.T) {
-	config := android.TestArchConfig(buildDir, nil)
-	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
-	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
-	ctx := testCcWithConfig(t, `
+	bp := `
 		vndk_libraries_txt {
 			name: "llndk.libraries.txt",
-		}`, config)
+		}`
+	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())
+	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) {
-	config := android.TestArchConfig(buildDir, 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, `
+	bp := `
 		cc_library {
 			name: "libvndk",
 			vendor_available: true,
@@ -465,7 +478,16 @@
 		vndk_libraries_txt {
 			name: "vndkcorevariant.libraries.txt",
 		}
-	`, config)
+	`
+
+	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"})
 }
@@ -1445,76 +1467,232 @@
 	`)
 }
 
+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)
 
 	assertMapKeys(t, vndkCoreLibraries(config),
 		[]string{"libvndk", "libvndkprivate"})
@@ -1525,7 +1703,7 @@
 	assertMapKeys(t, vndkPrivateLibraries(config),
 		[]string{"libft2", "libllndkprivate", "libvndkprivate"})
 
-	vendorVariant27 := "android_arm64_armv8-a_vendor.27_shared"
+	vendorVariant27 := "android_vendor.27_arm64_armv8-a_shared"
 
 	tests := []struct {
 		variant  string
@@ -1906,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"})
@@ -1940,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"})
@@ -1976,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.VER_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)
@@ -2052,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)
@@ -2062,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.VER_shared"
+	variant = "android_vendor.VER_arm64_armv8-a_shared"
 
 	module = ctx.ModuleForTests("libvendor_available2", variant).Module().(*Module)
 	checkRuntimeLibs(t, []string{"libvendor_available1.vendor"}, module)
@@ -2074,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.VER_shared"
+	variant = "android_vendor.VER_arm64_armv8-a_shared"
 	module = ctx.ModuleForTests("libvendor_available3", variant).Module().(*Module)
 	checkRuntimeLibs(t, nil, module)
 }
@@ -2088,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)
@@ -2123,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", "libc++demangle", "libclang_rt.builtins-aarch64-android", "libatomic", "libgcc_stripped"}, module)
+	checkStaticLibs(t, []string{"lib1", "libc++demangle", "libclang_rt.builtins-aarch64-android", "libatomic"}, 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", "libc++demangle", "libclang_rt.builtins-aarch64-android", "libatomic", "libgcc_stripped"}, module)
+	checkStaticLibs(t, []string{"lib1", "libc++_static", "libc++demangle", "libclang_rt.builtins-aarch64-android", "libatomic"}, module)
 }
 
 var compilerFlagsTestCases = []struct {
@@ -2256,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.VER", 1)).Rule("ld")
+	ld = ctx.ModuleForTests("libvendor", vendorVariant).Rule("ld")
 	libflags = ld.Args["libFlags"]
-	stubPaths = getOutputPaths(ctx, strings.Replace(variant, "_core", "_vendor.VER", 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)
 	}
@@ -2303,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)
 	}
@@ -2338,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) {
@@ -2368,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) {
@@ -2391,7 +2570,7 @@
 			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"}
@@ -2434,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"+
@@ -2475,7 +2654,7 @@
 			srcs: ["foo.c"],
 		}`)
 
-	variant := "android_arm64_armv8-a_core"
+	variant := "android_arm64_armv8-a_fuzzer"
 	ctx.ModuleForTests("fuzz_smoke_test", variant).Rule("cc")
 }
 
@@ -2550,24 +2729,24 @@
 		return ret
 	}
 
-	shared := ctx.ModuleForTests("libshared", "android_arm64_armv8-a_core_shared").Rule("ld")
+	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_core_shared").Rule("ld")
+	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_core").Rule("ld")
+	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_core_static").Rule("ar")
+	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_core_static").Rule("ar")
+	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..4e23a7b
--- /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(cparams)-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/cmakelists.go b/cc/cmakelists.go
index 97d21f4..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"))
@@ -465,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()+"-"+
@@ -473,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 519380f..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)
 		}
 	}
@@ -174,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(),
 			}
@@ -200,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 671861b..1ced451 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -27,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 {
@@ -509,6 +513,12 @@
 		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
 }
 
@@ -550,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)
diff --git a/cc/config/OWNERS b/cc/config/OWNERS
new file mode 100644
index 0000000..b2f54e5
--- /dev/null
+++ b/cc/config/OWNERS
@@ -0,0 +1 @@
+per-file vndk.go = smoreland@google.com, victoryang@google.com
diff --git a/cc/config/global.go b/cc/config/global.go
index f18f950..87314dc 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -48,6 +48,8 @@
 		"-fno-strict-aliasing",
 
 		"-Werror=date-time",
+		"-Werror=pragma-pack",
+		"-Werror=pragma-pack-suspicious-include",
 	}
 
 	commonGlobalConlyflags = []string{}
@@ -86,6 +88,7 @@
 		"-Wl,--no-undefined-version",
 		"-Wl,--exclude-libs,libgcc.a",
 		"-Wl,--exclude-libs,libgcc_stripped.a",
+		"-Wl,--exclude-libs,libunwind_llvm.a",
 	}
 
 	deviceGlobalLldflags = append(ClangFilterUnknownLldflags(deviceGlobalLdflags),
diff --git a/cc/config/vndk.go b/cc/config/vndk.go
index c3cda49..3b16c78 100644
--- a/cc/config/vndk.go
+++ b/cc/config/vndk.go
@@ -71,6 +71,7 @@
 	"android.hardware.nfc@1.2",
 	"android.hardware.oemlock@1.0",
 	"android.hardware.power.stats@1.0",
+	"android.hardware.power-ndk_platform",
 	"android.hardware.power@1.0",
 	"android.hardware.power@1.1",
 	"android.hardware.radio@1.4",
@@ -84,6 +85,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",
@@ -130,6 +132,7 @@
 	"libsqlite",
 	"libssl",
 	"libstagefright_amrnb_common",
+	"libstagefright_bufferpool@2.0",
 	"libstagefright_bufferqueue_helper",
 	"libstagefright_enc_common",
 	"libstagefright_flacdec",
@@ -165,5 +168,4 @@
 	"libxml2",
 	"libyuv",
 	"libziparchive",
-	"vintf-vibrator-ndk_platform",
 }
diff --git a/cc/coverage.go b/cc/coverage.go
index abeba1f..b94b628 100644
--- a/cc/coverage.go
+++ b/cc/coverage.go
@@ -189,6 +189,7 @@
 	IsNativeCoverageNeeded(ctx android.BaseModuleContext) bool
 	PreventInstall()
 	HideFromMake()
+	MarkAsCoverageVariant(bool)
 }
 
 func coverageMutator(mctx android.BottomUpMutatorContext) {
@@ -217,6 +218,7 @@
 		// 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
index c2b0ff4..8b84be8 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -27,9 +27,10 @@
 type FuzzConfig struct {
 	// Email address of people to CC on bugs or contact about this fuzz target.
 	Cc []string `json:"cc,omitempty"`
-	// Boolean specifying whether to disable the fuzz target from running
-	// automatically in continuous fuzzing infrastructure.
-	Disable *bool `json:"disable,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.
@@ -49,6 +50,9 @@
 	// 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.
@@ -81,6 +85,8 @@
 	corpus                android.Paths
 	corpusIntermediateDir android.Path
 	config                android.Path
+	data                  android.Paths
+	dataIntermediateDir   android.Path
 	installedSharedDeps   []string
 }
 
@@ -210,6 +216,17 @@
 	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" {
@@ -377,6 +394,19 @@
 			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 {
diff --git a/cc/gen_test.go b/cc/gen_test.go
index ceecf1c..4b9a36e 100644
--- a/cc/gen_test.go
+++ b/cc/gen_test.go
@@ -31,8 +31,8 @@
 			],
 		}`)
 
-		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.Local.CommonFlags) {
 			t.Errorf("missing aidl includes in global flags")
@@ -55,8 +55,8 @@
 			],
 		}`)
 
-		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.Local.CommonFlags) {
 			t.Errorf("missing aidl includes in global flags")
diff --git a/cc/genrule.go b/cc/genrule.go
index e74dd4d..548d5f2 100644
--- a/cc/genrule.go
+++ b/cc/genrule.go
@@ -55,16 +55,15 @@
 		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 {
-	if Bool(g.Recovery_available) {
-		primaryArch := ctx.Config().DevicePrimaryArchType()
-		moduleArch := ctx.Target().Arch.ArchType
-		return moduleArch == primaryArch
-	}
-	return false
+	return Bool(g.Recovery_available)
 }
 
 func (g *GenruleExtraProperties) ExtraImageVariations(ctx android.BaseModuleContext) []string {
@@ -72,16 +71,26 @@
 		return nil
 	}
 
+	var variants []string
 	if Bool(g.Vendor_available) || ctx.SocSpecific() || ctx.DeviceSpecific() {
-		var variants []string
 		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
 	}
 
-	return nil
+	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 785e3e1..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", 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.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 9fdc88a..2f55ac5 100644
--- a/cc/installer.go
+++ b/cc/installer.go
@@ -72,7 +72,11 @@
 		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/library.go b/cc/library.go
index 98cae3d..f29c4d0 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -174,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
@@ -188,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()
 }
 
@@ -195,6 +204,7 @@
 func LibraryStaticFactory() android.Module {
 	module, library := NewLibrary(android.HostAndDeviceSupported)
 	library.BuildOnlyStatic()
+	module.sdkMemberTypes = []android.SdkMemberType{staticLibrarySdkMemberType}
 	return module.Init()
 }
 
@@ -202,6 +212,7 @@
 func LibrarySharedFactory() android.Module {
 	module, library := NewLibrary(android.HostAndDeviceSupported)
 	library.BuildOnlyShared()
+	module.sdkMemberTypes = []android.SdkMemberType{sharedLibrarySdkMemberType}
 	return module.Init()
 }
 
@@ -210,6 +221,7 @@
 func LibraryHostStaticFactory() android.Module {
 	module, library := NewLibrary(android.HostSupported)
 	library.BuildOnlyStatic()
+	module.sdkMemberTypes = []android.SdkMemberType{staticLibrarySdkMemberType}
 	return module.Init()
 }
 
@@ -217,6 +229,7 @@
 func LibraryHostSharedFactory() android.Module {
 	module, library := NewLibrary(android.HostSupported)
 	library.BuildOnlyShared()
+	module.sdkMemberTypes = []android.SdkMemberType{sharedLibrarySdkMemberType}
 	return module.Init()
 }
 
@@ -237,6 +250,7 @@
 	systemDirs android.Paths
 	flags      []string
 	deps       android.Paths
+	headers    android.Paths
 }
 
 func (f *flagExporter) exportedIncludes(ctx ModuleContext) android.Paths {
@@ -280,6 +294,12 @@
 	f.deps = append(f.deps, deps...)
 }
 
+// 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
 }
@@ -296,11 +316,16 @@
 	return f.deps
 }
 
+func (f *flagExporter) exportedGeneratedHeaders() android.Paths {
+	return f.headers
+}
+
 type exportedFlagsProducer interface {
 	exportedDirs() android.Paths
 	exportedSystemDirs() android.Paths
 	exportedFlags() []string
 	exportedDeps() android.Paths
+	exportedGeneratedHeaders() android.Paths
 }
 
 var _ exportedFlagsProducer = (*flagExporter)(nil)
@@ -360,7 +385,8 @@
 
 	// If useCoreVariant is true, the vendor variant of a VNDK library is
 	// not installed.
-	useCoreVariant bool
+	useCoreVariant       bool
+	checkSameCoreVariant bool
 
 	// Decorated interafaces
 	*baseCompiler
@@ -509,6 +535,19 @@
 	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) != ""
 }
 
@@ -966,12 +1005,16 @@
 	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")
 			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...)
 		}
 	}
 
@@ -983,7 +1026,10 @@
 			}
 			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...)
 		}
 	}
 
@@ -1001,6 +1047,7 @@
 
 		library.reexportDirs(dir)
 		library.reexportDeps(library.baseCompiler.pathDeps...)
+		library.addExportedGeneratedHeaders(library.baseCompiler.pathDeps...)
 	}
 
 	if library.buildStubs() {
@@ -1050,8 +1097,11 @@
 			if ctx.isVndkSp() {
 				library.baseInstaller.subDir = "vndk-sp"
 			} else if ctx.isVndk() {
-				if ctx.DeviceConfig().VndkUseCoreVariant() && !ctx.mustUseVendorVariant() {
-					library.useCoreVariant = true
+				if !ctx.mustUseVendorVariant() {
+					library.checkSameCoreVariant = true
+					if ctx.DeviceConfig().VndkUseCoreVariant() {
+						library.useCoreVariant = true
+					}
 				}
 				library.baseInstaller.subDir = "vndk"
 			}
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 f8d8934..b8d8895 100644
--- a/cc/library_test.go
+++ b/cc/library_test.go
@@ -27,8 +27,8 @@
 			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) != 2 {
 			t.Fatalf("unexpected inputs to libfoo shared: %#v", libfooShared.Inputs.Strings())
@@ -56,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())
@@ -82,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())
@@ -108,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())
@@ -134,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())
@@ -165,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())
@@ -180,7 +180,7 @@
 			t.Errorf("static objects not reused for shared library")
 		}
 
-		libfoo := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_core_shared").Module().(*Module)
+		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
index 815d405..106092b 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -51,6 +51,8 @@
 	ToolchainLibrary() bool
 	NdkPrebuiltStl() bool
 	StubDecorator() bool
+
+	AllStaticDeps() []string
 }
 
 type DependencyTag struct {
diff --git a/cc/linker.go b/cc/linker.go
index 61ae757..6f2e5b7 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -224,11 +224,10 @@
 	}
 
 	if ctx.toolchain().Bionic() {
-		// libclang_rt.builtins, libgcc and libatomic have to be last on the command line
+		// libclang_rt.builtins and libatomic have to be last on the command line
 		if !Bool(linker.Properties.No_libcrt) {
 			deps.LateStaticLibs = append(deps.LateStaticLibs, config.BuiltinsRuntimeLibrary(ctx.toolchain()))
 			deps.LateStaticLibs = append(deps.LateStaticLibs, "libatomic")
-			deps.LateStaticLibs = append(deps.LateStaticLibs, "libgcc_stripped")
 		}
 
 		systemSharedLibs := linker.Properties.System_shared_libs
diff --git a/cc/llndk_library.go b/cc/llndk_library.go
index f3ee5c1..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.Module().(*Module).Properties.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, "--llndk")
+	objs, versionScript := compileStubLibrary(ctx, flags, String(stub.Properties.Symbol_file), vndkVer, "--llndk")
 	stub.versionScriptPath = versionScript
 	return objs
 }
diff --git a/cc/makevars.go b/cc/makevars.go
index e8cedf0..0f9f4c1 100644
--- a/cc/makevars.go
+++ b/cc/makevars.go
@@ -147,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}")
 
diff --git a/cc/ndk_headers.go b/cc/ndk_headers.go
index b8423be..5744bb2 100644
--- a/cc/ndk_headers.go
+++ b/cc/ndk_headers.go
@@ -16,7 +16,6 @@
 
 import (
 	"fmt"
-	"os"
 	"path/filepath"
 	"strings"
 
@@ -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 da94d33..00338b9 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -228,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 {
@@ -390,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 f6de4ef..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)
diff --git a/cc/pgo.go b/cc/pgo.go
index 4618f4e..e341d03 100644
--- a/cc/pgo.go
+++ b/cc/pgo.go
@@ -177,6 +177,10 @@
 		// if profileFile gets updated
 		flags.CFlagsDeps = append(flags.CFlagsDeps, profileFilePath)
 		flags.LdFlagsDeps = append(flags.LdFlagsDeps, profileFilePath)
+
+		if props.isSampling() {
+			flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-mllvm,-no-warn-sample-unused=true")
+		}
 	}
 	return flags
 }
@@ -210,11 +214,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 4e6cdd7..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)
 
@@ -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,9 +147,24 @@
 
 	android.InitPrebuiltModule(module, &prebuilt.properties.Srcs)
 
-	// Prebuilt libraries can be included in APEXes
-	android.InitApexModule(module)
+	// 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
 }
@@ -165,19 +177,8 @@
 }
 
 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)
-	android.InitSdkAwareModule(module)
 	return module, library
 }
 
diff --git a/cc/prebuilt_test.go b/cc/prebuilt_test.go
index 72f9f4a..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", PrebuiltSharedLibraryFactory)
-	ctx.RegisterModuleType("cc_prebuilt_library_static", PrebuiltStaticLibraryFactory)
-	ctx.RegisterModuleType("cc_prebuilt_binary", 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_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 61fd1a8..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()
 		}
 	})
 }
diff --git a/cc/sanitize.go b/cc/sanitize.go
index b4082d3..c4aeb96 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -494,6 +494,15 @@
 		// 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) {
diff --git a/cc/stl.go b/cc/stl.go
index 5ccd44a..af015f9 100644
--- a/cc/stl.go
+++ b/cc/stl.go
@@ -171,11 +171,13 @@
 			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")
+				if ctx.Arch().ArchType == android.Arm {
+					deps.StaticLibs = append(deps.StaticLibs, "libunwind_llvm")
+				} else {
+					deps.StaticLibs = append(deps.StaticLibs, "libgcc_stripped")
+				}
 			}
 		}
 	case "":
@@ -196,6 +198,8 @@
 		}
 		if ctx.Arch().ArchType == android.Arm {
 			deps.StaticLibs = append(deps.StaticLibs, "ndk_libunwind")
+		} else {
+			deps.StaticLibs = append(deps.StaticLibs, "libgcc_stripped")
 		}
 	default:
 		panic(fmt.Errorf("Unknown stl: %q", stl.Properties.SelectedStl))
diff --git a/cc/test_data_test.go b/cc/test_data_test.go
index 962ff26..ae59e2f 100644
--- a/cc/test_data_test.go
+++ b/cc/test_data_test.go
@@ -115,20 +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 := android.NewTestContext()
 			ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
 			ctx.RegisterModuleType("test", newTest)
-			ctx.Register()
+			ctx.Register(config)
 
 			_, errs := ctx.ParseBlueprintsFiles("Blueprints")
 			android.FailIfErrored(t, errs)
diff --git a/cc/testing.go b/cc/testing.go
index 3b10f87..ba8ed95 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -18,6 +18,19 @@
 	"android/soong/android"
 )
 
+func RegisterRequiredBuildComponentsForTest(ctx android.RegistrationContext) {
+	RegisterPrebuiltBuildComponents(ctx)
+	android.RegisterPrebuiltMutators(ctx)
+
+	RegisterCCBuildComponents(ctx)
+	RegisterBinaryBuildComponents(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 {
@@ -97,6 +110,14 @@
 			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,
@@ -115,6 +136,7 @@
 			name: "libc",
 			no_libcrt: true,
 			nocrt: true,
+			stl: "none",
 			system_shared_libs: [],
 			recovery_available: true,
 		}
@@ -126,6 +148,7 @@
 			name: "libm",
 			no_libcrt: true,
 			nocrt: true,
+			stl: "none",
 			system_shared_libs: [],
 			recovery_available: true,
 		}
@@ -137,6 +160,7 @@
 			name: "libdl",
 			no_libcrt: true,
 			nocrt: true,
+			stl: "none",
 			system_shared_libs: [],
 			recovery_available: true,
 		}
@@ -249,45 +273,13 @@
 	return ret
 }
 
-func CreateTestContext(bp string, fs map[string][]byte,
-	os android.OsType) *android.TestContext {
-
-	ctx := android.NewTestArchContext()
-	ctx.RegisterModuleType("cc_defaults", defaultsFactory)
-	ctx.RegisterModuleType("cc_binary", BinaryFactory)
-	ctx.RegisterModuleType("cc_binary_host", binaryHostFactory)
-	ctx.RegisterModuleType("cc_fuzz", FuzzFactory)
-	ctx.RegisterModuleType("cc_library", LibraryFactory)
-	ctx.RegisterModuleType("cc_library_shared", LibrarySharedFactory)
-	ctx.RegisterModuleType("cc_library_static", LibraryStaticFactory)
-	ctx.RegisterModuleType("cc_library_headers", LibraryHeaderFactory)
-	ctx.RegisterModuleType("cc_test", TestFactory)
-	ctx.RegisterModuleType("toolchain_library", ToolchainLibraryFactory)
-	ctx.RegisterModuleType("llndk_library", LlndkLibraryFactory)
-	ctx.RegisterModuleType("llndk_headers", llndkHeadersFactory)
-	ctx.RegisterModuleType("vendor_public_library", vendorPublicLibraryFactory)
-	ctx.RegisterModuleType("cc_object", ObjectFactory)
-	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
-	ctx.RegisterModuleType("vndk_prebuilt_shared", VndkPrebuiltSharedFactory)
-	ctx.RegisterModuleType("vndk_libraries_txt", VndkLibrariesTxtFactory)
-	ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.BottomUp("image", android.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.PreArchMutators(android.RegisterDefaultsPreArchMutators)
-	ctx.RegisterSingletonType("vndk-snapshot", 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,
@@ -305,7 +297,29 @@
 		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_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/vndk.go b/cc/vndk.go
index f25861a..872a473 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -42,7 +42,6 @@
 			vndkCoreLibrariesTxt,
 			vndkSpLibrariesTxt,
 			vndkPrivateLibrariesTxt,
-			vndkUsingCoreVariantLibrariesTxt,
 		}
 	}
 	// Snapshot vndks have their own *.libraries.VER.txt files.
@@ -132,16 +131,16 @@
 		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
 			}
 		}
@@ -227,7 +226,7 @@
 	vndkSpLibrariesKey               = android.NewOnceKey("vndkSpLibrarires")
 	llndkLibrariesKey                = android.NewOnceKey("llndkLibrarires")
 	vndkPrivateLibrariesKey          = android.NewOnceKey("vndkPrivateLibrarires")
-	vndkUsingCoreVariantLibrariesKey = android.NewOnceKey("vndkUsingCoreVariantLibrarires")
+	vndkUsingCoreVariantLibrariesKey = android.NewOnceKey("vndkUsingCoreVariantLibraries")
 	vndkMustUseVendorVariantListKey  = android.NewOnceKey("vndkMustUseVendorVariantListKey")
 	vndkLibrariesLock                sync.Mutex
 
@@ -350,7 +349,7 @@
 	}
 
 	if lib, ok := m.linker.(libraryInterface); ok {
-		useCoreVariant := m.vndkVersion() == mctx.DeviceConfig().PlatformVndkVersion() &&
+		useCoreVariant := m.VndkVersion() == mctx.DeviceConfig().PlatformVndkVersion() &&
 			mctx.DeviceConfig().VndkUseCoreVariant() && !m.MustUseVendorVariant()
 		return lib.shared() && m.UseVndk() && m.IsVndk() && !m.isVndkExt() && !useCoreVariant
 	}
@@ -445,7 +444,13 @@
 		return
 	}
 
-	filename := insertVndkVersion(txt.Name(), ctx.DeviceConfig().PlatformVndkVersion())
+	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,
@@ -460,8 +465,8 @@
 	ctx.InstallFile(installPath, filename, txt.outputFile)
 }
 
-func (txt *vndkLibrariesTxt) AndroidMkEntries() android.AndroidMkEntries {
-	return android.AndroidMkEntries{
+func (txt *vndkLibrariesTxt) AndroidMkEntries() []android.AndroidMkEntries {
+	return []android.AndroidMkEntries{android.AndroidMkEntries{
 		Class:      "ETC",
 		OutputFile: android.OptionalPathForPath(txt.outputFile),
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
@@ -469,7 +474,7 @@
 				entries.SetString("LOCAL_MODULE_STEM", txt.outputFile.Base())
 			},
 		},
-	}
+	}}
 }
 
 func (txt *vndkLibrariesTxt) OutputFile() android.OutputPath {
@@ -665,14 +670,14 @@
 		if m.Target().NativeBridge == android.NativeBridgeEnabled {
 			return nil, "", false
 		}
-		if !m.UseVndk() || !m.IsForPlatform() || !m.installable() {
+		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.VndkVersion() == ctx.DeviceConfig().PlatformVndkVersion() && m.IsVndk() && !m.isVndkExt() {
 			if m.isVndkSp() {
 				return l, "vndk-sp", true
 			} else {
@@ -704,7 +709,7 @@
 		// 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.exportedDeps()...)
+		generatedHeaders = append(generatedHeaders, l.exportedGeneratedHeaders()...)
 		for _, dir := range append(l.exportedDirs(), l.exportedSystemDirs()...) {
 			exportedIncludes[dir.String()] = true
 		}
diff --git a/cc/xom.go b/cc/xom.go
index e1cac53..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.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-execute-only")
+			flags.Local.LdFlags = append(flags.Local.LdFlags,
+				"-Wl,--execute-only",
+				"-Wl,-z,separate-code",
+			)
 		}
 	}
 
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 2800ade..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
 
@@ -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()
@@ -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/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 83e3673..2a929c5 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -16,13 +16,14 @@
 
 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 {
 	DisablePreopt        bool     // disable preopt for all modules
 	DisablePreoptModules []string // modules with preopt disabled by product-specific config
@@ -82,19 +83,19 @@
 	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
 }
 
@@ -133,6 +134,17 @@
 	PresignedPrebuilt bool
 }
 
+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 {
 	buildDirPrefix := ctx.Config().BuildDir() + "/"
 	if path == "" {
@@ -167,9 +179,12 @@
 	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
 
@@ -177,44 +192,29 @@
 		// used to construct the real value manually below.
 		DirtyImageObjects 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.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
 
@@ -232,7 +232,7 @@
 
 	config := ModuleJSONConfig{}
 
-	_, err := loadConfig(ctx, path, &config)
+	err := json.Unmarshal(data, &config)
 	if err != nil {
 		return config.ModuleConfig, err
 	}
@@ -253,24 +253,102 @@
 	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 {
@@ -312,7 +390,7 @@
 		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 40986c3..ac5b691 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -131,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).
@@ -170,7 +170,7 @@
 
 	cmd := rule.Command().
 		Text(`ANDROID_LOG_TAGS="*:e"`).
-		Tool(global.Tools.Profman)
+		Tool(global.SoongConfig.Profman)
 
 	// The profile is a test listing of methods.
 	// We need to generate the actual binary profile.
@@ -299,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"`).
@@ -327,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.
@@ -340,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).
@@ -409,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").
@@ -495,8 +495,7 @@
 	}
 
 	for _, f := range global.PatternsOnSystemOther {
-		// See comment of SYSTEM_OTHER_ODEX_FILTER for details on the matching.
-		if makefileMatch("/"+f, dexLocation) || makefileMatch(filepath.Join(SystemPartition, f), dexLocation) {
+		if makefileMatch(filepath.Join(SystemPartition, f), dexLocation) {
 			return true
 		}
 	}
diff --git a/dexpreopt/dexpreopt_gen/dexpreopt_gen.go b/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
index 009e906..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,17 +31,17 @@
 )
 
 var (
-	dexpreoptScriptPath = flag.String("dexpreopt_script", "", "path to output dexpreopt 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) {}
 
@@ -63,23 +64,51 @@
 		usage("path to output dexpreopt 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)
@@ -121,7 +150,7 @@
 		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())
diff --git a/dexpreopt/dexpreopt_test.go b/dexpreopt/dexpreopt_test.go
index 254be0a..a128dc0 100644
--- a/dexpreopt/dexpreopt_test.go
+++ b/dexpreopt/dexpreopt_test.go
@@ -61,7 +61,7 @@
 }
 
 func TestDexPreopt(t *testing.T) {
-	ctx := android.PathContextForTesting(android.TestConfig("out", nil), nil)
+	ctx := android.PathContextForTesting(android.TestConfig("out", nil, "", nil))
 	global, module := GlobalConfigForTests(ctx), testSystemModuleConfig(ctx, "test")
 
 	rule, err := GenerateDexpreoptRule(ctx, global, module)
@@ -80,7 +80,7 @@
 }
 
 func TestDexPreoptSystemOther(t *testing.T) {
-	ctx := android.PathContextForTesting(android.TestConfig("out", nil), nil)
+	ctx := android.PathContextForTesting(android.TestConfig("out", nil, "", nil))
 	global := GlobalConfigForTests(ctx)
 	systemModule := testSystemModuleConfig(ctx, "Stest")
 	systemProductModule := testSystemProductModuleConfig(ctx, "SPtest")
@@ -104,12 +104,13 @@
 				{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: "system_other/product"},
+				{module: productModule, expectedPartition: "product"},
 			},
 		},
 	}
@@ -129,7 +130,7 @@
 			}
 
 			if rule.Installs().String() != wantInstalls.String() {
-				t.Errorf("\npatterns: %v\nwant installs:\n   %v\ngot:\n   %v", test.patterns, wantInstalls, rule.Installs())
+				t.Errorf("\nwant installs:\n   %v\ngot:\n   %v", wantInstalls, rule.Installs())
 			}
 		}
 	}
@@ -137,7 +138,7 @@
 }
 
 func TestDexPreoptProfile(t *testing.T) {
-	ctx := android.PathContextForTesting(android.TestConfig("out", nil), nil)
+	ctx := android.PathContextForTesting(android.TestConfig("out", nil, "", nil))
 	global, module := GlobalConfigForTests(ctx), testSystemModuleConfig(ctx, "test")
 
 	module.ProfileClassListing = android.OptionalPathForPath(android.PathForTesting("profile"))
diff --git a/docs/best_practices.md b/docs/best_practices.md
index 93be319..bc760b8 100644
--- a/docs/best_practices.md
+++ b/docs/best_practices.md
@@ -240,8 +240,8 @@
 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
-[Name resolution](../README.md#name-resolution) section of the Soong README.md
-for more on namespaces.
+[Referencing Modules](../README.md#referencing-modules) section of the Soong
+README.md for more on namespaces.
 
 ### Module with name based on variable
 
diff --git a/docs/perf.md b/docs/perf.md
index c3a2647..538adff 100644
--- a/docs/perf.md
+++ b/docs/perf.md
@@ -8,7 +8,7 @@
 viewed.  Just open `$OUT_DIR/build.trace.gz` in Chrome's <chrome://tracing>, or
 with [catapult's trace viewer][catapult trace_viewer]. The last few traces are
 stored in `build.trace.#.gz` (larger numbers are older). The associated logs
-are stored in `soong.#.log`.
+are stored in `soong.#.log` and `verbose.#.log.gz`.
 
 ![trace example](./trace_example.png)
 
@@ -31,29 +31,29 @@
 
 In most cases, we've found that the fast-path is slow because all of the
 `$(shell)` commands need to be re-executed to determine if their output changed.
-The `$OUT_DIR/soong.log` contains statistics from the regen check:
+The `$OUT_DIR/verbose.log.gz` contains statistics from the regen check:
 
 ```
-.../kati.go:127: *kati*: regen check time: 1.699207
-.../kati.go:127: *kati*: glob time (regen): 0.377193 / 33609
-.../kati.go:127: *kati*: shell time (regen): 1.313529 / 184
-.../kati.go:127: *kati*:  0.217 find device vendor -type f -name \*.pk8 -o -name verifiedboot\* -o -name \*.x509.pem -o -name oem\*.prop | sort
-.../kati.go:127: *kati*:  0.105 cd packages/apps/Dialer ; find -L . -type d -name "res"
-.../kati.go:127: *kati*:  0.035 find device vendor -maxdepth 4 -name '*_aux_variant_config.mk' -o -name '*_aux_os_config.mk' | sort
-.../kati.go:127: *kati*:  0.029 cd frameworks/base ; find -L  core/java graphics/java location/java media/java media/mca/effect/java media/mca/filterfw/java media/mca/filterpacks/java drm/java opengl/java sax/java telecomm/java telephony/java wifi/java lowpan/java keystore/java rs/java ../opt/telephony/src/java/android/telephony ../opt/telephony/src/java/android/telephony/gsm ../opt/net/voip/src/java/android/net/rtp ../opt/net/voip/src/java/android/net/sip   -name "*.html" -and -not -name ".*"
-.../kati.go:127: *kati*:  0.025 test -d device && find -L device -maxdepth 4 -path '*/marlin/BoardConfig.mk'
-.../kati.go:127: *kati*:  0.023 find packages/apps/Settings/tests/robotests -type f -name '*Test.java' | sed -e 's!.*\(com/google.*Test\)\.java!\1!' -e 's!.*\(com/android.*Test\)\.java!\1!' | sed 's!/!\.!g' | cat
-.../kati.go:127: *kati*:  0.022 test -d vendor && find -L vendor -maxdepth 4 -path '*/marlin/BoardConfig.mk'
-.../kati.go:127: *kati*:  0.017 cd cts/tests/tests/shortcutmanager/packages/launchermanifest ; find -L  ../src -name "*.java" -and -not -name ".*"
-.../kati.go:127: *kati*:  0.016 cd cts/tests/tests/shortcutmanager/packages/launchermanifest ; find -L  ../../common/src -name "*.java" -and -not -name ".*"
-.../kati.go:127: *kati*:  0.015 cd libcore && (find luni/src/test/java -name "*.java" 2> /dev/null) | grep -v -f java_tests_blacklist
-.../kati.go:127: *kati*: stat time (regen): 0.250384 / 4405
+verbose: *kati*: regen check time: 0.754030
+verbose: *kati*: glob time (regen): 0.545859 / 43840
+verbose: *kati*: shell time (regen): 0.278095 / 66 (59 unique)
+verbose: *kati*:   0.012 / 1 mkdir -p out/target/product/generic && echo Android/aosp_arm/generic:R/AOSP.MASTER/$(date -d @$(cat out/build_date.txt) +%m%d%H%M):eng/test-keys >out/target/product/generic/build_fingerprint.txt && grep " " out/target/product/generic/build_fingerprint.txt
+verbose: *kati*:   0.010 / 1 echo 'com.android.launcher3.config.FlagOverrideSampleTest com.android.launcher3.logging.FileLogTest com.android.launcher3.model.AddWorkspaceItemsTaskTest com.android.launcher3.model.CacheDataUpdatedTaskTest com.android.launcher3.model.DbDowngradeHelperTest com.android.launcher3.model.GridBackupTableTest com.android.launcher3.model.GridSizeMigrationTaskTest com.android.launcher3.model.PackageInstallStateChangedTaskTest com.android.launcher3.popup.PopupPopulatorTest com.android.launcher3.util.GridOccupancyTest com.android.launcher3.util.IntSetTest' | tr ' ' '\n' | cat
+verbose: *kati*:   0.010 / 1 cd cts/tests/framework/base/windowmanager ; find -L  * -name "Components.java" -and -not -name ".*"
+verbose: *kati*:   0.010 / 1 git -C test/framework/build log -s -n 1 --format="%cd" --date=format:"%Y%m%d_%H%M%S" 2>/dev/null
+verbose: *kati*:   0.009 / 2 cd development/samples/ShortcutDemo/publisher ; find -L  ../common/src -name "*.java" -and -not -name ".*"
+verbose: *kati*:   0.009 / 2 cd development/samples/ShortcutDemo/launcher ; find -L  ../common/src -name "*.java" -and -not -name ".*"
+verbose: *kati*:   0.009 / 1 if ! cmp -s out/target/product/generic/obj/CONFIG/kati_packaging/dist.mk.tmp out/target/product/generic/obj/CONFIG/kati_packaging/dist.mk; then mv out/target/product/generic/obj/CONFIG/kati_packaging/dist.mk.tmp out/target/product/generic/obj/CONFIG/kati_packaging/dist.mk; else rm out/target/product/generic/obj/CONFIG/kati_packaging/dist.mk.tmp; fi
+verbose: *kati*:   0.008 / 1 mkdir -p out/target/product/generic && echo R/AOSP.MASTER/$(cat out/build_number.txt):eng/test-keys >out/target/product/generic/build_thumbprint.txt && grep " " out/target/product/generic/build_thumbprint.txt
+verbose: *kati*:   0.007 / 1 echo 'com.android.customization.model.clock.BaseClockManagerTest com.android.customization.model.clock.ClockManagerTest com.android.customization.model.grid.GridOptionsManagerTest com.android.customization.model.theme.ThemeManagerTest' | tr ' ' '\n' | cat
+verbose: *kati*:   0.007 / 1 uname -sm
+verbose: *kati*: stat time (regen): 0.361907 / 1241
 ```
 
-In this case, the total time spent checking was 1.69 seconds, even though the
+In this case, the total time spent checking was 0.75 seconds, even though the
 other "(regen)" numbers add up to more than that (some parts are parallelized
-where possible). The biggest contributor is the `$(shell)` times -- 184
-executions took a total of 1.31 seconds. The top 10 longest shell functions are
+where possible). Often times, the biggest contributor is the `$(shell)` times
+-- in this case, 66 calls took 0.27s. The top 10 longest shell functions are
 printed.
 
 All the longest commands in this case are all variants of a call to `find`, but
@@ -96,7 +96,8 @@
 $(sort $(shell find device vendor -type -f -a -name \*.pk8 -o -name verifiedboot\* -o -name \*.x509.pem -o -name oem\*.prop))
 ```
 
-Kati is learning about the implicit `-a` in [this change](https://github.com/google/kati/pull/132)
+Kati has now learned about the implicit `-a`, so this particular change is no
+longer necessary, but the basic concept holds.
 
 #### Kati regens too often
 
@@ -113,6 +114,46 @@
 is available when ckati is run with `--regen_debug`, but that can be a lot of
 data to understand.
 
+#### Debugging the slow path
+
+Kati will now dump out information about which Makefiles took the most time to
+execute. This is also in the `verbose.log.gz` file:
+
+```
+verbose: *kati*: included makefiles: 73.640833 / 232810 (1066 unique)
+verbose: *kati*:  18.389 /     1 out/soong/Android-aosp_arm.mk
+verbose: *kati*:  13.137 / 20144 build/make/core/soong_cc_prebuilt.mk
+verbose: *kati*:  11.743 / 27666 build/make/core/base_rules.mk
+verbose: *kati*:   2.289 /     1 art/Android.mk
+verbose: *kati*:   2.054 /     1 art/build/Android.cpplint.mk
+verbose: *kati*:   1.955 / 28269 build/make/core/clear_vars.mk
+verbose: *kati*:   1.795 /   283 build/make/core/package.mk
+verbose: *kati*:   1.790 /   283 build/make/core/package_internal.mk
+verbose: *kati*:   1.757 / 17382 build/make/core/link_type.mk
+verbose: *kati*:   1.078 /   297 build/make/core/aapt2.mk
+```
+
+This shows that soong_cc_prebuilt.mk was included 20144 times, for a total time
+spent of 13.137 secounds. While Android-aosp_arm.mk was only included once, and
+took 18.389 seconds. In this case, Android-aosp_arm.mk is the only file that
+includes soong_cc_prebuilt.mk, so we can safely assume that 13 of the 18 seconds
+in Android-aosp_arm.mk was actually spent within soong_cc_prebuilt.mk (or
+something that it included, like base_rules.mk).
+
+By default this only includes the top 10 entries, but you can ask for the stats
+for any makefile to be printed with `$(KATI_profile_makefile)`:
+
+```
+$(KATI_profile_makefile build/make/core/product.mk)
+```
+
+With these primitives, it's possible to get the timing information for small
+chunks, or even single lines, of a makefile. Just move the lines you want to
+measure into a new makefile, and replace their use with an `include` of the
+new makefile. It's possible to analyze where the time is being spent by doing
+a binary search using this method, but you do need to be careful not to split
+conditionals across two files (the ifeq/else/endif must all be in the same file).
+
 ### Ninja
 
 #### Understanding why something rebuilt
@@ -164,15 +205,14 @@
 
 ### Common
 
-#### mm
+### <= Android 10 (Q): mm
 
 Soong always loads the entire module graph, so as modules convert from Make to
 Soong, `mm` is becoming closer to `mma`. This produces more correct builds, but
 does slow down builds, as we need to verify/produce/load a larger build graph.
 
-We're exploring a few options to speed up build startup, one being [an
-experimental set of ninja patches][ninja parse optimization],
-though that's not the current path we're working towards.
+As of Android Q, loading large build graphs is fast, and in Android R, `mm` is
+now an alias of `mma`.
 
 ### Android 8.1 (Oreo MR1)
 
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/genrule/genrule.go b/genrule/genrule.go
index 57ca9bc..c5aaed2 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -30,10 +30,18 @@
 )
 
 func init() {
-	android.RegisterModuleType("genrule_defaults", defaultsFactory)
+	registerGenruleBuildComponents(android.InitRegistrationContext)
+}
 
-	android.RegisterModuleType("gensrcs", GenSrcsFactory)
-	android.RegisterModuleType("genrule", GenRuleFactory)
+func registerGenruleBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("genrule_defaults", defaultsFactory)
+
+	ctx.RegisterModuleType("gensrcs", GenSrcsFactory)
+	ctx.RegisterModuleType("genrule", GenRuleFactory)
+
+	ctx.FinalDepsMutators(func(ctx android.RegisterMutatorsContext) {
+		ctx.BottomUp("genrule_tool_deps", toolDepsMutator).Parallel()
+	})
 }
 
 var (
@@ -166,7 +174,7 @@
 	return g.outputDeps
 }
 
-func (g *Module) DepsMutator(ctx android.BottomUpMutatorContext) {
+func toolDepsMutator(ctx android.BottomUpMutatorContext) {
 	if g, ok := ctx.Module().(*Module); ok {
 		for _, tool := range g.properties.Tools {
 			tag := hostToolDependencyTag{label: tool}
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index 07de999..7eb43ac 100644
--- a/genrule/genrule_test.go
+++ b/genrule/genrule_test.go
@@ -51,18 +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.FileGroupFactory)
-	ctx.RegisterModuleType("genrule", GenRuleFactory)
-	ctx.RegisterModuleType("gensrcs", GenSrcsFactory)
-	ctx.RegisterModuleType("genrule_defaults", defaultsFactory)
 	ctx.RegisterModuleType("tool", toolFactory)
-	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
-	ctx.Register()
 
+	registerGenruleBuildComponents(ctx)
+
+	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
+	ctx.Register(config)
+
+	return ctx
+}
+
+func testConfig(bp string, fs map[string][]byte) android.Config {
 	bp += `
 		tool {
 			name: "tool",
@@ -104,7 +107,6 @@
 	`
 
 	mockFS := map[string][]byte{
-		"Android.bp": []byte(bp),
 		"tool":       nil,
 		"tool_file1": nil,
 		"tool_file2": nil,
@@ -119,9 +121,7 @@
 		mockFS[k] = v
 	}
 
-	ctx.MockFileSystem(mockFS)
-
-	return ctx
+	return android.TestArchConfig(buildDir, nil, bp, mockFS)
 }
 
 func TestGenruleCmd(t *testing.T) {
@@ -461,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"})
@@ -546,14 +546,14 @@
 
 	for _, test := range testcases {
 		t.Run(test.name, func(t *testing.T) {
-			config := android.TestArchConfig(buildDir, nil)
 			bp := "gensrcs {\n"
 			bp += `name: "gen",` + "\n"
 			bp += `output_extension: "h",` + "\n"
 			bp += test.prop
 			bp += "}\n"
 
-			ctx := testContext(config, bp, nil)
+			config := testConfig(bp, nil)
+			ctx := testContext(config)
 
 			_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
 			if errs == nil {
@@ -595,7 +595,6 @@
 }
 
 func TestGenruleDefaults(t *testing.T) {
-	config := android.TestArchConfig(buildDir, nil)
 	bp := `
 				genrule_defaults {
 					name: "gen_defaults1",
@@ -613,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)
diff --git a/java/aar.go b/java/aar.go
index e0bea5d..ae064e5 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -34,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)
 }
 
 //
@@ -572,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))
diff --git a/java/androidmk.go b/java/androidmk.go
index c973739..04bf15c 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -17,101 +17,109 @@
 import (
 	"fmt"
 	"io"
-	"strings"
 
 	"android/soong/android"
 )
 
-// TODO(jungjw): We'll probably want AndroidMkEntriesProvider.AndroidMkEntries to return multiple
-// entries so that this can be more error-proof.
-func (library *Library) AndroidMkHostDex(w io.Writer, name string, entries *android.AndroidMkEntries) {
-	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(entries.Required) > 0 {
-			fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES :=", strings.Join(entries.Required, " "))
-		}
-		if len(entries.Host_required) > 0 {
-			fmt.Fprintln(w, "LOCAL_HOST_REQUIRED_MODULES :=", strings.Join(entries.Host_required, " "))
-		}
-		if len(entries.Target_required) > 0 {
-			fmt.Fprintln(w, "LOCAL_TARGET_REQUIRED_MODULES :=", strings.Join(entries.Target_required, " "))
-		}
-		if r := library.deviceProperties.Target.Hostdex.Required; len(r) > 0 {
-			fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES +=", strings.Join(r, " "))
-		}
-		fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", library.Stem()+"-hostdex")
-		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) AndroidMkEntries() android.AndroidMkEntries {
-	if !library.IsForPlatform() {
-		return android.AndroidMkEntries{
-			Disabled: true,
+func (library *Library) AndroidMkEntries() []android.AndroidMkEntries {
+	var entriesList []android.AndroidMkEntries
+
+	mainEntries := android.AndroidMkEntries{Disabled: true}
+
+	// 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())
+				},
+			},
 		}
 	}
-	return 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)
+	hostDexEntries := library.AndroidMkEntriesHostDex()
 
-				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())
-			},
-		},
-		ExtraFooters: []android.AndroidMkExtraFootersFunc{
-			func(w io.Writer, name, prefix, moduleDir string, entries *android.AndroidMkEntries) {
-				library.AndroidMkHostDex(w, name, entries)
-			},
-		},
-	}
+	entriesList = append(entriesList, mainEntries, hostDexEntries)
+	return entriesList
 }
 
 // Called for modules that are a component of a test suite.
@@ -124,35 +132,40 @@
 	}
 }
 
-func (j *Test) AndroidMkEntries() android.AndroidMkEntries {
-	entries := j.Library.AndroidMkEntries()
+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 {
 			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")
+		}
 	})
 
-	return entries
+	return entriesList
 }
 
-func (j *TestHelperLibrary) AndroidMkEntries() android.AndroidMkEntries {
-	entries := j.Library.AndroidMkEntries()
+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 entries
+	return entriesList
 }
 
-func (prebuilt *Import) AndroidMkEntries() android.AndroidMkEntries {
+func (prebuilt *Import) AndroidMkEntries() []android.AndroidMkEntries {
 	if !prebuilt.IsForPlatform() || !prebuilt.ContainingSdk().Unversioned() {
-		return android.AndroidMkEntries{
+		return []android.AndroidMkEntries{android.AndroidMkEntries{
 			Disabled: true,
-		}
+		}}
 	}
-	return android.AndroidMkEntries{
+	return []android.AndroidMkEntries{android.AndroidMkEntries{
 		Class:      "JAVA_LIBRARIES",
 		OutputFile: android.OptionalPathForPath(prebuilt.combinedClasspathFile),
 		Include:    "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
@@ -165,16 +178,16 @@
 				entries.SetString("LOCAL_MODULE_STEM", prebuilt.Stem())
 			},
 		},
-	}
+	}}
 }
 
-func (prebuilt *DexImport) AndroidMkEntries() android.AndroidMkEntries {
+func (prebuilt *DexImport) AndroidMkEntries() []android.AndroidMkEntries {
 	if !prebuilt.IsForPlatform() {
-		return android.AndroidMkEntries{
+		return []android.AndroidMkEntries{android.AndroidMkEntries{
 			Disabled: true,
-		}
+		}}
 	}
-	return android.AndroidMkEntries{
+	return []android.AndroidMkEntries{android.AndroidMkEntries{
 		Class:      "JAVA_LIBRARIES",
 		OutputFile: android.OptionalPathForPath(prebuilt.maybeStrippedDexJarFile),
 		Include:    "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
@@ -193,11 +206,11 @@
 				entries.SetString("LOCAL_MODULE_STEM", prebuilt.Stem())
 			},
 		},
-	}
+	}}
 }
 
-func (prebuilt *AARImport) AndroidMkEntries() android.AndroidMkEntries {
-	return android.AndroidMkEntries{
+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",
@@ -213,13 +226,13 @@
 				entries.SetString("LOCAL_SDK_VERSION", prebuilt.sdkVersion())
 			},
 		},
-	}
+	}}
 }
 
-func (binary *Binary) AndroidMkEntries() android.AndroidMkEntries {
+func (binary *Binary) AndroidMkEntries() []android.AndroidMkEntries {
 
 	if !binary.isWrapperVariant {
-		return android.AndroidMkEntries{
+		return []android.AndroidMkEntries{android.AndroidMkEntries{
 			Class:      "JAVA_LIBRARIES",
 			OutputFile: android.OptionalPathForPath(binary.outputFile),
 			Include:    "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
@@ -240,9 +253,9 @@
 					fmt.Fprintln(w, "jar_installed_module := $(LOCAL_INSTALLED_MODULE)")
 				},
 			},
-		}
+		}}
 	} else {
-		return android.AndroidMkEntries{
+		return []android.AndroidMkEntries{android.AndroidMkEntries{
 			Class:      "EXECUTABLES",
 			OutputFile: android.OptionalPathForPath(binary.wrapperFile),
 			ExtraEntries: []android.AndroidMkExtraEntriesFunc{
@@ -257,17 +270,17 @@
 					fmt.Fprintln(w, "jar_installed_module :=")
 				},
 			},
-		}
+		}}
 	}
 }
 
-func (app *AndroidApp) AndroidMkEntries() android.AndroidMkEntries {
+func (app *AndroidApp) AndroidMkEntries() []android.AndroidMkEntries {
 	if !app.IsForPlatform() {
-		return android.AndroidMkEntries{
+		return []android.AndroidMkEntries{android.AndroidMkEntries{
 			Disabled: true,
-		}
+		}}
 	}
-	return android.AndroidMkEntries{
+	return []android.AndroidMkEntries{android.AndroidMkEntries{
 		Class:      "APPS",
 		OutputFile: android.OptionalPathForPath(app.outputFile),
 		Include:    "$(BUILD_SYSTEM)/soong_app_prebuilt.mk",
@@ -359,7 +372,7 @@
 				}
 			},
 		},
-	}
+	}}
 }
 
 func (a *AndroidApp) getOverriddenPackages() []string {
@@ -373,8 +386,9 @@
 	return overridden
 }
 
-func (a *AndroidTest) AndroidMkEntries() android.AndroidMkEntries {
-	entries := a.AndroidApp.AndroidMkEntries()
+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 {
@@ -383,20 +397,22 @@
 		androidMkWriteTestData(a.data, entries)
 	})
 
-	return entries
+	return entriesList
 }
 
-func (a *AndroidTestHelperApp) AndroidMkEntries() android.AndroidMkEntries {
-	entries := a.AndroidApp.AndroidMkEntries()
+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)
 	})
 
-	return entries
+	return entriesList
 }
 
-func (a *AndroidLibrary) AndroidMkEntries() android.AndroidMkEntries {
-	entries := a.Library.AndroidMkEntries()
+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 {
@@ -417,11 +433,11 @@
 		entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", true)
 	})
 
-	return entries
+	return entriesList
 }
 
-func (jd *Javadoc) AndroidMkEntries() android.AndroidMkEntries {
-	return android.AndroidMkEntries{
+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",
@@ -435,11 +451,11 @@
 				}
 			},
 		},
-	}
+	}}
 }
 
-func (ddoc *Droiddoc) AndroidMkEntries() android.AndroidMkEntries {
-	return android.AndroidMkEntries{
+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",
@@ -520,11 +536,11 @@
 				}
 			},
 		},
-	}
+	}}
 }
 
-func (dstubs *Droidstubs) AndroidMkEntries() android.AndroidMkEntries {
-	return android.AndroidMkEntries{
+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",
@@ -600,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())
@@ -636,11 +652,11 @@
 				}
 			},
 		},
-	}
+	}}
 }
 
-func (a *AndroidAppImport) AndroidMkEntries() android.AndroidMkEntries {
-	return android.AndroidMkEntries{
+func (a *AndroidAppImport) AndroidMkEntries() []android.AndroidMkEntries {
+	return []android.AndroidMkEntries{android.AndroidMkEntries{
 		Class:      "APPS",
 		OutputFile: android.OptionalPathForPath(a.outputFile),
 		Include:    "$(BUILD_SYSTEM)/soong_app_prebuilt.mk",
@@ -659,16 +675,17 @@
 				entries.AddStrings("LOCAL_INSTALLED_MODULE_STEM", a.installPath.Rel())
 			},
 		},
-	}
+	}}
 }
 
-func (a *AndroidTestImport) AndroidMkEntries() android.AndroidMkEntries {
-	entries := a.AndroidAppImport.AndroidMkEntries()
+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 entries
+	return entriesList
 }
 
 func androidMkWriteTestData(data android.Paths, entries *android.AndroidMkEntries) {
diff --git a/java/androidmk_test.go b/java/androidmk_test.go
index 438b66a..acc6bf0 100644
--- a/java/androidmk_test.go
+++ b/java/androidmk_test.go
@@ -31,7 +31,7 @@
 	`)
 
 	mod := ctx.ModuleForTests("foo", "android_common").Module()
-	entries := android.AndroidMkEntriesForTest(t, config, "", mod)
+	entries := android.AndroidMkEntriesForTest(t, config, "", mod)[0]
 
 	expected := []string{"libfoo"}
 	actual := entries.EntryMap["LOCAL_REQUIRED_MODULES"]
@@ -50,17 +50,23 @@
 	`)
 
 	mod := ctx.ModuleForTests("foo", "android_common").Module()
-	entries := android.AndroidMkEntriesForTest(t, config, "", mod)
+	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 := entries.EntryMap["LOCAL_MODULE"]
+	actual := mainEntries.EntryMap["LOCAL_MODULE"]
 	if !reflect.DeepEqual(expected, actual) {
 		t.Errorf("Unexpected module name - expected: %q, actual: %q", expected, actual)
 	}
 
-	footerLines := entries.FooterLinesForTests()
-	if !android.InList("LOCAL_MODULE := foo-hostdex", footerLines) {
-		t.Errorf("foo-hostdex is not found in the footers: %q", footerLines)
+	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)
 	}
 }
 
@@ -75,17 +81,23 @@
 	`)
 
 	mod := ctx.ModuleForTests("foo", "android_common").Module()
-	entries := android.AndroidMkEntriesForTest(t, config, "", mod)
+	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 := entries.EntryMap["LOCAL_REQUIRED_MODULES"]
+	actual := mainEntries.EntryMap["LOCAL_REQUIRED_MODULES"]
 	if !reflect.DeepEqual(expected, actual) {
 		t.Errorf("Unexpected required modules - expected: %q, actual: %q", expected, actual)
 	}
 
-	footerLines := entries.FooterLinesForTests()
-	if !android.InList("LOCAL_REQUIRED_MODULES := libfoo", footerLines) {
-		t.Errorf("Wrong or missing required line for foo-hostdex in the footers: %q", footerLines)
+	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)
 	}
 }
 
@@ -104,14 +116,20 @@
 	`)
 
 	mod := ctx.ModuleForTests("foo", "android_common").Module()
-	entries := android.AndroidMkEntriesForTest(t, config, "", mod)
+	entriesList := android.AndroidMkEntriesForTest(t, config, "", mod)
+	if len(entriesList) != 2 {
+		t.Errorf("two entries are expected, but got %d", len(entriesList))
+	}
 
-	if r, ok := entries.EntryMap["LOCAL_REQUIRED_MODULES"]; ok {
+	mainEntries := &entriesList[0]
+	if r, ok := mainEntries.EntryMap["LOCAL_REQUIRED_MODULES"]; ok {
 		t.Errorf("Unexpected required modules: %q", r)
 	}
 
-	footerLines := entries.FooterLinesForTests()
-	if !android.InList("LOCAL_REQUIRED_MODULES += libfoo", footerLines) {
-		t.Errorf("Wrong or missing required line for foo-hostdex in the footers: %q", footerLines)
+	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
index c772e47..e9941f2 100755
--- a/java/app.go
+++ b/java/app.go
@@ -33,18 +33,22 @@
 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("override_android_test", OverrideAndroidTestModuleFactory)
-	android.RegisterModuleType("android_app_import", AndroidAppImportFactory)
-	android.RegisterModuleType("android_test_import", AndroidTestImportFactory)
+	RegisterAppBuildComponents(android.InitRegistrationContext)
 
 	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
 // package splits
 
@@ -167,18 +171,11 @@
 		a.aapt.deps(ctx, sdkDep)
 	}
 
+	tag := &jniDependencyTag{}
 	for _, jniTarget := range ctx.MultiTargets() {
 		variation := append(jniTarget.Variations(),
 			blueprint.Variation{Mutator: "link", Variation: "shared"})
-		tag := &jniDependencyTag{
-			target: jniTarget,
-		}
 		ctx.AddFarVariationDependencies(variation, tag, a.appProperties.Jni_libs...)
-		if String(a.appProperties.Stl) == "c++_shared" {
-			if a.shouldEmbedJnis(ctx) {
-				ctx.AddFarVariationDependencies(variation, tag, "ndk_libc++_shared")
-			}
-		}
 	}
 
 	a.usesLibrary.deps(ctx, sdkDep.hasFrameworkLibs())
@@ -471,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() {
@@ -507,22 +504,33 @@
 	}
 }
 
-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)
@@ -530,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
@@ -630,11 +644,40 @@
 	}
 	a.generateAndroidBuildActions(ctx)
 
-	a.testConfig = tradefed.AutoGenInstrumentationTestConfig(ctx, a.testProperties.Test_config,
+	testConfig := tradefed.AutoGenInstrumentationTestConfig(ctx, a.testProperties.Test_config,
 		a.testProperties.Test_config_template, a.manifestPath, a.testProperties.Test_suites, a.testProperties.Auto_gen_config)
+	a.testConfig = a.FixTestConfig(ctx, testConfig)
 	a.data = android.PathsForModuleSrc(ctx, a.testProperties.Data)
 }
 
+func (a *AndroidTest) FixTestConfig(ctx android.ModuleContext, testConfig android.Path) android.Path {
+	if testConfig == nil {
+		return nil
+	}
+
+	fixedConfig := android.PathForModuleOut(ctx, "test_config_fixer", "AndroidTest.xml")
+	rule := android.NewRuleBuilder()
+	command := rule.Command().BuiltTool(ctx, "test_config_fixer").Input(testConfig).Output(fixedConfig)
+	fixNeeded := false
+
+	if ctx.ModuleName() != a.installApkName {
+		fixNeeded = true
+		command.FlagWithArg("--test-file-name ", a.installApkName+".apk")
+	}
+
+	if a.overridableAppProperties.Package_name != nil {
+		fixNeeded = true
+		command.FlagWithInput("--manifest ", a.manifestPath).
+			FlagWithArg("--package-name ", *a.overridableAppProperties.Package_name)
+	}
+
+	if fixNeeded {
+		rule.Build(pctx, ctx, "fix_test_config", "fix test config")
+		return fixedConfig
+	}
+	return testConfig
+}
+
 func (a *AndroidTest) DepsMutator(ctx android.BottomUpMutatorContext) {
 	a.AndroidApp.DepsMutator(ctx)
 }
@@ -727,6 +770,7 @@
 
 	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
 	android.InitDefaultableModule(module)
+	android.InitApexModule(module)
 	return module
 }
 
@@ -872,7 +916,7 @@
 	MergePropertiesFromVariant(ctx, &a.properties, archProps, archType.Name)
 }
 
-func MergePropertiesFromVariant(ctx android.BaseModuleContext,
+func MergePropertiesFromVariant(ctx android.EarlyModuleContext,
 	dst interface{}, variantGroup reflect.Value, variant string) {
 	src := variantGroup.FieldByName(proptools.FieldNameForProperty(variant))
 	if !src.IsValid() {
@@ -900,6 +944,16 @@
 
 func (a *AndroidAppImport) uncompressEmbeddedJniLibs(
 	ctx android.ModuleContext, inputPath android.Path, outputPath android.OutputPath) {
+	// Test apps don't need their JNI libraries stored uncompressed. As a matter of fact, messing
+	// with them may invalidate pre-existing signature data.
+	if ctx.InstallInTestcases() && Bool(a.properties.Presigned) {
+		ctx.Build(pctx, android.BuildParams{
+			Rule:   android.Cp,
+			Output: outputPath,
+			Input:  inputPath,
+		})
+		return
+	}
 	rule := android.NewRuleBuilder()
 	rule.Command().
 		Textf(`if (zipinfo %s 'lib/*.so' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then`, inputPath).
@@ -957,7 +1011,7 @@
 		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
@@ -977,6 +1031,8 @@
 	var installDir android.InstallPath
 	if Bool(a.properties.Privileged) {
 		installDir = android.PathForModuleInstall(ctx, "priv-app", a.BaseModuleName())
+	} else if ctx.InstallInTestcases() {
+		installDir = android.PathForModuleInstall(ctx, a.BaseModuleName(), ctx.DeviceConfig().DeviceArch())
 	} else {
 		installDir = android.PathForModuleInstall(ctx, "app", a.BaseModuleName())
 	}
@@ -1037,6 +1093,10 @@
 	return a.outputFile
 }
 
+func (a *AndroidAppImport) JacocoReportClassesFile() android.Path {
+	return nil
+}
+
 var dpiVariantGroupType reflect.Type
 var archVariantGroupType reflect.Type
 
@@ -1129,6 +1189,10 @@
 	a.data = android.PathsForModuleSrc(ctx, a.testProperties.Data)
 }
 
+func (a *AndroidTestImport) InstallInTestcases() bool {
+	return true
+}
+
 // 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 {
diff --git a/java/app_builder.go b/java/app_builder.go
index ec2f6da..5e7fbe6 100644
--- a/java/app_builder.go
+++ b/java/app_builder.go
@@ -200,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 fc8cf8e..6f89da4 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -43,7 +43,7 @@
 	}
 )
 
-func testAppContext(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(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(bp, nil)
+	ctx := testContext()
 
 	run(t, ctx, config)
 
@@ -175,6 +175,95 @@
 	`)
 }
 
+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
@@ -212,8 +301,8 @@
 
 	for _, testCase := range testCases {
 		t.Run(testCase.name, func(t *testing.T) {
-			config := testConfig(nil)
-			ctx := testContext(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")
@@ -420,7 +509,7 @@
 
 	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 {
@@ -430,7 +519,7 @@
 				config.TestProductVariables.EnforceRROExcludedOverlays = testCase.enforceRROExcludedOverlays
 			}
 
-			ctx := testAppContext(bp, fs)
+			ctx := testContext()
 			run(t, ctx, config)
 
 			resourceListToFiles := func(module android.TestingModule, list []string) (files []string) {
@@ -560,12 +649,12 @@
 					%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(bp, nil)
+				ctx := testContext()
 
 				run(t, ctx, config)
 
@@ -688,9 +777,6 @@
 	`)
 
 	for _, enforce := range []bool{true, false} {
-
-		config := testConfig(nil)
-		config.TestProductVariables.EnforceProductPartitionInterface = proptools.BoolPtr(enforce)
 		bp := `
 			android_app {
 				name: "foo",
@@ -699,10 +785,13 @@
 				platform_apis: true,
 			}
 		`
+
+		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", bp, config)
+			testJavaErrorWithConfig(t, "sdk_version must have a value when the module is located at vendor or product", config)
 		} else {
-			testJavaWithConfig(t, bp, config)
+			testJavaWithConfig(t, config)
 		}
 	}
 }
@@ -865,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(test.bp, nil)
+			ctx := testContext()
 
 			run(t, ctx, config)
 			foo := ctx.ModuleForTests("foo", "android_common")
@@ -925,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(test.bp, nil)
+			ctx := testContext()
 
 			run(t, ctx, config)
 			foo := ctx.ModuleForTests("foo", "android_common")
@@ -962,9 +1051,9 @@
 			sdk_version: "current",
 		}
 		`
-	config := testConfig(nil)
+	config := testAppConfig(nil, bp, nil)
 	config.TestProductVariables.ManifestPackageNameOverrides = []string{"foo:org.dandroid.bp"}
-	ctx := testAppContext(bp, nil)
+	ctx := testContext()
 
 	run(t, ctx, config)
 
@@ -1216,6 +1305,87 @@
 	}
 }
 
+func TestAndroidTest_FixTestConfig(t *testing.T) {
+	ctx, _ := testJava(t, `
+		android_app {
+			name: "foo",
+			srcs: ["a.java"],
+			package_name: "com.android.foo",
+			sdk_version: "current",
+		}
+
+		android_test {
+			name: "foo_test",
+			srcs: ["b.java"],
+			instrumentation_for: "foo",
+		}
+
+		android_test {
+			name: "bar_test",
+			srcs: ["b.java"],
+			package_name: "com.android.bar.test",
+			instrumentation_for: "foo",
+		}
+
+		override_android_test {
+			name: "baz_test",
+			base: "foo_test",
+			package_name: "com.android.baz.test",
+		}
+		`)
+
+	testCases := []struct {
+		moduleName    string
+		variantName   string
+		expectedFlags []string
+	}{
+		{
+			moduleName:  "foo_test",
+			variantName: "android_common",
+		},
+		{
+			moduleName:  "bar_test",
+			variantName: "android_common",
+			expectedFlags: []string{
+				"--manifest " + buildDir + "/.intermediates/bar_test/android_common/manifest_fixer/AndroidManifest.xml",
+				"--package-name com.android.bar.test",
+			},
+		},
+		{
+			moduleName:  "foo_test",
+			variantName: "android_common_baz_test",
+			expectedFlags: []string{
+				"--manifest " + buildDir +
+					"/.intermediates/foo_test/android_common_baz_test/manifest_fixer/AndroidManifest.xml",
+				"--package-name com.android.baz.test",
+				"--test-file-name baz_test.apk",
+			},
+		},
+	}
+
+	for _, test := range testCases {
+		variant := ctx.ModuleForTests(test.moduleName, test.variantName)
+		params := variant.MaybeOutput("test_config_fixer/AndroidTest.xml")
+
+		if len(test.expectedFlags) > 0 {
+			if params.Rule == nil {
+				t.Errorf("test_config_fixer was expected to run, but didn't")
+			} else {
+				for _, flag := range test.expectedFlags {
+					if !strings.Contains(params.RuleParams.Command, flag) {
+						t.Errorf("Flag %q was not found in command: %q", flag, params.RuleParams.Command)
+					}
+				}
+			}
+		} else {
+			if params.Rule != nil {
+				t.Errorf("test_config_fixer was not expected to run, but did: %q", params.RuleParams.Command)
+			}
+		}
+
+	}
+}
+
 func TestAndroidAppImport(t *testing.T) {
 	ctx, _ := testJava(t, `
 		android_app_import {
@@ -1382,10 +1552,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(bp, nil)
+		ctx := testContext()
 
 		run(t, ctx, config)
 
@@ -1440,7 +1610,7 @@
 		a := variant.Module().(*AndroidAppImport)
 		expectedValues := []string{test.expected}
 		actualValues := android.AndroidMkEntriesForTest(
-			t, config, "", a).EntryMap["LOCAL_INSTALLED_MODULE_STEM"]
+			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)
@@ -1526,7 +1696,7 @@
 	test := ctx.ModuleForTests("foo", "android_common").Module().(*AndroidTestImport)
 
 	// Check android mks.
-	entries := android.AndroidMkEntriesForTest(t, config, "", test)
+	entries := android.AndroidMkEntriesForTest(t, config, "", test)[0]
 	expected := []string{"tests"}
 	actual := entries.EntryMap["LOCAL_MODULE_TAGS"]
 	if !reflect.DeepEqual(expected, actual) {
@@ -1539,10 +1709,82 @@
 	}
 }
 
+func TestAndroidTestImport_NoJinUncompressForPresigned(t *testing.T) {
+	ctx, _ := testJava(t, `
+		android_test_import {
+			name: "foo",
+			apk: "prebuilts/apk/app.apk",
+			certificate: "cert/new_cert",
+			data: [
+				"testdata/data",
+			],
+		}
+
+		android_test_import {
+			name: "foo_presigned",
+			apk: "prebuilts/apk/app.apk",
+			presigned: true,
+			data: [
+				"testdata/data",
+			],
+		}
+		`)
+
+	variant := ctx.ModuleForTests("foo", "android_common")
+	jniRule := variant.Output("jnis-uncompressed/foo.apk").RuleParams.Command
+	if !strings.HasPrefix(jniRule, "if (zipinfo") {
+		t.Errorf("Unexpected JNI uncompress rule command: " + jniRule)
+	}
+
+	variant = ctx.ModuleForTests("foo_presigned", "android_common")
+	jniRule = variant.Output("jnis-uncompressed/foo_presigned.apk").BuildParams.Rule.String()
+	if jniRule != android.Cp.String() {
+		t.Errorf("Unexpected JNI uncompress rule: " + jniRule)
+	}
+}
+
 func TestStl(t *testing.T) {
 	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 {
@@ -1643,10 +1885,10 @@
 		}
 	`
 
-	config := testConfig(nil)
+	config := testAppConfig(nil, bp, nil)
 	config.TestProductVariables.MissingUsesLibraries = []string{"baz"}
 
-	ctx := testAppContext(bp, nil)
+	ctx := testContext()
 
 	run(t, ctx, config)
 
@@ -1917,12 +2159,12 @@
 	test := func(t *testing.T, bp string, want bool, unbundled bool) {
 		t.Helper()
 
-		config := testConfig(nil)
+		config := testAppConfig(nil, bp, nil)
 		if unbundled {
 			config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true)
 		}
 
-		ctx := testAppContext(bp, nil)
+		ctx := testContext()
 
 		run(t, ctx, config)
 
diff --git a/java/builder.go b/java/builder.go
index 417a7fa..26a49ea 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -64,6 +64,8 @@
 
 	_ = pctx.VariableFunc("kytheCorpus",
 		func(ctx android.PackageVarContext) string { return ctx.Config().XrefCorpusName() })
+	_ = pctx.VariableFunc("kytheCuEncoding",
+		func(ctx android.PackageVarContext) string { return ctx.Config().XrefCuEncoding() })
 	_ = 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 ...
@@ -76,6 +78,7 @@
 				`KYTHE_ROOT_DIRECTORY=. KYTHE_OUTPUT_FILE=$out ` +
 				`KYTHE_CORPUS=${kytheCorpus} ` +
 				`KYTHE_VNAMES=${kytheVnames} ` +
+				`KYTHE_KZIP_ENCODING=${kytheCuEncoding} ` +
 				`${config.SoongJavacWrapper} ${config.JavaCmd} ` +
 				`--add-opens=java.base/java.nio=ALL-UNNAMED ` +
 				`-jar ${config.JavaKytheExtractorJar} ` +
diff --git a/java/config/config.go b/java/config/config.go
index 333de32..6da7279 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -29,7 +29,7 @@
 
 	DefaultBootclasspathLibraries = []string{"core.platform.api.stubs", "core-lambda-stubs"}
 	DefaultSystemModules          = "core-platform-api-stubs-system-modules"
-	DefaultLibraries              = []string{"ext", "framework", "updatable_media_stubs"}
+	DefaultLibraries              = []string{"ext", "framework"}
 	DefaultLambdaStubsLibrary     = "core-lambda-stubs"
 	SdkLambdaStubsPath            = "prebuilts/sdk/tools/core-lambda-stubs.jar"
 
@@ -45,7 +45,10 @@
 		"core-icu4j",
 		"core-oj",
 		"core-libart",
+		// TODO: Could this be all updatable bootclasspath jars?
 		"updatable-media",
+		"framework-sdkextensions",
+		"ike",
 	}
 )
 
@@ -124,7 +127,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()
 		}
 	})
 
@@ -170,7 +173,7 @@
 		if ctx.Config().UnbundledBuild() || ctx.Config().IsPdkBuild() {
 			return filepath.Join("prebuilts/sdk/tools", runtime.GOOS, "bin", tool)
 		} else {
-			return pctx.HostBinToolPath(ctx, tool).String()
+			return ctx.Config().HostToolPath(ctx, tool).String()
 		}
 	})
 }
@@ -180,7 +183,7 @@
 		if ctx.Config().UnbundledBuild() || ctx.Config().IsPdkBuild() {
 			return filepath.Join("prebuilts/sdk/tools/lib", tool+".jar")
 		} else {
-			return pctx.HostJavaToolPath(ctx, tool+".jar").String()
+			return ctx.Config().HostJavaToolPath(ctx, tool+".jar").String()
 		}
 	})
 }
@@ -194,7 +197,7 @@
 			}
 			return filepath.Join("prebuilts/sdk/tools", runtime.GOOS, "lib64", tool+ext)
 		} else {
-			return pctx.HostJNIToolPath(ctx, tool).String()
+			return ctx.Config().HostJNIToolPath(ctx, tool).String()
 		}
 	})
 }
@@ -204,7 +207,7 @@
 		if ctx.Config().UnbundledBuild() || ctx.Config().IsPdkBuild() {
 			return filepath.Join("prebuilts/build-tools", ctx.Config().PrebuiltOS(), "bin", tool)
 		} else {
-			return pctx.HostBinToolPath(ctx, tool).String()
+			return ctx.Config().HostToolPath(ctx, tool).String()
 		}
 	})
 }
diff --git a/java/device_host_converter.go b/java/device_host_converter.go
index 1524418..b40ab93 100644
--- a/java/device_host_converter.go
+++ b/java/device_host_converter.go
@@ -170,6 +170,10 @@
 	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/dexpreopt.go b/java/dexpreopt.go
index 479dec6..da68660 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -85,6 +85,11 @@
 		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
@@ -101,9 +106,8 @@
 
 	global := dexpreoptGlobalConfig(ctx)
 	bootImage := defaultBootImageConfig(ctx)
-	defaultBootImage := bootImage
 	if global.UseApexImage {
-		bootImage = apexBootImageConfig(ctx)
+		bootImage = frameworkJZBootImageConfig(ctx)
 	}
 
 	var archs []android.ArchType
@@ -174,11 +178,8 @@
 		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.dexPathsDeps.Paths(),
-		PreoptBootClassPathDexLocations: defaultBootImage.dexLocationsDeps,
+		PreoptBootClassPathDexFiles:     bootImage.dexPathsDeps.Paths(),
+		PreoptBootClassPathDexLocations: bootImage.dexLocationsDeps,
 
 		PreoptExtractedApk: false,
 
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index 280ce8c..66840b5 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -220,7 +220,8 @@
 	d.otherImages = append(d.otherImages, buildBootImage(ctx, artBootImageConfig(ctx)))
 	if global.GenerateApexImage {
 		// Create boot images for the JIT-zygote experiment.
-		d.otherImages = append(d.otherImages, buildBootImage(ctx, apexBootImageConfig(ctx)))
+		d.otherImages = append(d.otherImages, buildBootImage(ctx, artJZBootImageConfig(ctx)))
+		d.otherImages = append(d.otherImages, buildBootImage(ctx, frameworkJZBootImageConfig(ctx)))
 	}
 
 	dumpOatRules(ctx, d.defaultBootImage)
@@ -327,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).
@@ -436,7 +437,6 @@
 		return nil
 	}
 	profile := ctx.Config().Once(bootImageProfileRuleKey, func() interface{} {
-		tools := global.Tools
 		defaultProfile := "frameworks/base/config/boot-image-profile.txt"
 
 		rule := android.NewRuleBuilder()
@@ -462,7 +462,7 @@
 
 		rule.Command().
 			Text(`ANDROID_LOG_TAGS="*:e"`).
-			Tool(tools.Profman).
+			Tool(global.SoongConfig.Profman).
 			FlagWithInput("--create-profile-from=", bootImageProfile).
 			FlagForEachInput("--apk=", image.dexPathsDeps.Paths()).
 			FlagForEachArg("--dex-location=", image.dexLocationsDeps).
@@ -491,8 +491,6 @@
 		return nil
 	}
 	return ctx.Config().Once(bootFrameworkProfileRuleKey, func() interface{} {
-		tools := global.Tools
-
 		rule := android.NewRuleBuilder()
 		rule.MissingDeps(missingDeps)
 
@@ -513,7 +511,7 @@
 
 		rule.Command().
 			Text(`ANDROID_LOG_TAGS="*:e"`).
-			Tool(tools.Profman).
+			Tool(global.SoongConfig.Profman).
 			Flag("--generate-boot-profile").
 			FlagWithInput("--create-profile-from=", bootFrameworkProfile).
 			FlagForEachInput("--apk=", image.dexPathsDeps.Paths()).
@@ -547,7 +545,7 @@
 			BuiltTool(ctx, "oatdumpd").
 			FlagWithInputList("--runtime-arg -Xbootclasspath:", image.dexPathsDeps.Paths(), ":").
 			FlagWithList("--runtime-arg -Xbootclasspath-locations:", image.dexLocationsDeps, ":").
-			FlagWithArg("--image=", dexpreopt.PathToLocation(image.images[arch], arch)).Implicit(image.images[arch]).
+			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())
@@ -590,6 +588,7 @@
 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
@@ -597,7 +596,6 @@
 		ctx.Strict("DEXPREOPT_IMAGE_PROFILE_BUILT_INSTALLED", image.profileInstalls.String())
 		ctx.Strict("DEXPREOPT_BOOTCLASSPATH_DEX_FILES", strings.Join(image.dexPathsDeps.Strings(), " "))
 		ctx.Strict("DEXPREOPT_BOOTCLASSPATH_DEX_LOCATIONS", strings.Join(image.dexLocationsDeps, " "))
-		ctx.Strict("DEXPREOPT_IMAGE_LOCATIONS", strings.Join(image.imageLocations, ":"))
 
 		var imageNames []string
 		for _, current := range append(d.otherImages, image) {
@@ -610,15 +608,15 @@
 			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 87d5e30..4ce30f6 100644
--- a/java/dexpreopt_bootjars_test.go
+++ b/java/dexpreopt_bootjars_test.go
@@ -44,14 +44,14 @@
 		}
 	`
 
-	config := testConfig(nil)
+	config := testConfig(nil, bp, nil)
 
-	pathCtx := android.PathContextForTesting(config, nil)
+	pathCtx := android.PathContextForTesting(config)
 	dexpreoptConfig := dexpreopt.GlobalConfigForTests(pathCtx)
 	dexpreoptConfig.BootJars = []string{"foo", "bar", "baz"}
 	setDexpreoptTestGlobalConfig(config, dexpreoptConfig)
 
-	ctx := testContext(bp, nil)
+	ctx := testContext()
 
 	ctx.RegisterSingletonType("dex_bootjars", dexpreoptBootJarsFactory)
 
diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go
index fd1cfd4..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)
 			}
@@ -120,10 +122,11 @@
 }
 
 var (
-	bootImageConfigKey     = android.NewOnceKey("bootImageConfig")
-	artBootImageName       = "art"
-	frameworkBootImageName = "boot"
-	apexBootImageName      = "apex"
+	bootImageConfigKey       = android.NewOnceKey("bootImageConfig")
+	artBootImageName         = "art"
+	frameworkBootImageName   = "boot"
+	artJZBootImageName       = "jitzygote-art"
+	frameworkJZBootImageName = "jitzygote-boot"
 )
 
 // Construct the global boot image configs.
@@ -135,7 +138,7 @@
 		deviceDir := android.PathForOutput(ctx, ctx.Config().DeviceName())
 
 		artModules := global.ArtApexJars
-		// In coverage builds ART boot class path jars are instrumented and have additional dependencies.
+		// With EMMA_INSTRUMENT_FRAMEWORK=true the Core libraries depend on jacoco.
 		if ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") {
 			artModules = append(artModules, "jacocoagent")
 		}
@@ -177,22 +180,33 @@
 			dexLocationsDeps: append(artLocations, frameworkLocations...),
 		}
 
-		// Apex config for the  boot image used in the JIT-zygote experiment.
-		// It includes both the Core libraries and framework.
-		apexCfg := bootImageConfig{
+		// ART config for JIT-zygote boot image.
+		artJZCfg := bootImageConfig{
 			extension:        false,
-			name:             apexBootImageName,
+			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:          concat(artModules, frameworkModules),
-			dexLocations:     concat(artLocations, frameworkLocations),
-			dexLocationsDeps: concat(artLocations, frameworkLocations),
+			modules:          frameworkModules,
+			dexLocations:     frameworkLocations,
+			dexLocationsDeps: append(artLocations, frameworkLocations...),
 		}
 
 		configs := map[string]*bootImageConfig{
-			artBootImageName:       &artCfg,
-			frameworkBootImageName: &frameworkCfg,
-			apexBootImageName:      &apexCfg,
+			artBootImageName:         &artCfg,
+			frameworkBootImageName:   &frameworkCfg,
+			artJZBootImageName:       &artJZCfg,
+			frameworkJZBootImageName: &frameworkJZCfg,
 		}
 
 		// common to all configs
@@ -234,6 +248,10 @@
 		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)
 }
@@ -246,8 +264,12 @@
 	return *genBootImageConfigs(ctx)[frameworkBootImageName]
 }
 
-func apexBootImageConfig(ctx android.PathContext) bootImageConfig {
-	return *genBootImageConfigs(ctx)[apexBootImageName]
+func artJZBootImageConfig(ctx android.PathContext) bootImageConfig {
+	return *genBootImageConfigs(ctx)[artJZBootImageName]
+}
+
+func frameworkJZBootImageConfig(ctx android.PathContext) bootImageConfig {
+	return *genBootImageConfigs(ctx)[frameworkJZBootImageName]
 }
 
 func defaultBootclasspath(ctx android.PathContext) []string {
diff --git a/java/droiddoc.go b/java/droiddoc.go
index 16e6921..f62f5f9 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -19,6 +19,7 @@
 	"path/filepath"
 	"strings"
 
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
@@ -26,19 +27,37 @@
 )
 
 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)
 
-	android.RegisterModuleType("prebuilt_stubs_sources", PrebuiltStubsSourcesFactory)
+	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 (
@@ -774,7 +793,7 @@
 		if t, ok := m.(*ExportedDroiddocDir); ok {
 			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))
 		}
 	})
 
@@ -1932,12 +1951,42 @@
 
 	properties PrebuiltStubsSourcesProperties
 
-	srcs        android.Paths
+	// 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.srcs = android.PathsForModuleSrc(ctx, p.properties.Srcs)
+	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 {
@@ -1948,10 +1997,6 @@
 	return p.prebuilt.Name(p.ModuleBase.Name())
 }
 
-func (p *PrebuiltStubsSources) Srcs() android.Paths {
-	return append(android.Paths{}, p.srcs...)
-}
-
 // prebuilt_stubs_sources imports a set of java source files as if they were
 // generated by droidstubs.
 //
@@ -1973,12 +2018,31 @@
 	return module
 }
 
-func (d *Droidstubs) BuildSnapshot(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder) {
+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(sdkModuleContext.OtherModuleName(d), "prebuilt_stubs_sources")
+	pbm := builder.AddPrebuiltModule(member, "prebuilt_stubs_sources")
 	pbm.AddProperty("srcs", []string{snapshotRelativeDir})
 }
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 c0ef444..ad84cde 100644
--- a/java/hiddenapi_singleton.go
+++ b/java/hiddenapi_singleton.go
@@ -190,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, ":").
diff --git a/java/java.go b/java/java.go
index 9c0fcba..4c6a5a5 100644
--- a/java/java.go
+++ b/java/java.go
@@ -34,24 +34,53 @@
 )
 
 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.RegisterSingletonType("kythe_java_extract", kytheExtractJavaFactory)
+	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) {
@@ -405,10 +434,6 @@
 	}
 }
 
-func (j *Module) DexJarFile() android.Path {
-	return j.dexJarFile
-}
-
 var _ android.OutputFileProducer = (*Module)(nil)
 
 type Dependency interface {
@@ -422,6 +447,7 @@
 	ExportedPlugins() (android.Paths, []string)
 	SrcJarArgs() ([]string, android.Paths)
 	BaseModuleName() string
+	JacocoReportClassesFile() android.Path
 }
 
 type SdkLibraryDependency interface {
@@ -449,7 +475,6 @@
 
 type jniDependencyTag struct {
 	blueprint.BaseDependencyTag
-	target android.Target
 }
 
 func IsJniDepTag(depTag blueprint.DependencyTag) bool {
@@ -475,6 +500,14 @@
 	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
 
@@ -542,6 +575,16 @@
 	return j.sdkVersion()
 }
 
+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))
@@ -568,8 +611,33 @@
 		}
 	}
 
-	ctx.AddVariationDependencies(nil, libTag, j.properties.Libs...)
-	ctx.AddVariationDependencies(nil, staticLibTag, j.properties.Static_libs...)
+	syspropPublicStubs := syspropPublicStubs(ctx.Config())
+
+	// 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...)
@@ -589,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")
 	}
 }
@@ -792,8 +867,7 @@
 			return
 		}
 		switch module.(type) {
-		case *Library:
-		case *AndroidLibrary:
+		case *Library, *AndroidLibrary:
 			if to, ok := module.(linkTypeContext); ok {
 				switch tag {
 				case bootClasspathTag, libTag, staticLibTag:
@@ -1393,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)
 	}
@@ -1658,6 +1726,10 @@
 	return proptools.StringDefault(j.deviceProperties.Stem, j.Name())
 }
 
+func (j *Module) JacocoReportClassesFile() android.Path {
+	return j.jacocoReportClassesFile
+}
+
 //
 // Java libraries (.jar file)
 //
@@ -1711,23 +1783,53 @@
 }
 
 const (
-	aidlIncludeDir     = "aidl"
-	javaStubDir        = "java"
-	javaStubFileSuffix = ".jar"
+	aidlIncludeDir   = "aidl"
+	javaDir          = "java"
+	jarFileSuffix    = ".jar"
+	testConfigSuffix = "-AndroidTest.xml"
 )
 
-// path to the stub file of a java library. Relative to <sdk_root>/<api_dir>
-func (j *Library) javaStubFilePathFor() string {
-	return filepath.Join(javaStubDir, j.Name()+javaStubFileSuffix)
+// 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 (j *Library) BuildSnapshot(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder) {
-	headerJars := j.HeaderJars()
-	if len(headerJars) != 1 {
-		panic(fmt.Errorf("there must be only one header jar from %q", j.Name()))
+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)
+		}
 	}
-	snapshotRelativeJavaLibPath := j.javaStubFilePathFor()
-	builder.CopyToSnapshot(headerJars[0], snapshotRelativeJavaLibPath)
+	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
@@ -1737,10 +1839,40 @@
 		}
 	}
 
-	module := builder.AddPrebuiltModule(sdkModuleContext.OtherModuleName(j), "java_import")
+	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
@@ -1823,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
 
@@ -1838,6 +1980,14 @@
 	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.testProperties.Auto_gen_config)
@@ -1850,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.
 //
@@ -1893,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.
 //
@@ -2085,6 +2306,10 @@
 	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...)
 }
@@ -2394,10 +2619,9 @@
 	return DefaultsFactory()
 }
 
-func DefaultsFactory(props ...interface{}) android.Module {
+func DefaultsFactory() android.Module {
 	module := &Defaults{}
 
-	module.AddProperties(props...)
 	module.AddProperties(
 		&CompilerProperties{},
 		&CompilerDeviceProperties{},
diff --git a/java/java_test.go b/java/java_test.go
index dc498a4..a2788cb 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -56,168 +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(bp string, fs map[string][]byte) *android.TestContext {
+func testContext() *android.TestContext {
 
 	ctx := android.NewTestArchContext()
-	ctx.RegisterModuleType("android_app", AndroidAppFactory)
-	ctx.RegisterModuleType("android_app_certificate", AndroidAppCertificateFactory)
-	ctx.RegisterModuleType("android_app_import", AndroidAppImportFactory)
-	ctx.RegisterModuleType("android_library", AndroidLibraryFactory)
-	ctx.RegisterModuleType("android_test", AndroidTestFactory)
-	ctx.RegisterModuleType("android_test_helper_app", AndroidTestHelperAppFactory)
-	ctx.RegisterModuleType("android_test_import", AndroidTestImportFactory)
-	ctx.RegisterModuleType("java_binary", BinaryFactory)
-	ctx.RegisterModuleType("java_binary_host", BinaryHostFactory)
-	ctx.RegisterModuleType("java_device_for_host", DeviceForHostFactory)
-	ctx.RegisterModuleType("java_host_for_device", HostForDeviceFactory)
-	ctx.RegisterModuleType("java_library", LibraryFactory)
-	ctx.RegisterModuleType("java_library_host", LibraryHostFactory)
-	ctx.RegisterModuleType("java_test", TestFactory)
-	ctx.RegisterModuleType("java_import", ImportFactory)
-	ctx.RegisterModuleType("java_import_host", ImportFactoryHost)
-	ctx.RegisterModuleType("java_defaults", defaultsFactory)
-	ctx.RegisterModuleType("java_system_modules", SystemModulesFactory)
-	ctx.RegisterModuleType("java_genrule", genRuleFactory)
+	RegisterJavaBuildComponents(ctx)
+	RegisterAppBuildComponents(ctx)
+	RegisterAARBuildComponents(ctx)
+	RegisterGenRuleBuildComponents(ctx)
+	RegisterSystemModulesBuildComponents(ctx)
 	ctx.RegisterModuleType("java_plugin", PluginFactory)
-	ctx.RegisterModuleType("dex_import", DexImportFactory)
 	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
 	ctx.RegisterModuleType("genrule", genrule.GenRuleFactory)
-	ctx.RegisterModuleType("droiddoc", DroiddocFactory)
-	ctx.RegisterModuleType("droiddoc_host", DroiddocHostFactory)
-	ctx.RegisterModuleType("droiddoc_template", ExportedDroiddocDirFactory)
-	ctx.RegisterModuleType("prebuilt_stubs_sources", PrebuiltStubsSourcesFactory)
-	ctx.RegisterModuleType("java_sdk_library", SdkLibraryFactory)
-	ctx.RegisterModuleType("java_sdk_library_import", sdkLibraryImportFactory)
-	ctx.RegisterModuleType("override_android_app", OverrideAndroidAppModuleFactory)
-	ctx.RegisterModuleType("override_android_test", OverrideAndroidTestModuleFactory)
-	ctx.RegisterModuleType("prebuilt_apis", PrebuiltApisFactory)
-	ctx.PreArchMutators(android.RegisterPrebuiltsPreArchMutators)
-	ctx.PreArchMutators(android.RegisterPrebuiltsPostDepsMutators)
+	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", cc.LibraryFactory)
-	ctx.RegisterModuleType("cc_object", cc.ObjectFactory)
-	ctx.RegisterModuleType("toolchain_library", cc.ToolchainLibraryFactory)
-	ctx.RegisterModuleType("llndk_library", cc.LlndkLibraryFactory)
+	cc.RegisterRequiredBuildComponentsForTest(ctx)
 	ctx.RegisterModuleType("ndk_prebuilt_shared_stl", 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,
-		"APP_NOTICE":             nil,
-		"GENRULE_NOTICE":         nil,
-		"LIB_NOTICE":             nil,
-		"TOOL_NOTICE":            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
-	}
-
-	ctx.MockFileSystem(mockFS)
 
 	return ctx
 }
@@ -225,10 +92,10 @@
 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)
@@ -237,17 +104,17 @@
 
 func testJavaError(t *testing.T, pattern string, bp string) (*android.TestContext, android.Config) {
 	t.Helper()
-	return testJavaErrorWithConfig(t, pattern, bp, testConfig(nil))
+	return testJavaErrorWithConfig(t, pattern, testConfig(nil, bp, nil))
 }
 
-func testJavaErrorWithConfig(t *testing.T, pattern string, bp string, config android.Config) (*android.TestContext, android.Config) {
+func testJavaErrorWithConfig(t *testing.T, pattern string, config android.Config) (*android.TestContext, android.Config) {
 	t.Helper()
-	ctx := testContext(bp, nil)
+	ctx := testContext()
 
-	pathCtx := android.PathContextForTesting(config, nil)
+	pathCtx := android.PathContextForTesting(config)
 	setDexpreoptTestGlobalConfig(config, dexpreopt.GlobalConfigForTests(pathCtx))
 
-	ctx.Register()
+	ctx.Register(config)
 	_, errs := ctx.ParseBlueprintsFiles("Android.bp")
 	if len(errs) > 0 {
 		android.FailIfNoMatchingErrors(t, pattern, errs)
@@ -266,12 +133,12 @@
 
 func testJava(t *testing.T, bp string) (*android.TestContext, android.Config) {
 	t.Helper()
-	return testJavaWithConfig(t, bp, testConfig(nil))
+	return testJavaWithConfig(t, testConfig(nil, bp, nil))
 }
 
-func testJavaWithConfig(t *testing.T, bp string, config android.Config) (*android.TestContext, android.Config) {
+func testJavaWithConfig(t *testing.T, config android.Config) (*android.TestContext, android.Config) {
 	t.Helper()
-	ctx := testContext(bp, nil)
+	ctx := testContext()
 	run(t, ctx, config)
 
 	return ctx, config
@@ -288,6 +155,94 @@
 	}
 }
 
+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, `
 		java_library {
@@ -432,9 +387,6 @@
 	`)
 
 	for _, enforce := range []bool{true, false} {
-
-		config := testConfig(nil)
-		config.TestProductVariables.EnforceProductPartitionInterface = proptools.BoolPtr(enforce)
 		bp := `
 			java_library {
 				name: "foo",
@@ -442,10 +394,13 @@
 				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", bp, config)
+			testJavaErrorWithConfig(t, "sdk_version must have a value when the module is located at vendor or product", config)
 		} else {
-			testJavaWithConfig(t, bp, config)
+			testJavaWithConfig(t, config)
 		}
 	}
 }
@@ -529,28 +484,31 @@
 
 		prebuilt_stubs_sources {
 			name: "stubs-source",
-			srcs: ["stubs/sources/**/*.java"],
+			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
 
-	inputs := []string{}
-	for _, p := range javac.BuildParams.Inputs {
-		inputs = append(inputs, p.String())
-	}
+	fooLibrary := fooModule.Module().(*Library)
+	assertDeepEquals(t, "foo java sources incorrect",
+		[]string{"a.java"}, fooLibrary.compiledJavaSrcs.Strings())
 
-	expected := []string{
-		"a.java",
-		"stubs/sources/foo/Foo.java",
-	}
-	if !reflect.DeepEqual(expected, inputs) {
-		t.Errorf("foo inputs incorrect: expected %q, found %q", expected, inputs)
-	}
+	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())
@@ -567,6 +525,12 @@
 	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, `
 		java_defaults {
@@ -907,7 +871,7 @@
 
 func TestDroiddoc(t *testing.T) {
 	ctx, _ := testJava(t, `
-		droiddoc_template {
+		droiddoc_exported_dir {
 		    name: "droiddoc-templates-sdk",
 		    path: ".",
 		}
@@ -1039,8 +1003,7 @@
 }
 
 func TestJavaLibrary(t *testing.T) {
-	config := testConfig(nil)
-	ctx := testContext("", map[string][]byte{
+	config := testConfig(nil, "", map[string][]byte{
 		"libcore/Android.bp": []byte(`
 				java_library {
 						name: "core",
@@ -1048,12 +1011,13 @@
 						system_modules: "none",
 				}`),
 	})
+	ctx := testContext()
 	run(t, ctx, config)
 }
 
 func TestJavaSdkLibrary(t *testing.T) {
 	ctx, _ := testJava(t, `
-		droiddoc_template {
+		droiddoc_exported_dir {
 			name: "droiddoc-templates-sdk",
 			path: ".",
 		}
@@ -1259,7 +1223,71 @@
 		checkPatchModuleFlag(t, ctx, "foo", "")
 		expected := "java.base=.:" + buildDir
 		checkPatchModuleFlag(t, ctx, "bar", expected)
-		expected = "java.base=" + strings.Join([]string{".", buildDir, moduleToPath("ext"), moduleToPath("framework"), moduleToPath("updatable_media_stubs")}, ":")
+		expected = "java.base=" + strings.Join([]string{".", buildDir, moduleToPath("ext"), moduleToPath("framework")}, ":")
 		checkPatchModuleFlag(t, ctx, "baz", expected)
 	})
 }
+
+func TestJavaSystemModules(t *testing.T) {
+	ctx, _ := testJava(t, `
+		java_system_modules {
+			name: "system-modules",
+			libs: ["system-module1", "system-module2"],
+		}
+		java_library {
+			name: "system-module1",
+			srcs: ["a.java"],
+			sdk_version: "none",
+			system_modules: "none",
+		}
+		java_library {
+			name: "system-module2",
+			srcs: ["b.java"],
+			sdk_version: "none",
+			system_modules: "none",
+		}
+		`)
+
+	// check the existence of the module
+	systemModules := ctx.ModuleForTests("system-modules", "android_common")
+
+	cmd := systemModules.Rule("jarsTosystemModules")
+
+	// make sure the command compiles against the supplied modules.
+	for _, module := range []string{"system-module1.jar", "system-module2.jar"} {
+		if !strings.Contains(cmd.Args["classpath"], module) {
+			t.Errorf("system modules classpath %v does not contain %q", cmd.Args["classpath"],
+				module)
+		}
+	}
+}
+
+func TestJavaSystemModulesImport(t *testing.T) {
+	ctx, _ := testJava(t, `
+		java_system_modules_import {
+			name: "system-modules",
+			libs: ["system-module1", "system-module2"],
+		}
+		java_import {
+			name: "system-module1",
+			jars: ["a.jar"],
+		}
+		java_import {
+			name: "system-module2",
+			jars: ["b.jar"],
+		}
+		`)
+
+	// check the existence of the module
+	systemModules := ctx.ModuleForTests("system-modules", "android_common")
+
+	cmd := systemModules.Rule("jarsTosystemModules")
+
+	// make sure the command compiles against the supplied modules.
+	for _, module := range []string{"system-module1.jar", "system-module2.jar"} {
+		if !strings.Contains(cmd.Args["classpath"], module) {
+			t.Errorf("system modules classpath %v does not contain %q", cmd.Args["classpath"],
+				module)
+		}
+	}
+}
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/platform_compat_config.go b/java/platform_compat_config.go
index 23ba2b0..d5c7579 100644
--- a/java/platform_compat_config.go
+++ b/java/platform_compat_config.go
@@ -19,9 +19,14 @@
 )
 
 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"`
 }
@@ -32,47 +37,77 @@
 	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))
 
-	// Use the empty config if the compat config file idoesn't exist (can happen if @ChangeId
-	// annotation is not used).
-	emptyConfig := `'<?xml version="1.0" encoding="UTF-8" standalone="no"?><config/>'`
-	configPath := `compat/compat_config.xml`
-
 	rule.Command().
-		Text(`unzip`).
-		Flag(`-l`).
-		Input(path).
-		Text(`| grep`).
-		Flag(`-q`).
-		Text(configPath).
-		Text(`; if [ "$?" = "0" ] ; then`).
-		Text(`unzip`).
-		Flag(`-qp`).
-		Input(path).
-		Text(configPath).
-		Text(`>`).
-		Output(p.configFile).
-		Text(`; else echo `).
-		Text(emptyConfig).
-		Text(`>`).
-		Output(p.configFile).
-		Text(`; fi`)
+		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{
+func (p *platformCompatConfig) AndroidMkEntries() []android.AndroidMkEntries {
+	return []android.AndroidMkEntries{android.AndroidMkEntries{
 		Class:      "ETC",
 		OutputFile: android.OptionalPathForPath(p.configFile),
 		Include:    "$(BUILD_PREBUILT)",
@@ -82,7 +117,11 @@
 				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.configFile.Base())
 			},
 		},
-	}
+	}}
+}
+
+func platformCompatConfigSingletonFactory() android.Singleton {
+	return &platformCompatConfigSingleton{}
 }
 
 func platformCompatConfigFactory() android.Module {
diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go
index 0d5e31f..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()
 	})
 }
diff --git a/java/robolectric.go b/java/robolectric.go
index b7646eb..3195615 100644
--- a/java/robolectric.go
+++ b/java/robolectric.go
@@ -158,8 +158,9 @@
 	TransformResourcesToJar(ctx, outputFile, srcJarArgs, srcJarDeps)
 }
 
-func (r *robolectricTest) AndroidMkEntries() android.AndroidMkEntries {
-	entries := r.Library.AndroidMkEntries()
+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) {
@@ -185,7 +186,7 @@
 		},
 	}
 
-	return entries
+	return entriesList
 }
 
 func (r *robolectricTest) writeTestRunner(w io.Writer, module, name string, tests []string) {
diff --git a/java/sdk.go b/java/sdk.go
index 6f0f432..66eb284 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -58,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
@@ -72,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
@@ -80,7 +80,7 @@
 	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"
@@ -127,12 +127,12 @@
 		}
 
 		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{}
 		}
 
diff --git a/java/sdk_library.go b/java/sdk_library.go
index b7efcff..72c4a7c 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"}
@@ -68,8 +79,7 @@
 // 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())
@@ -78,16 +88,31 @@
 	})
 }
 
+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
 
@@ -110,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
 }
@@ -130,6 +158,8 @@
 	publicApiFilePath android.Path
 	systemApiFilePath android.Path
 	testApiFilePath   android.Path
+
+	permissionsFile android.Path
 }
 
 var _ Dependency = (*SdkLibrary)(nil)
@@ -143,8 +173,7 @@
 	}
 	ctx.AddVariationDependencies(nil, publicApiFileTag, module.docsName(apiScopePublic))
 
-	sdkDep := decodeSdkDep(ctx, sdkContext(&module.Library))
-	if sdkDep.hasStandardLibs() {
+	if module.sdkLibraryProperties.Has_system_and_test_apis {
 		if useBuiltStubs {
 			ctx.AddVariationDependencies(nil, systemApiStubsTag, module.stubsName(apiScopeSystem))
 			ctx.AddVariationDependencies(nil, testApiStubsTag, module.stubsName(apiScopeTest))
@@ -157,7 +186,12 @@
 }
 
 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,
@@ -194,13 +228,40 @@
 	})
 }
 
-func (module *SdkLibrary) AndroidMkEntries() android.AndroidMkEntries {
-	entries := module.Library.AndroidMkEntries()
+func (module *SdkLibrary) buildPermissionsFile(ctx android.ModuleContext) {
+	xmlContent := fmt.Sprintf(permissionsTemplate, module.BaseModuleName(), module.implPath())
+	permissionsFile := android.PathForModuleOut(ctx, module.xmlFileName())
+
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        android.WriteFile,
+		Output:      permissionsFile,
+		Description: "Generating " + module.BaseModuleName() + " permissions",
+		Args: map[string]string{
+			"content": xmlContent,
+		},
+	})
+
+	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) {
-			module.Library.AndroidMkHostDex(w, name, entries)
 			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.
@@ -252,7 +313,7 @@
 			}
 		},
 	}
-	return entries
+	return entriesList
 }
 
 // Module name of the stubs library
@@ -286,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"
@@ -293,6 +360,8 @@
 		partition = "odm"
 	} else if module.ProductSpecific() {
 		partition = "product"
+	} else if module.SystemExtSpecific() {
+		partition = "system_ext"
 	}
 	return "/" + partition + "/framework/" + module.implName() + ".jar"
 }
@@ -305,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"
@@ -318,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
@@ -363,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
 			}
@@ -387,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
@@ -417,6 +496,8 @@
 		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(LibraryFactory, &props)
@@ -424,12 +505,13 @@
 
 // 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
 		Sdk_version                      *string
+		System_modules                   *string
 		Libs                             []string
 		Arg_files                        []string
 		Args                             *string
@@ -451,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"
@@ -459,6 +543,7 @@
 	props.Name = proptools.StringPtr(module.docsName(apiScope))
 	props.Srcs = append(props.Srcs, module.Library.Module.properties.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.
@@ -471,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
@@ -500,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)
@@ -523,53 +624,19 @@
 
 // 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(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)
@@ -577,6 +644,8 @@
 		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.PrebuiltEtcFactory, &etcProps)
 }
@@ -598,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()}
@@ -648,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)
@@ -683,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() {
@@ -733,6 +823,7 @@
 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
@@ -747,16 +838,8 @@
 
 	Sdk_version *string
 
-	Installable *bool
-
 	// List of shared java libs that this module has dependencies to
 	Libs []string
-
-	// List of files to remove from the jar file(s)
-	Exclude_files []string
-
-	// List of directories to remove from the jar file(s)
-	Exclude_dirs []string
 }
 
 type sdkLibraryImport struct {
@@ -795,10 +878,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)
@@ -809,6 +893,8 @@
 		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(ImportFactory, &props, &module.properties)
diff --git a/java/sdk_test.go b/java/sdk_test.go
index 525c898..9cabd77 100644
--- a/java/sdk_test.go
+++ b/java/sdk_test.go
@@ -327,14 +327,14 @@
 
 			// Test with legacy javac -source 1.8 -target 1.8
 			t.Run("Java language level 8", func(t *testing.T) {
-				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(bpJava8, nil)
+				ctx := testContext()
 				run(t, ctx, config)
 
 				checkClasspath(t, ctx, true /* isJava8 */)
@@ -350,14 +350,14 @@
 
 			// Test with default javac -source 9 -target 9
 			t.Run("Java language level 9", func(t *testing.T) {
-				config := testConfig(nil)
+				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(bp, nil)
+				ctx := testContext()
 				run(t, ctx, config)
 
 				checkClasspath(t, ctx, false /* isJava8 */)
@@ -373,7 +373,7 @@
 
 			// 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)
+				config := testConfig(nil, bpJava8, nil)
 				config.TestProductVariables.Platform_sdk_codename = proptools.StringPtr("REL")
 				config.TestProductVariables.Platform_sdk_final = proptools.BoolPtr(true)
 
@@ -383,7 +383,7 @@
 				if testcase.pdk {
 					config.TestProductVariables.Pdk = proptools.BoolPtr(true)
 				}
-				ctx := testContext(bpJava8, nil)
+				ctx := testContext()
 				run(t, ctx, config)
 
 				checkClasspath(t, ctx, true /* isJava8 */)
@@ -391,7 +391,7 @@
 
 			// 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)
+				config := testConfig(nil, bp, nil)
 				config.TestProductVariables.Platform_sdk_codename = proptools.StringPtr("REL")
 				config.TestProductVariables.Platform_sdk_final = proptools.BoolPtr(true)
 
@@ -401,7 +401,7 @@
 				if testcase.pdk {
 					config.TestProductVariables.Pdk = proptools.BoolPtr(true)
 				}
-				ctx := testContext(bp, nil)
+				ctx := testContext()
 				run(t, ctx, config)
 
 				checkClasspath(t, ctx, false /* isJava8 */)
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 b56a401..92297c4 100644
--- a/java/system_modules.go
+++ b/java/system_modules.go
@@ -28,11 +28,16 @@
 // 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)
+	ctx.RegisterModuleType("java_system_modules_import", systemModulesImportFactory)
+}
+
 var (
 	jarsTosystemModules = pctx.AndroidStaticRule("jarsTosystemModules", blueprint.RuleParams{
 		Command: `rm -rf ${outDir} ${workDir} && mkdir -p ${workDir}/jmod && ` +
@@ -88,6 +93,9 @@
 	return outDir, outputs.Paths()
 }
 
+// java_system_modules creates a system module from a set of java libraries that can
+// be referenced from the system_modules property. It must contain at a minimum the
+// java.base module which must include classes from java.lang amongst other java packages.
 func SystemModulesFactory() android.Module {
 	module := &SystemModules{}
 	module.AddProperties(&module.properties)
@@ -153,3 +161,30 @@
 		},
 	}
 }
+
+// A prebuilt version of java_system_modules. It does not import the
+// generated system module, it generates the system module from imported
+// java libraries in the same way that java_system_modules does. It just
+// acts as a prebuilt, i.e. can have the same base name as another module
+// type and the one to use is selected at runtime.
+func systemModulesImportFactory() android.Module {
+	module := &systemModulesImport{}
+	module.AddProperties(&module.properties)
+	android.InitPrebuiltModule(module, &module.properties.Libs)
+	android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon)
+	android.InitDefaultableModule(module)
+	return module
+}
+
+type systemModulesImport struct {
+	SystemModules
+	prebuilt android.Prebuilt
+}
+
+func (system *systemModulesImport) Name() string {
+	return system.prebuilt.Name(system.ModuleBase.Name())
+}
+
+func (system *systemModulesImport) Prebuilt() *android.Prebuilt {
+	return &system.prebuilt
+}
diff --git a/java/testing.go b/java/testing.go
index acbefb9..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
 }
@@ -38,7 +139,6 @@
 	extraModules := []string{
 		"core-lambda-stubs",
 		"ext",
-		"updatable_media_stubs",
 		"android_stubs_current",
 		"android_system_stubs_current",
 		"android_test_stubs_current",
@@ -105,9 +205,6 @@
 	systemModules := []string{
 		"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 {
diff --git a/python/androidmk.go b/python/androidmk.go
index aae7ced..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)
 }
diff --git a/python/python.go b/python/python.go
index c67c577..8b912be 100644
--- a/python/python.go
+++ b/python/python.go
@@ -340,6 +340,11 @@
 			// dependencies later.
 			ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherSharedLibTag, "libsqlite")
 
+			if ctx.Device() {
+				ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherSharedLibTag,
+					"liblog")
+			}
+
 			if ctx.Target().Os.Bionic() {
 				ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherSharedLibTag,
 					"libc", "libdl", "libm")
diff --git a/python/python_test.go b/python/python_test.go
index ba5e7fa..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,10 +326,9 @@
 )
 
 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()
@@ -336,8 +337,7 @@
 			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)
@@ -425,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/rust/androidmk.go b/rust/androidmk.go
index 2636d97..0fba739 100644
--- a/rust/androidmk.go
+++ b/rust/androidmk.go
@@ -99,6 +99,9 @@
 		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)
 }
diff --git a/rust/binary.go b/rust/binary.go
index d4b6614..fda056e 100644
--- a/rust/binary.go
+++ b/rust/binary.go
@@ -57,7 +57,7 @@
 	module := newModule(hod, android.MultilibFirst)
 
 	binary := &binaryDecorator{
-		baseCompiler: NewBaseCompiler("bin", ""),
+		baseCompiler: NewBaseCompiler("bin", "", InstallInSystem),
 	}
 
 	module.compiler = binary
diff --git a/rust/compiler.go b/rust/compiler.go
index 88e3fb2..4593165 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -36,14 +36,22 @@
 	compiler.Properties.No_stdlibs = proptools.BoolPtr(true)
 }
 
-func NewBaseCompiler(dir, dir64 string) *baseCompiler {
+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
@@ -109,10 +117,15 @@
 	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}
 }
diff --git a/rust/config/arm64_device.go b/rust/config/arm64_device.go
index 0264052..60796d8 100644
--- a/rust/config/arm64_device.go
+++ b/rust/config/arm64_device.go
@@ -27,7 +27,8 @@
 		"-Wl,--icf=safe",
 		"-Wl,-z,max-page-size=4096",
 
-		"-Wl,-execute-only",
+		"-Wl,--execute-only",
+		"-Wl,-z,separate-code",
 	}
 
 	Arm64ArchVariantRustFlags = map[string][]string{
diff --git a/rust/config/global.go b/rust/config/global.go
index 4d87780..fb9b14b 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -24,12 +24,11 @@
 var pctx = android.NewPackageContext("android/soong/rust/config")
 
 var (
-	RustDefaultVersion = "1.37.0"
+	RustDefaultVersion = "1.40.0"
 	RustDefaultBase    = "prebuilts/rust/"
 	DefaultEdition     = "2018"
 	Stdlibs            = []string{
 		"libstd",
-		"libterm",
 		"libtest",
 	}
 
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/library.go b/rust/library.go
index ba47541..0cf2dd0 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -290,7 +290,7 @@
 			BuildShared: true,
 			BuildStatic: true,
 		},
-		baseCompiler: NewBaseCompiler("lib", "lib64"),
+		baseCompiler: NewBaseCompiler("lib", "lib64", InstallInSystem),
 	}
 
 	module.compiler = library
@@ -323,6 +323,7 @@
 	return deps
 }
 func (library *libraryDecorator) compilerFlags(ctx ModuleContext, flags Flags) Flags {
+	flags.RustFlags = append(flags.RustFlags, "-C metadata="+ctx.baseModuleName())
 	flags = library.baseCompiler.compilerFlags(ctx, flags)
 	if library.shared() || library.static() {
 		library.includeDirs = append(library.includeDirs, android.PathsForModuleSrc(ctx, library.Properties.Include_dirs)...)
@@ -337,7 +338,7 @@
 
 	flags.RustFlags = append(flags.RustFlags, deps.depFlags...)
 
-	if library.dylib() || library.shared() {
+	if library.dylib() {
 		// 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
diff --git a/rust/proc_macro.go b/rust/proc_macro.go
index 0da87da..10ea1e3 100644
--- a/rust/proc_macro.go
+++ b/rust/proc_macro.go
@@ -53,7 +53,7 @@
 	module := newModule(hod, android.MultilibFirst)
 
 	procMacro := &procMacroDecorator{
-		baseCompiler: NewBaseCompiler("lib", "lib64"),
+		baseCompiler: NewBaseCompiler("lib", "lib64", InstallInSystem),
 	}
 
 	module.compiler = procMacro
diff --git a/rust/rust.go b/rust/rust.go
index a3266f7..14513fb 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -226,6 +226,7 @@
 	compilerDeps(ctx DepsContext, deps Deps) Deps
 	crateName() string
 
+	inData() bool
 	install(ctx ModuleContext, path android.Path)
 	relativeInstallPath() string
 }
@@ -345,6 +346,11 @@
 	return nil
 }
 
+func (mod *Module) AllStaticDeps() []string {
+	// TODO(jiyong): do this for rust?
+	return nil
+}
+
 func (mod *Module) Module() android.Module {
 	return mod
 }
@@ -681,6 +687,13 @@
 	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]
 }
diff --git a/rust/rust_test.go b/rust/rust_test.go
index 91c2f09..3be9ee7 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -51,6 +51,19 @@
 	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" {
@@ -58,11 +71,11 @@
 	}
 
 	t.Helper()
-	config := android.TestArchConfig(buildDir, nil)
+	config := testConfig(bp)
 
 	t.Helper()
-	ctx := CreateTestContext(bp)
-	ctx.Register()
+	ctx := CreateTestContext()
+	ctx.Register(config)
 
 	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
 	android.FailIfErrored(t, errs)
@@ -79,10 +92,10 @@
 	}
 
 	t.Helper()
-	config := android.TestArchConfig(buildDir, nil)
+	config := testConfig(bp)
 
-	ctx := CreateTestContext(bp)
-	ctx.Register()
+	ctx := CreateTestContext()
+	ctx.Register(config)
 
 	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
 	if len(errs) > 0 {
@@ -245,7 +258,7 @@
 			srcs: ["foo.rs"],
                         no_stdlibs: true,
 		}`)
-	module := ctx.ModuleForTests("fizz-buzz", "android_arm64_armv8-a_core").Module().(*Module)
+	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
index b391103..04f844c 100644
--- a/rust/test.go
+++ b/rust/test.go
@@ -55,8 +55,7 @@
 
 	test := &testDecorator{
 		binaryDecorator: &binaryDecorator{
-			// TODO(chh): set up dir64?
-			baseCompiler: NewBaseCompiler("testcases", ""),
+			baseCompiler: NewBaseCompiler("nativetest", "nativetest64", InstallInData),
 		},
 	}
 
@@ -79,22 +78,26 @@
 }
 
 func (test *testDecorator) install(ctx ModuleContext, file android.Path) {
-	name := ctx.ModuleName() // default executable name
-	if ctx.Device() {        // on device, use mutated module name
-		name = name + test.getMutatedModuleSubName(name)
-	} else { // on host, use stem name in relative_install_path
-		if stem := String(test.baseCompiler.Properties.Stem); stem != "" {
-			name = stem
+	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
 		}
-		if path := test.baseCompiler.relativeInstallPath(); path != "" {
-			name = path + "/" + 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)
 }
 
diff --git a/rust/testing.go b/rust/testing.go
index 45dbbbd..f9adec8 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -75,97 +75,19 @@
 		//////////////////////////////
 		// Device module requirements
 
-		toolchain_library {
-			name: "libgcc",
-			no_libcrt: true,
-			nocrt: true,
-			src: "",
-			system_shared_libs: [],
-		}
-		cc_library {
-			name: "libc",
-			no_libcrt: true,
-			nocrt: true,
-			system_shared_libs: [],
-		}
-		cc_library {
-			name: "libm",
-			no_libcrt: true,
-			nocrt: true,
-			system_shared_libs: [],
-		}
-		cc_library {
-			name: "libdl",
-			no_libcrt: true,
-			nocrt: true,
-			system_shared_libs: [],
-		}
-		cc_object {
-			name: "crtbegin_dynamic",
-		}
-
-		cc_object {
-			name: "crtend_android",
-		}
 		cc_library {
 			name: "liblog",
 			no_libcrt: true,
 			nocrt: true,
 			system_shared_libs: [],
 		}
-
-		//////////////////////////////
-		// cc module requirements
-
-		toolchain_library {
-			name: "libatomic",
-			src: "",
-		}
-		toolchain_library {
-			name: "libclang_rt.builtins-aarch64-android",
-			src: "",
-		}
-		toolchain_library {
-			name: "libgcc_stripped",
-			src: "",
-		}
-		cc_library {
-			name: "libc++_static",
-			no_libcrt: true,
-			nocrt: true,
-			system_shared_libs: [],
-			stl: "none",
-		}
-		cc_library {
-			name: "libc++demangle",
-			no_libcrt: true,
-			nocrt: true,
-			system_shared_libs: [],
-			stl: "none",
-			host_supported: false,
-		}
-		cc_library {
-			name: "libc++",
-			no_libcrt: true,
-			nocrt: true,
-			system_shared_libs: [],
-			stl: "none",
-		}
-		cc_library {
-			name: "libunwind_llvm",
-			no_libcrt: true,
-			nocrt: true,
-			system_shared_libs: [],
-			stl: "none",
-		}
-		`
+` + cc.GatherRequiredDepsForTest(android.NoOsType)
 	return bp
 }
 
-func CreateTestContext(bp string) *android.TestContext {
+func CreateTestContext() *android.TestContext {
 	ctx := android.NewTestArchContext()
-	ctx.RegisterModuleType("cc_library", cc.LibraryFactory)
-	ctx.RegisterModuleType("cc_object", cc.ObjectFactory)
+	cc.RegisterRequiredBuildComponentsForTest(ctx)
 	ctx.RegisterModuleType("rust_binary", RustBinaryFactory)
 	ctx.RegisterModuleType("rust_binary_host", RustBinaryHostFactory)
 	ctx.RegisterModuleType("rust_test", RustTestFactory)
@@ -182,29 +104,11 @@
 	ctx.RegisterModuleType("rust_library_host_static", RustLibraryStaticHostFactory)
 	ctx.RegisterModuleType("rust_proc_macro", ProcMacroFactory)
 	ctx.RegisterModuleType("rust_prebuilt_dylib", PrebuiltDylibFactory)
-	ctx.RegisterModuleType("toolchain_library", cc.ToolchainLibraryFactory)
 	ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		// cc mutators
-		ctx.BottomUp("image", android.ImageMutator).Parallel()
-		ctx.BottomUp("link", cc.LinkageMutator).Parallel()
-		ctx.BottomUp("version", cc.VersionMutator).Parallel()
-		ctx.BottomUp("begin", cc.BeginMutator).Parallel()
-
 		// rust mutators
 		ctx.BottomUp("rust_libraries", LibraryMutator).Parallel()
 		ctx.BottomUp("rust_unit_tests", TestPerSrcMutator).Parallel()
 	})
-	bp = bp + GatherRequiredDepsForTest()
-
-	mockFS := map[string][]byte{
-		"Android.bp": []byte(bp),
-		"foo.rs":     nil,
-		"src/bar.rs": nil,
-		"liby.so":    nil,
-		"libz.so":    nil,
-	}
-
-	ctx.MockFileSystem(mockFS)
 
 	return ctx
 }
diff --git a/scripts/Android.bp b/scripts/Android.bp
index 8c59cbc..4aaff9a 100644
--- a/scripts/Android.bp
+++ b/scripts/Android.bp
@@ -85,3 +85,39 @@
         },
     }
 }
+
+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/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/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/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/test_config_fixer.py b/scripts/test_config_fixer.py
new file mode 100644
index 0000000..32d5b17
--- /dev/null
+++ b/scripts/test_config_fixer.py
@@ -0,0 +1,98 @@
+#!/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('--test-file-name', default='', dest='test_file_name',
+                      help=('overwrite test file name 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')
+
+  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 overwrite_test_file_name(test_config_doc, test_file_name):
+
+  test_config = parse_test_config(test_config_doc)
+  tests = get_children_with_tag(test_config, 'target_preparer')
+
+  for test in tests:
+    if test.getAttribute('class') == "com.android.tradefed.targetprep.TestAppInstallSetup":
+      options = get_children_with_tag(test, 'option')
+      for option in options:
+        if option.getAttribute('name') == "test-file-name":
+          option.setAttribute('value', test_file_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)
+
+    if args.test_file_name:
+      overwrite_test_file_name(doc, args.test_file_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..1272c6b
--- /dev/null
+++ b/scripts/test_config_fixer_test.py
@@ -0,0 +1,98 @@
+#!/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())
+
+
+class OverwriteTestFileNameTest(unittest.TestCase):
+  """ Unit tests for overwrite_test_file_name function """
+
+  test_config = (
+      '<?xml version="1.0" encoding="utf-8"?>\n'
+      '<configuration description="Runs some tests.">\n'
+      '    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">\n'
+      '        <option name="test-file-name" value="%s"/>\n'
+      '    </target_preparer>\n'
+      '    <test class="com.android.tradefed.testtype.AndroidJUnitTest">\n'
+      '        <option name="package" value="com.android.foo"/>\n'
+      '        <option name="runtime-hint" value="20s"/>\n'
+      '    </test>\n'
+      '</configuration>\n')
+
+  def test_all(self):
+    doc = minidom.parseString(self.test_config % ("foo.apk"))
+
+    test_config_fixer.overwrite_test_file_name(doc, "bar.apk")
+    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 % ("bar.apk")
+    self.assertEqual(expected, output.getvalue())
+
+
+if __name__ == '__main__':
+  unittest.main(verbosity=2)
diff --git a/sdk/bp.go b/sdk/bp.go
index 19fb70d..ae06a09 100644
--- a/sdk/bp.go
+++ b/sdk/bp.go
@@ -51,13 +51,23 @@
 	return s.properties[name]
 }
 
-func (s *bpPropertySet) copy() bpPropertySet {
+func (s *bpPropertySet) deepCopy() *bpPropertySet {
 	propertiesCopy := make(map[string]interface{})
 	for p, v := range s.properties {
-		propertiesCopy[p] = v
+		var valueCopy interface{}
+		if ps, ok := v.(*bpPropertySet); ok {
+			valueCopy = ps.deepCopy()
+		} else if values, ok := v.([]string); ok {
+			valuesCopy := make([]string, len(values))
+			copy(valuesCopy, values)
+			valueCopy = valuesCopy
+		} else {
+			valueCopy = v
+		}
+		propertiesCopy[p] = valueCopy
 	}
 
-	return bpPropertySet{
+	return &bpPropertySet{
 		properties: propertiesCopy,
 		order:      append([]string(nil), s.order...),
 	}
@@ -95,15 +105,15 @@
 }
 
 type bpModule struct {
-	bpPropertySet
+	*bpPropertySet
 	moduleType string
 }
 
 var _ android.BpModule = (*bpModule)(nil)
 
-func (m *bpModule) copy() *bpModule {
+func (m *bpModule) deepCopy() *bpModule {
 	return &bpModule{
-		bpPropertySet: m.bpPropertySet.copy(),
+		bpPropertySet: m.bpPropertySet.deepCopy(),
 		moduleType:    m.moduleType,
 	}
 }
@@ -134,8 +144,9 @@
 
 func (f *bpFile) newModule(moduleType string) *bpModule {
 	module := &bpModule{
-		moduleType: moduleType,
+		moduleType:    moduleType,
+		bpPropertySet: &bpPropertySet{},
 	}
-	(&module.bpPropertySet).init()
+	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..9a75610
--- /dev/null
+++ b/sdk/cc_sdk_test.go
@@ -0,0 +1,688 @@
+// 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",
+			// TODO: remove //apex_available:platform
+			apex_available: [
+				"//apex_available:platform",
+				"myapex",
+			],
+		}
+
+		cc_prebuilt_library_shared {
+			name: "sdkmember_mysdk_2",
+			sdk_member_name: "sdkmember",
+			srcs: ["libfoo.so"],
+			system_shared_libs: [],
+			stl: "none",
+			// TODO: remove //apex_available:platform
+			apex_available: [
+				"//apex_available:platform",
+				"myapex2",
+			],
+		}
+
+		cc_library_shared {
+			name: "mycpplib",
+			srcs: ["Test.cpp"],
+			shared_libs: ["sdkmember"],
+			system_shared_libs: [],
+			stl: "none",
+			apex_available: [
+				"myapex",
+				"myapex2",
+			],
+		}
+
+		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..692c205
--- /dev/null
+++ b/sdk/java_sdk_test.go
@@ -0,0 +1,571 @@
+// 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_available: [
+				"myapex",
+				"myapex2",
+			],
+		}
+
+		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
index 431ace9..3b0752f 100644
--- a/sdk/sdk.go
+++ b/sdk/sdk.go
@@ -17,6 +17,7 @@
 import (
 	"fmt"
 	"io"
+	"reflect"
 	"strconv"
 
 	"github.com/google/blueprint"
@@ -26,12 +27,13 @@
 	// 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"
-	"android/soong/cc"
 )
 
 func init() {
 	pctx.Import("android/soong/android")
-	android.RegisterModuleType("sdk", ModuleFactory)
+	pctx.Import("android/soong/java/config")
+
+	android.RegisterModuleType("sdk", SdkModuleFactory)
 	android.RegisterModuleType("sdk_snapshot", SnapshotModuleFactory)
 	android.PreDepsMutators(RegisterPreDepsMutators)
 	android.PostDepsMutators(RegisterPostDepsMutators)
@@ -41,6 +43,13 @@
 	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
@@ -50,21 +59,152 @@
 }
 
 type sdkProperties struct {
-	// The list of java libraries in this SDK
-	Java_libs []string
-	// The list of native libraries in this SDK
-	Native_shared_libs []string
-	// The list of stub sources in this SDK
-	Stubs_sources []string
-
 	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 ModuleFactory() android.Module {
+func SdkModuleFactory() android.Module {
+	return newSdkModule(false)
+}
+
+func newSdkModule(moduleExports bool) *sdk {
 	s := &sdk{}
-	s.AddProperties(&s.properties)
+	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) {
@@ -79,8 +219,8 @@
 
 // sdk_snapshot is a versioned snapshot of an SDK. This is an auto-generated module.
 func SnapshotModuleFactory() android.Module {
-	s := ModuleFactory()
-	s.(*sdk).properties.Snapshot = true
+	s := newSdkModule(false)
+	s.properties.Snapshot = true
 	return s
 }
 
@@ -96,12 +236,12 @@
 	}
 }
 
-func (s *sdk) AndroidMkEntries() android.AndroidMkEntries {
+func (s *sdk) AndroidMkEntries() []android.AndroidMkEntries {
 	if !s.snapshotFile.Valid() {
-		return android.AndroidMkEntries{}
+		return []android.AndroidMkEntries{}
 	}
 
-	return android.AndroidMkEntries{
+	return []android.AndroidMkEntries{android.AndroidMkEntries{
 		Class:      "FAKE",
 		OutputFile: s.snapshotFile,
 		DistFile:   s.snapshotFile,
@@ -113,7 +253,7 @@
 				fmt.Fprintln(w, s.Name()+":", s.snapshotFile.String())
 			},
 		},
-	}
+	}}
 }
 
 // RegisterPreDepsMutators registers pre-deps mutators to support modules implementing SdkAware
@@ -143,37 +283,24 @@
 	blueprint.BaseDependencyTag
 }
 
-// For dependencies from an SDK module to its members
-// e.g. mysdk -> libfoo and libbar
-var sdkMemberDepTag dependencyTag
-
 // 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 {
+type sdkMemberVersionedDepTag struct {
 	dependencyTag
 	member  string
 	version string
 }
 
+// Mark this tag so dependencies that use it are excluded from visibility enforcement.
+func (t sdkMemberVersionedDepTag) ExcludeFromVisibilityEnforcement() {}
+
 // Step 1: create dependencies from an SDK module to its members.
 func memberMutator(mctx android.BottomUpMutatorContext) {
-	if m, ok := mctx.Module().(*sdk); ok {
-		mctx.AddVariationDependencies(nil, sdkMemberDepTag, m.properties.Java_libs...)
-		mctx.AddVariationDependencies(nil, sdkMemberDepTag, m.properties.Stubs_sources...)
-
-		targets := mctx.MultiTargets()
-		for _, target := range targets {
-			for _, lib := range m.properties.Native_shared_libs {
-				name, version := cc.StubsLibNameAndVersion(lib)
-				if version == "" {
-					version = cc.LatestStubsVersionFor(mctx.Config(), name)
-				}
-				mctx.AddFarVariationDependencies(append(target.Variations(), []blueprint.Variation{
-					{Mutator: "image", Variation: android.CoreVariation},
-					{Mutator: "link", Variation: "shared"},
-					{Mutator: "version", Variation: version},
-				}...), sdkMemberDepTag, name)
-			}
+	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)
 		}
 	}
 }
@@ -213,7 +340,7 @@
 	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}
+			tag := sdkMemberVersionedDepTag{member: memberName, version: m.ContainingSdk().Version}
 			mctx.AddReverseDependency(mctx.Module(), tag, memberName)
 		}
 	}
diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go
index 5435ef6..d376e59 100644
--- a/sdk/sdk_test.go
+++ b/sdk/sdk_test.go
@@ -15,338 +15,24 @@
 package sdk
 
 import (
-	"io/ioutil"
-	"os"
-	"path/filepath"
-	"strings"
 	"testing"
-
-	"android/soong/android"
-	"android/soong/apex"
-	"android/soong/cc"
-	"android/soong/java"
 )
 
-func testSdkContext(t *testing.T, bp string) (*android.TestContext, android.Config) {
-	config := android.TestArchConfig(buildDir, nil)
-	ctx := android.NewTestArchContext()
-
-	// from android package
-	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
-	ctx.PreArchMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.BottomUp("prebuilts", android.PrebuiltMutator).Parallel()
-	})
-	ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.TopDown("prebuilt_select", android.PrebuiltSelectModuleMutator).Parallel()
-		ctx.BottomUp("prebuilt_postdeps", android.PrebuiltPostDepsMutator).Parallel()
-	})
-
-	// from java package
-	ctx.RegisterModuleType("android_app_certificate", java.AndroidAppCertificateFactory)
-	ctx.RegisterModuleType("java_library", java.LibraryFactory)
-	ctx.RegisterModuleType("java_import", java.ImportFactory)
-	ctx.RegisterModuleType("droidstubs", java.DroidstubsFactory)
-	ctx.RegisterModuleType("prebuilt_stubs_sources", java.PrebuiltStubsSourcesFactory)
-
-	// from cc package
-	ctx.RegisterModuleType("cc_library", cc.LibraryFactory)
-	ctx.RegisterModuleType("cc_library_shared", cc.LibrarySharedFactory)
-	ctx.RegisterModuleType("cc_object", cc.ObjectFactory)
-	ctx.RegisterModuleType("cc_prebuilt_library_shared", cc.PrebuiltSharedLibraryFactory)
-	ctx.RegisterModuleType("cc_prebuilt_library_static", cc.PrebuiltStaticLibraryFactory)
-	ctx.RegisterModuleType("llndk_library", cc.LlndkLibraryFactory)
-	ctx.RegisterModuleType("toolchain_library", cc.ToolchainLibraryFactory)
-	ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.BottomUp("image", android.ImageMutator).Parallel()
-		ctx.BottomUp("link", cc.LinkageMutator).Parallel()
-		ctx.BottomUp("vndk", cc.VndkMutator).Parallel()
-		ctx.BottomUp("test_per_src", cc.TestPerSrcMutator).Parallel()
-		ctx.BottomUp("version", cc.VersionMutator).Parallel()
-		ctx.BottomUp("begin", cc.BeginMutator).Parallel()
-	})
-
-	// from apex package
-	ctx.RegisterModuleType("apex", apex.BundleFactory)
-	ctx.RegisterModuleType("apex_key", apex.ApexKeyFactory)
-	ctx.PostDepsMutators(apex.RegisterPostDepsMutators)
-
-	// from this package
-	ctx.RegisterModuleType("sdk", ModuleFactory)
-	ctx.RegisterModuleType("sdk_snapshot", SnapshotModuleFactory)
-	ctx.PreDepsMutators(RegisterPreDepsMutators)
-	ctx.PostDepsMutators(RegisterPostDepsMutators)
-
-	ctx.Register()
-
-	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)
-
-	ctx.MockFileSystem(map[string][]byte{
-		"Android.bp":                                 []byte(bp),
-		"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,
-		"Test.java":                                  nil,
-		"Test.cpp":                                   nil,
-		"include/Test.h":                             nil,
-		"aidl/foo/bar/Test.aidl":                     nil,
-		"libfoo.so":                                  nil,
-		"stubs-sources/foo/bar/Foo.java":             nil,
-		"foo/bar/Foo.java":                           nil,
-	})
-
-	return ctx, config
-}
-
-func testSdk(t *testing.T, bp string) (*android.TestContext, android.Config) {
-	ctx, config := testSdkContext(t, bp)
-	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
-	android.FailIfErrored(t, errs)
-	_, errs = ctx.PrepareBuildActions(config)
-	android.FailIfErrored(t, errs)
-	return ctx, config
-}
-
-func testSdkError(t *testing.T, pattern, bp string) {
-	t.Helper()
-	ctx, config := testSdkContext(t, bp)
-	_, 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 {
-	ret := []string{}
-	for _, p := range paths {
-		ret = append(ret, p.String())
-	}
-	return ret
-}
-
-func TestBasicSdkWithJava(t *testing.T) {
-	ctx, _ := testSdk(t, `
-		sdk {
-			name: "mysdk",
-			java_libs: ["sdkmember"],
-		}
-
-		sdk_snapshot {
-			name: "mysdk@1",
-			java_libs: ["sdkmember_mysdk_1"],
-		}
-
-		sdk_snapshot {
-			name: "mysdk@2",
-			java_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 := ctx.ModuleForTests("sdkmember_mysdk_1", "android_common_myapex").Rule("combineJar").Output
-	sdkMemberV2 := ctx.ModuleForTests("sdkmember_mysdk_2", "android_common_myapex2").Rule("combineJar").Output
-
-	javalibForMyApex := ctx.ModuleForTests("myjavalib", "android_common_myapex")
-	javalibForMyApex2 := 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 TestBasicSdkWithCc(t *testing.T) {
-	ctx, _ := testSdk(t, `
-		sdk {
-			name: "mysdk",
-			native_shared_libs: ["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 := ctx.ModuleForTests("sdkmember_mysdk_1", "android_arm64_armv8-a_core_shared_myapex").Rule("toc").Output
-	sdkMemberV2 := ctx.ModuleForTests("sdkmember_mysdk_2", "android_arm64_armv8-a_core_shared_myapex2").Rule("toc").Output
-
-	cpplibForMyApex := ctx.ModuleForTests("mycpplib", "android_arm64_armv8-a_core_shared_myapex")
-	cpplibForMyApex2 := ctx.ModuleForTests("mycpplib", "android_arm64_armv8-a_core_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())
-}
-
-// 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) {
-	testSdk(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",
-		}
-	`)
+// 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_libs: ["sdkmember"],
+			java_header_libs: ["sdkmember"],
 		}
 
 		sdk_snapshot {
 			name: "mysdk@1",
-			java_libs: ["sdkmember_mysdk_1"],
+			java_header_libs: ["sdkmember_mysdk_1"],
 		}
 
 		java_import {
@@ -394,439 +80,122 @@
 	`)
 }
 
-func TestSdkIsCompileMultilibBoth(t *testing.T) {
-	ctx, _ := testSdk(t, `
+// 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",
-			native_shared_libs: ["sdkmember"],
-		}
-
-		cc_library_shared {
-			name: "sdkmember",
-			srcs: ["Test.cpp"],
-			system_shared_libs: [],
-			stl: "none",
-		}
-	`)
-
-	armOutput := ctx.ModuleForTests("sdkmember", "android_arm_armv7-a-neon_core_shared").Module().(*cc.Module).OutputFile()
-	arm64Output := ctx.ModuleForTests("sdkmember", "android_arm64_armv8-a_core_shared").Module().(*cc.Module).OutputFile()
-
-	var inputs []string
-	buildParams := ctx.ModuleForTests("mysdk", "android_common").Module().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 TestSnapshot(t *testing.T) {
-	ctx, config := testSdk(t, `
-		sdk {
-			name: "mysdk",
-			java_libs: ["myjavalib"],
-			native_shared_libs: ["mynativelib"],
-			stubs_sources: ["myjavaapistubs"],
+			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"],
-			aidl: {
-				export_include_dirs: ["aidl"],
-			},
-			system_modules: "none",
-			sdk_version: "none",
-			compile_dex: true,
-			host_supported: true,
-		}
-
-		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",
-		}
-
-		droidstubs {
-			name: "myjavaapistubs",
-			srcs: ["foo/bar/Foo.java"],
 			system_modules: "none",
 			sdk_version: "none",
 		}
-	`)
 
-	sdk := ctx.ModuleForTests("mysdk", "android_common").Module().(*sdk)
+		java_library {
+			name: "mypublicjavalib",
+      visibility: ["//visibility:public"],
+			srcs: ["Test.java"],
+			system_modules: "none",
+			sdk_version: "none",
+		}
 
-	checkSnapshotAndroidBpContents(t, sdk, `// This is auto-generated. DO NOT EDIT.
+		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,
-    jars: ["java/myjavalib.jar"],
-}
-
-prebuilt_stubs_sources {
-    name: "mysdk_myjavaapistubs@current",
-    sdk_member_name: "myjavaapistubs",
-    srcs: ["java/myjavaapistubs_stubs_sources"],
-}
-
-prebuilt_stubs_sources {
-    name: "myjavaapistubs",
-    prefer: false,
-    srcs: ["java/myjavaapistubs_stubs_sources"],
-}
-
-cc_prebuilt_library_shared {
-    name: "mysdk_mynativelib@current",
-    sdk_member_name: "mynativelib",
-    arch: {
-        arm64: {
-            srcs: ["arm64/lib/mynativelib.so"],
-            export_include_dirs: [
-                "arm64/include/include",
-                "arm64/include_gen/mynativelib",
-            ],
-        },
-        arm: {
-            srcs: ["arm/lib/mynativelib.so"],
-            export_include_dirs: [
-                "arm/include/include",
-                "arm/include_gen/mynativelib",
-            ],
-        },
-    },
-    stl: "none",
-    system_shared_libs: [],
-}
-
-cc_prebuilt_library_shared {
-    name: "mynativelib",
-    prefer: false,
-    arch: {
-        arm64: {
-            srcs: ["arm64/lib/mynativelib.so"],
-            export_include_dirs: [
-                "arm64/include/include",
-                "arm64/include_gen/mynativelib",
-            ],
-        },
-        arm: {
-            srcs: ["arm/lib/mynativelib.so"],
-            export_include_dirs: [
-                "arm/include/include",
-                "arm/include_gen/mynativelib",
-            ],
-        },
-    },
-    stl: "none",
-    system_shared_libs: [],
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    java_libs: ["mysdk_myjavalib@current"],
-    stubs_sources: ["mysdk_myjavaapistubs@current"],
-    native_shared_libs: ["mysdk_mynativelib@current"],
-}
-
-`)
-
-	var copySrcs []string
-	var copyDests []string
-	buildParams := sdk.BuildParamsForTests()
-	var zipBp android.BuildParams
-	for _, bp := range buildParams {
-		ruleString := bp.Rule.String()
-		if ruleString == "android/soong/android.Cp" {
-			copySrcs = append(copySrcs, bp.Input.String())
-			copyDests = append(copyDests, bp.Output.Rel()) // rooted at the snapshot root
-		} else if ruleString == "<local rule>:m.mysdk_android_common.snapshot" {
-			zipBp = bp
-		}
-	}
-
-	buildDir := config.BuildDir()
-	ensureListContains(t, copySrcs, "aidl/foo/bar/Test.aidl")
-	ensureListContains(t, copySrcs, "include/Test.h")
-	ensureListContains(t, copySrcs, filepath.Join(buildDir, ".intermediates/mynativelib/android_arm64_armv8-a_core_shared/gen/aidl/aidl/foo/bar/BnTest.h"))
-	ensureListContains(t, copySrcs, filepath.Join(buildDir, ".intermediates/mynativelib/android_arm64_armv8-a_core_shared/gen/aidl/aidl/foo/bar/BpTest.h"))
-	ensureListContains(t, copySrcs, filepath.Join(buildDir, ".intermediates/mynativelib/android_arm64_armv8-a_core_shared/gen/aidl/aidl/foo/bar/Test.h"))
-	ensureListContains(t, copySrcs, filepath.Join(buildDir, ".intermediates/myjavalib/android_common/turbine-combined/myjavalib.jar"))
-	ensureListContains(t, copySrcs, filepath.Join(buildDir, ".intermediates/mynativelib/android_arm64_armv8-a_core_shared/mynativelib.so"))
-
-	ensureListContains(t, copyDests, "aidl/aidl/foo/bar/Test.aidl")
-	ensureListContains(t, copyDests, "arm64/include/include/Test.h")
-	ensureListContains(t, copyDests, "arm64/include_gen/mynativelib/aidl/foo/bar/BnTest.h")
-	ensureListContains(t, copyDests, "arm64/include_gen/mynativelib/aidl/foo/bar/BpTest.h")
-	ensureListContains(t, copyDests, "arm64/include_gen/mynativelib/aidl/foo/bar/Test.h")
-	ensureListContains(t, copyDests, "java/myjavalib.jar")
-	ensureListContains(t, copyDests, "arm64/lib/mynativelib.so")
-
-	// Ensure that the droidstubs .srcjar as repackaged into a temporary zip file
-	// and then merged together with the intermediate snapshot zip.
-	snapshotCreationInputs := zipBp.Implicits.Strings()
-	ensureListContains(t, snapshotCreationInputs,
-		filepath.Join(buildDir, ".intermediates/mysdk/android_common/tmp/java/myjavaapistubs_stubs_sources.zip"))
-	ensureListContains(t, snapshotCreationInputs,
-		filepath.Join(buildDir, ".intermediates/mysdk/android_common/mysdk-current.unmerged.zip"))
-	actual := zipBp.Output.String()
-	expected := filepath.Join(buildDir, ".intermediates/mysdk/android_common/mysdk-current.zip")
-	if actual != expected {
-		t.Errorf("Expected snapshot output to be %q but was %q", expected, actual)
-	}
-}
-
-func TestHostSnapshot(t *testing.T) {
-	// b/145598135 - Generating host snapshots for anything other than linux is not supported.
-	SkipIfNotLinux(t)
-
-	ctx, config := testSdk(t, `
-		sdk {
-			name: "mysdk",
-			device_supported: false,
-			host_supported: true,
-			java_libs: ["myjavalib"],
-			native_shared_libs: ["mynativelib"],
-			stubs_sources: ["myjavaapistubs"],
-		}
-
-		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,
-		}
-
-		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",
-		}
-
-		droidstubs {
-			name: "myjavaapistubs",
-			device_supported: false,
-			host_supported: true,
-			srcs: ["foo/bar/Foo.java"],
-			system_modules: "none",
-			sdk_version: "none",
-		}
-	`)
-
-	sdk := ctx.ModuleForTests("mysdk", "linux_glibc_common").Module().(*sdk)
-
-	checkSnapshotAndroidBpContents(t, sdk, `// This is auto-generated. DO NOT EDIT.
-
-java_import {
-    name: "mysdk_myjavalib@current",
-    sdk_member_name: "myjavalib",
-    device_supported: false,
-    host_supported: true,
+    visibility: ["//other/foo:__pkg__"],
     jars: ["java/myjavalib.jar"],
 }
 
 java_import {
-    name: "myjavalib",
+    name: "mysdk_mypublicjavalib@current",
+    sdk_member_name: "mypublicjavalib",
+    visibility: ["//visibility:public"],
+    jars: ["java/mypublicjavalib.jar"],
+}
+
+java_import {
+    name: "mypublicjavalib",
     prefer: false,
-    device_supported: false,
-    host_supported: true,
-    jars: ["java/myjavalib.jar"],
+    visibility: ["//visibility:public"],
+    jars: ["java/mypublicjavalib.jar"],
 }
 
-prebuilt_stubs_sources {
-    name: "mysdk_myjavaapistubs@current",
-    sdk_member_name: "myjavaapistubs",
-    device_supported: false,
-    host_supported: true,
-    srcs: ["java/myjavaapistubs_stubs_sources"],
+java_import {
+    name: "mysdk_mydefaultedjavalib@current",
+    sdk_member_name: "mydefaultedjavalib",
+    visibility: ["//other/bar:__pkg__"],
+    jars: ["java/mydefaultedjavalib.jar"],
 }
 
-prebuilt_stubs_sources {
-    name: "myjavaapistubs",
+java_import {
+    name: "mydefaultedjavalib",
     prefer: false,
-    device_supported: false,
-    host_supported: true,
-    srcs: ["java/myjavaapistubs_stubs_sources"],
-}
-
-cc_prebuilt_library_shared {
-    name: "mysdk_mynativelib@current",
-    sdk_member_name: "mynativelib",
-    device_supported: false,
-    host_supported: true,
-    arch: {
-        x86_64: {
-            srcs: ["x86_64/lib/mynativelib.so"],
-            export_include_dirs: [
-                "x86_64/include/include",
-                "x86_64/include_gen/mynativelib",
-            ],
-        },
-        x86: {
-            srcs: ["x86/lib/mynativelib.so"],
-            export_include_dirs: [
-                "x86/include/include",
-                "x86/include_gen/mynativelib",
-            ],
-        },
-    },
-    stl: "none",
-    system_shared_libs: [],
-}
-
-cc_prebuilt_library_shared {
-    name: "mynativelib",
-    prefer: false,
-    device_supported: false,
-    host_supported: true,
-    arch: {
-        x86_64: {
-            srcs: ["x86_64/lib/mynativelib.so"],
-            export_include_dirs: [
-                "x86_64/include/include",
-                "x86_64/include_gen/mynativelib",
-            ],
-        },
-        x86: {
-            srcs: ["x86/lib/mynativelib.so"],
-            export_include_dirs: [
-                "x86/include/include",
-                "x86/include_gen/mynativelib",
-            ],
-        },
-    },
-    stl: "none",
-    system_shared_libs: [],
+    visibility: ["//other/bar:__pkg__"],
+    jars: ["java/mydefaultedjavalib.jar"],
 }
 
 sdk_snapshot {
     name: "mysdk@current",
-    device_supported: false,
-    host_supported: true,
-    java_libs: ["mysdk_myjavalib@current"],
-    stubs_sources: ["mysdk_myjavaapistubs@current"],
-    native_shared_libs: ["mysdk_mynativelib@current"],
+    visibility: [
+        "//other/foo:__pkg__",
+        "//package:__subpackages__",
+    ],
+    java_header_libs: [
+        "mysdk_myjavalib@current",
+        "mysdk_mypublicjavalib@current",
+        "mysdk_mydefaultedjavalib@current",
+    ],
 }
-
-`)
-
-	var copySrcs []string
-	var copyDests []string
-	buildParams := sdk.BuildParamsForTests()
-	var zipBp android.BuildParams
-	for _, bp := range buildParams {
-		ruleString := bp.Rule.String()
-		if ruleString == "android/soong/android.Cp" {
-			copySrcs = append(copySrcs, bp.Input.String())
-			copyDests = append(copyDests, bp.Output.Rel()) // rooted at the snapshot root
-		} else if ruleString == "<local rule>:m.mysdk_linux_glibc_common.snapshot" {
-			zipBp = bp
-		}
-	}
-
-	buildDir := config.BuildDir()
-	ensureListContains(t, copySrcs, "aidl/foo/bar/Test.aidl")
-	ensureListContains(t, copySrcs, "include/Test.h")
-	ensureListContains(t, copySrcs, filepath.Join(buildDir, ".intermediates/mynativelib/linux_glibc_x86_64_shared/gen/aidl/aidl/foo/bar/BnTest.h"))
-	ensureListContains(t, copySrcs, filepath.Join(buildDir, ".intermediates/mynativelib/linux_glibc_x86_64_shared/gen/aidl/aidl/foo/bar/BpTest.h"))
-	ensureListContains(t, copySrcs, filepath.Join(buildDir, ".intermediates/mynativelib/linux_glibc_x86_64_shared/gen/aidl/aidl/foo/bar/Test.h"))
-	ensureListContains(t, copySrcs, filepath.Join(buildDir, ".intermediates/myjavalib/linux_glibc_common/javac/myjavalib.jar"))
-	ensureListContains(t, copySrcs, filepath.Join(buildDir, ".intermediates/mynativelib/linux_glibc_x86_64_shared/mynativelib.so"))
-
-	ensureListContains(t, copyDests, "aidl/aidl/foo/bar/Test.aidl")
-	ensureListContains(t, copyDests, "x86_64/include/include/Test.h")
-	ensureListContains(t, copyDests, "x86_64/include_gen/mynativelib/aidl/foo/bar/BnTest.h")
-	ensureListContains(t, copyDests, "x86_64/include_gen/mynativelib/aidl/foo/bar/BpTest.h")
-	ensureListContains(t, copyDests, "x86_64/include_gen/mynativelib/aidl/foo/bar/Test.h")
-	ensureListContains(t, copyDests, "java/myjavalib.jar")
-	ensureListContains(t, copyDests, "x86_64/lib/mynativelib.so")
-
-	// Ensure that the droidstubs .srcjar as repackaged into a temporary zip file
-	// and then merged together with the intermediate snapshot zip.
-	snapshotCreationInputs := zipBp.Implicits.Strings()
-	ensureListContains(t, snapshotCreationInputs,
-		filepath.Join(buildDir, ".intermediates/mysdk/linux_glibc_common/tmp/java/myjavaapistubs_stubs_sources.zip"))
-	ensureListContains(t, snapshotCreationInputs,
-		filepath.Join(buildDir, ".intermediates/mysdk/linux_glibc_common/mysdk-current.unmerged.zip"))
-	actual := zipBp.Output.String()
-	expected := filepath.Join(buildDir, ".intermediates/mysdk/linux_glibc_common/mysdk-current.zip")
-	if actual != expected {
-		t.Errorf("Expected snapshot output to be %q but was %q", expected, actual)
-	}
-}
-
-func checkSnapshotAndroidBpContents(t *testing.T, s *sdk, expectedContents string) {
-	t.Helper()
-	androidBpContents := strings.NewReplacer("\\n", "\n").Replace(s.GetAndroidBpContentsForTests())
-	if androidBpContents != expectedContents {
-		t.Errorf("Android.bp contents do not match, expected %s, actual %s", expectedContents, androidBpContents)
-	}
-}
-
-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 TestMain(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/testing.go b/sdk/testing.go
new file mode 100644
index 0000000..c9cc30f
--- /dev/null
+++ b/sdk/testing.go
@@ -0,0 +1,368 @@
+// 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
+	android.RegisterPackageBuildComponents(ctx)
+	ctx.PreArchMutators(android.RegisterVisibilityRuleChecker)
+	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
+	ctx.PreArchMutators(android.RegisterVisibilityRuleGatherer)
+	ctx.PostDepsMutators(android.RegisterVisibilityRuleEnforcer)
+
+	// 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
index 8159d3b..2731d50 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -16,19 +16,47 @@
 
 import (
 	"fmt"
-	"path/filepath"
 	"reflect"
 	"strings"
 
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
-	"android/soong/cc"
-	"android/soong/java"
 )
 
 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
@@ -74,105 +102,45 @@
 	rb.Build(pctx, ctx, gf.path.Base(), "Build "+gf.path.Base())
 }
 
-func (s *sdk) javaLibs(ctx android.ModuleContext) []android.SdkAware {
-	result := []android.SdkAware{}
+// 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) {
-		if j, ok := m.(*java.Library); ok {
-			result = append(result, j)
-		}
-	})
-	return result
-}
+		tag := ctx.OtherModuleDependencyTag(m)
+		if memberTag, ok := tag.(*sdkMemberDependencyTag); ok {
+			memberType := memberTag.memberType
 
-func (s *sdk) stubsSources(ctx android.ModuleContext) []android.SdkAware {
-	result := []android.SdkAware{}
-	ctx.VisitDirectDeps(func(m android.Module) {
-		if j, ok := m.(*java.Droidstubs); ok {
-			result = append(result, j)
-		}
-	})
-	return result
-}
-
-// archSpecificNativeLibInfo represents an arch-specific variant of a native lib
-type archSpecificNativeLibInfo struct {
-	name                      string
-	archType                  string
-	exportedIncludeDirs       android.Paths
-	exportedSystemIncludeDirs android.Paths
-	exportedFlags             []string
-	exportedDeps              android.Paths
-	outputFile                android.Path
-}
-
-func (lib *archSpecificNativeLibInfo) signature() string {
-	return fmt.Sprintf("%v %v %v %v",
-		lib.name,
-		lib.exportedIncludeDirs.Strings(),
-		lib.exportedSystemIncludeDirs.Strings(),
-		lib.exportedFlags)
-}
-
-// nativeLibInfo represents a collection of arch-specific modules having the same name
-type nativeLibInfo struct {
-	name         string
-	archVariants []archSpecificNativeLibInfo
-	// hasArchSpecificFlags is set to true if modules for each architecture all have the same
-	// include dirs, flags, etc, in which case only those of the first arch is selected.
-	hasArchSpecificFlags bool
-}
-
-// nativeMemberInfos collects all cc.Modules that are member of an SDK.
-func (s *sdk) nativeMemberInfos(ctx android.ModuleContext) []*nativeLibInfo {
-	infoMap := make(map[string]*nativeLibInfo)
-
-	// Collect cc.Modules
-	ctx.VisitDirectDeps(func(m android.Module) {
-		ccModule, ok := m.(*cc.Module)
-		if !ok {
-			return
-		}
-		depName := ctx.OtherModuleName(m)
-
-		if _, ok := infoMap[depName]; !ok {
-			infoMap[depName] = &nativeLibInfo{name: depName}
-		}
-
-		info := infoMap[depName]
-		info.archVariants = append(info.archVariants, archSpecificNativeLibInfo{
-			name:                      ccModule.BaseModuleName(),
-			archType:                  ccModule.Target().Arch.ArchType.String(),
-			exportedIncludeDirs:       ccModule.ExportedIncludeDirs(),
-			exportedSystemIncludeDirs: ccModule.ExportedSystemIncludeDirs(),
-			exportedFlags:             ccModule.ExportedFlags(),
-			exportedDeps:              ccModule.ExportedDeps(),
-			outputFile:                ccModule.OutputFile().Path(),
-		})
-	})
-
-	// Determine if include dirs and flags for each module are different across arch-specific
-	// modules or not. And set hasArchSpecificFlags accordingly
-	for _, info := range infoMap {
-		// by default, include paths and flags are assumed to be the same across arches
-		info.hasArchSpecificFlags = false
-		oldSignature := ""
-		for _, av := range info.archVariants {
-			newSignature := av.signature()
-			if oldSignature == "" {
-				oldSignature = newSignature
+			// 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())
 			}
-			if oldSignature != newSignature {
-				info.hasArchSpecificFlags = true
-				break
+
+			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...)
 	}
 
-	var list []*nativeLibInfo
-	for _, v := range infoMap {
-		list = append(list, v)
-	}
-	return list
+	return members
 }
 
 // SDK directory structure
@@ -193,44 +161,6 @@
 //         <arch>/lib/
 //            libFoo.so   : a stub library
 
-const (
-	nativeIncludeDir          = "include"
-	nativeGeneratedIncludeDir = "include_gen"
-	nativeStubDir             = "lib"
-	nativeStubFileSuffix      = ".so"
-)
-
-// path to the stub file of a native shared library. Relative to <sdk_root>/<api_dir>
-func nativeStubFilePathFor(lib archSpecificNativeLibInfo) string {
-	return filepath.Join(lib.archType,
-		nativeStubDir, lib.name+nativeStubFileSuffix)
-}
-
-// paths to the include dirs of a native shared library. Relative to <sdk_root>/<api_dir>
-func nativeIncludeDirPathsFor(ctx android.ModuleContext, lib archSpecificNativeLibInfo,
-	systemInclude bool, archSpecific bool) []string {
-	var result []string
-	var includeDirs []android.Path
-	if !systemInclude {
-		includeDirs = lib.exportedIncludeDirs
-	} else {
-		includeDirs = lib.exportedSystemIncludeDirs
-	}
-	for _, dir := range includeDirs {
-		var path string
-		if _, gen := dir.(android.WritablePath); gen {
-			path = filepath.Join(nativeGeneratedIncludeDir, lib.name)
-		} else {
-			path = filepath.Join(nativeIncludeDir, dir.String())
-		}
-		if archSpecific {
-			path = filepath.Join(lib.archType, path)
-		}
-		result = append(result, path)
-	}
-	return result
-}
-
 // 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 {
@@ -253,33 +183,20 @@
 		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
 
-	// copy exported AIDL files and stub jar files
-	javaLibs := s.javaLibs(ctx)
-	for _, m := range javaLibs {
-		m.BuildSnapshot(ctx, builder)
-	}
-
-	// copy stubs sources
-	stubsSources := s.stubsSources(ctx)
-	for _, m := range stubsSources {
-		m.BuildSnapshot(ctx, builder)
-	}
-
-	// copy exported header files and stub *.so files
-	nativeLibInfos := s.nativeMemberInfos(ctx)
-	for _, info := range nativeLibInfos {
-		buildSharedNativeLibSnapshot(ctx, info, 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()
+		versioned := unversioned.deepCopy()
 		name := versioned.properties["name"].(string)
 		versioned.setProperty("name", builder.versionedSdkMemberName(name))
 		versioned.insertAfter("name", "sdk_member_name", name)
@@ -292,17 +209,27 @@
 
 	// Create the snapshot module.
 	snapshotName := ctx.ModuleName() + string(android.SdkVersionSeparator) + builder.version
-	snapshotModule := bpFile.newModule("sdk_snapshot")
+	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)
-	if len(s.properties.Java_libs) > 0 {
-		snapshotModule.AddProperty("java_libs", builder.versionedSdkMemberNames(s.properties.Java_libs))
-	}
-	if len(s.properties.Stubs_sources) > 0 {
-		snapshotModule.AddProperty("stubs_sources", builder.versionedSdkMemberNames(s.properties.Stubs_sources))
-	}
-	if len(s.properties.Native_shared_libs) > 0 {
-		snapshotModule.AddProperty("native_shared_libs", builder.versionedSdkMemberNames(s.properties.Native_shared_libs))
+	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)
 
@@ -316,41 +243,39 @@
 
 	// zip them all
 	outputZipFile := android.PathForModuleOut(ctx, ctx.ModuleName()+"-current.zip").OutputPath
-	outputRuleName := "snapshot"
 	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 ruleName string
 	var desc string
 	if len(builder.zipsToMerge) == 0 {
 		zipFile = outputZipFile
-		ruleName = outputRuleName
 		desc = outputDesc
 	} else {
 		zipFile = android.PathForModuleOut(ctx, ctx.ModuleName()+"-current.unmerged.zip").OutputPath
-		ruleName = "intermediate snapshot"
 		desc = "Building intermediate snapshot for " + ctx.ModuleName()
 	}
 
-	rb := android.NewRuleBuilder()
-	rb.Command().
-		BuiltTool(ctx, "soong_zip").
-		FlagWithArg("-C ", builder.snapshotDir.String()).
-		FlagWithRspFileInputList("-l ", filesToZip).
-		FlagWithOutput("-o ", zipFile)
-	rb.Build(pctx, ctx, ruleName, desc)
+	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 {
-		rb := android.NewRuleBuilder()
-		rb.Command().
-			BuiltTool(ctx, "merge_zips").
-			Output(outputZipFile).
-			Input(zipFile).
-			Inputs(builder.zipsToMerge)
-		rb.Build(pctx, ctx, outputRuleName, outputDesc)
+		ctx.Build(pctx, android.BuildParams{
+			Description: outputDesc,
+			Rule:        mergeZips,
+			Input:       zipFile,
+			Inputs:      builder.zipsToMerge,
+			Output:      outputZipFile,
+		})
 	}
 
 	return outputZipFile
@@ -361,10 +286,9 @@
 	for _, bpModule := range bpFile.order {
 		contents.Printfln("")
 		contents.Printfln("%s {", bpModule.moduleType)
-		outputPropertySet(contents, &bpModule.bpPropertySet)
+		outputPropertySet(contents, bpModule.bpPropertySet)
 		contents.Printfln("}")
 	}
-	contents.Printfln("")
 }
 
 func outputPropertySet(contents *generatedContents, set *bpPropertySet) {
@@ -413,104 +337,17 @@
 	return contents.content.String()
 }
 
-func buildSharedNativeLibSnapshot(ctx android.ModuleContext, info *nativeLibInfo, builder android.SnapshotBuilder) {
-	// a function for emitting include dirs
-	printExportedDirCopyCommandsForNativeLibs := func(lib archSpecificNativeLibInfo) {
-		includeDirs := lib.exportedIncludeDirs
-		includeDirs = append(includeDirs, lib.exportedSystemIncludeDirs...)
-		if len(includeDirs) == 0 {
-			return
-		}
-		for _, dir := range includeDirs {
-			if _, gen := dir.(android.WritablePath); gen {
-				// generated headers are copied via exportedDeps. See below.
-				continue
-			}
-			targetDir := nativeIncludeDir
-			if info.hasArchSpecificFlags {
-				targetDir = filepath.Join(lib.archType, targetDir)
-			}
-
-			// TODO(jiyong) copy headers having other suffixes
-			headers, _ := ctx.GlobWithDeps(dir.String()+"/**/*.h", nil)
-			for _, file := range headers {
-				src := android.PathForSource(ctx, file)
-				dest := filepath.Join(targetDir, file)
-				builder.CopyToSnapshot(src, dest)
-			}
-		}
-
-		genHeaders := lib.exportedDeps
-		for _, file := range genHeaders {
-			targetDir := nativeGeneratedIncludeDir
-			if info.hasArchSpecificFlags {
-				targetDir = filepath.Join(lib.archType, targetDir)
-			}
-			dest := filepath.Join(targetDir, lib.name, file.Rel())
-			builder.CopyToSnapshot(file, dest)
-		}
-	}
-
-	if !info.hasArchSpecificFlags {
-		printExportedDirCopyCommandsForNativeLibs(info.archVariants[0])
-	}
-
-	// for each architecture
-	for _, av := range info.archVariants {
-		builder.CopyToSnapshot(av.outputFile, nativeStubFilePathFor(av))
-
-		if info.hasArchSpecificFlags {
-			printExportedDirCopyCommandsForNativeLibs(av)
-		}
-	}
-
-	info.generatePrebuiltLibrary(ctx, builder)
-}
-
-func (info *nativeLibInfo) generatePrebuiltLibrary(ctx android.ModuleContext, builder android.SnapshotBuilder) {
-
-	// a function for emitting include dirs
-	addExportedDirsForNativeLibs := func(lib archSpecificNativeLibInfo, properties android.BpPropertySet, systemInclude bool) {
-		includeDirs := nativeIncludeDirPathsFor(ctx, lib, systemInclude, info.hasArchSpecificFlags)
-		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(info.name, "cc_prebuilt_library_shared")
-
-	if !info.hasArchSpecificFlags {
-		addExportedDirsForNativeLibs(info.archVariants[0], pbm, false /*systemInclude*/)
-		addExportedDirsForNativeLibs(info.archVariants[0], pbm, true /*systemInclude*/)
-	}
-
-	archProperties := pbm.AddPropertySet("arch")
-	for _, av := range info.archVariants {
-		archTypeProperties := archProperties.AddPropertySet(av.archType)
-		archTypeProperties.AddProperty("srcs", []string{nativeStubFilePathFor(av)})
-		if info.hasArchSpecificFlags {
-			// 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{})
-}
-
 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
 
@@ -519,13 +356,22 @@
 }
 
 func (s *snapshotBuilder) CopyToSnapshot(src android.Path, dest string) {
-	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)
+	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) {
@@ -534,26 +380,37 @@
 	// 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
-	rb := android.NewRuleBuilder()
-	rb.Command().
-		BuiltTool(ctx, "zip2zip").
-		FlagWithInput("-i ", zipPath).
-		FlagWithOutput("-o ", tmpZipPath).
-		Flag("**/*:" + destDir)
-	rb.Build(pctx, ctx, "repackaging "+destDir,
-		"Repackaging zip file "+destDir+" for snapshot "+ctx.ModuleName())
+
+	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(name string, moduleType string) android.BpModule {
+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
@@ -582,3 +439,19 @@
 	}
 	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 a876341..ce404f8 100644
--- a/sysprop/sysprop_library.go
+++ b/sysprop/sysprop_library.go
@@ -35,6 +35,7 @@
 type syspropGenProperties struct {
 	Srcs  []string `android:"path"`
 	Scope string
+	Name  *string
 }
 
 type syspropJavaGenRule struct {
@@ -142,6 +143,9 @@
 
 	// list of .sysprop files which defines the properties.
 	Srcs []string `android:"path"`
+
+	// Whether public stub exists or not.
+	Public_stub *bool `blueprint:"mutated"`
 }
 
 var (
@@ -157,18 +161,37 @@
 	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()
 
@@ -274,6 +297,36 @@
 	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.properties.Srcs) == 0 {
 		ctx.PropertyErrorf("srcs", "sysprop_library must specify srcs")
@@ -304,120 +357,107 @@
 		return
 	}
 
-	socSpecific := ctx.SocSpecific()
-	deviceSpecific := ctx.DeviceSpecific()
-	productSpecific := ctx.ProductSpecific()
-
-	owner := m.properties.Property_owner
+	// 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 owner {
+	switch m.Owner() {
 	case "Platform":
 		// Every partition can access platform-defined properties
 		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
-		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
-	}{}
-
+	ccProps := ccLibraryProperties{}
 	ccProps.Name = proptools.StringPtr(m.CcModuleName())
 	ccProps.Srcs = m.properties.Srcs
-	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.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)
 
-	// internal scope contains all properties
-	// public scope only contains public properties
-	// use public if the owner is different from client
 	scope := "internal"
-	isProduct := ctx.ProductSpecific()
-	isVendor := ctx.SocSpecific()
-	isOwnerPlatform := owner == "Platform"
 
-	if isProduct {
-		// product can't own any sysprop_library now, so product must use public scope
+	// 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 isVendor && !isOwnerPlatform {
-		// vendor and odm can't use system's internal property.
+	} else if isOwnerPlatform && installedInVendorOrOdm {
+		// Vendor or Odm should use public version of Platform's sysprop_library.
 		scope = "public"
 	}
 
-	javaGenProps := struct {
-		Srcs  []string
-		Scope string
-		Name  *string
-	}{
+	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()),
+		})
 	}
-
-	ctx.CreateModule(syspropJavaGenFactory, &javaGenProps)
-
-	javaProps := struct {
-		Name             *string
-		Srcs             []string
-		Soc_specific     *bool
-		Device_specific  *bool
-		Product_specific *bool
-		Required         []string
-		Sdk_version      *string
-		Installable      *bool
-		Libs             []string
-	}{}
-
-	javaProps.Name = proptools.StringPtr(m.BaseModuleName())
-	javaProps.Srcs = []string{":" + *javaGenProps.Name}
-	javaProps.Soc_specific = proptools.BoolPtr(socSpecific)
-	javaProps.Device_specific = proptools.BoolPtr(deviceSpecific)
-	javaProps.Product_specific = proptools.BoolPtr(productSpecific)
-	javaProps.Installable = m.properties.Installable
-	javaProps.Sdk_version = proptools.StringPtr("core_current")
-	javaProps.Libs = []string{stub}
-
-	ctx.CreateModule(java.LibraryFactory, &javaProps)
 }
 
 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 5e0eb35..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,44 +53,42 @@
 	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", java.AndroidAppFactory)
-	ctx.RegisterModuleType("java_library", java.LibraryFactory)
-	ctx.RegisterModuleType("java_system_modules", java.SystemModulesFactory)
-	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.BottomUp("sysprop_deps", syspropDepsMutator).Parallel()
 	})
 
-	ctx.RegisterModuleType("cc_library", cc.LibraryFactory)
-	ctx.RegisterModuleType("cc_library_headers", cc.LibraryHeaderFactory)
-	ctx.RegisterModuleType("cc_library_static", cc.LibraryFactory)
-	ctx.RegisterModuleType("cc_object", cc.ObjectFactory)
-	ctx.RegisterModuleType("llndk_library", cc.LlndkLibraryFactory)
-	ctx.RegisterModuleType("toolchain_library", cc.ToolchainLibraryFactory)
+	cc.RegisterRequiredBuildComponentsForTest(ctx)
 	ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.BottomUp("image", android.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", 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,
@@ -135,21 +134,7 @@
 		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"})
-	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")
@@ -161,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
@@ -211,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",
@@ -285,10 +277,10 @@
 
 	// Check for generated cc_library
 	for _, variant := range []string{
-		"android_arm_armv7-a-neon_vendor.VER_shared",
-		"android_arm_armv7-a-neon_vendor.VER_static",
-		"android_arm64_armv8-a_vendor.VER_shared",
-		"android_arm64_armv8-a_vendor.VER_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",
 	} {
 		ctx.ModuleForTests("libsysprop-platform", variant)
 		ctx.ModuleForTests("libsysprop-vendor", variant)
@@ -296,10 +288,10 @@
 	}
 
 	for _, variant := range []string{
-		"android_arm_armv7-a-neon_core_shared",
-		"android_arm_armv7-a-neon_core_static",
-		"android_arm64_armv8-a_core_shared",
-		"android_arm64_armv8-a_core_static",
+		"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)
 
@@ -308,20 +300,21 @@
 	}
 
 	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.VER_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.VER_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.VER_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"]
@@ -360,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/ui/build/Android.bp b/ui/build/Android.bp
index 6f81c17..2a5a51a 100644
--- a/ui/build/Android.bp
+++ b/ui/build/Android.bp
@@ -59,6 +59,7 @@
         "util.go",
     ],
     testSrcs: [
+        "cleanbuild_test.go",
         "config_test.go",
         "environment_test.go",
         "util_test.go",
@@ -66,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 7dfb900..69ef003 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -19,6 +19,8 @@
 	"os"
 	"path/filepath"
 	"text/template"
+
+	"android/soong/ui/metrics"
 )
 
 // Ensures the out directory exists, and has the proper files to prevent kati
@@ -137,6 +139,10 @@
 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)
+
+	ctx.BeginTrace(metrics.Total, "total")
+	defer ctx.EndTrace()
 
 	if config.SkipMake() {
 		ctx.Verboseln("Skipping Make/Kati as requested")
diff --git a/ui/build/cleanbuild.go b/ui/build/cleanbuild.go
index 0b44b4d..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"
@@ -177,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 565f033..c084171 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -50,10 +50,14 @@
 	targetDevice    string
 	targetDeviceDir string
 
+	// Autodetected
+	totalRAM uint64
+
 	pdkBuild bool
 
-	brokenDupRules    bool
-	brokenUsesNetwork bool
+	brokenDupRules     bool
+	brokenUsesNetwork  bool
+	brokenNinjaEnvVars []string
 
 	pathReplaced bool
 }
@@ -96,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
@@ -716,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)
@@ -898,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/dumpvars.go b/ui/build/dumpvars.go
index 4270bb1..c3da38b 100644
--- a/ui/build/dumpvars.go
+++ b/ui/build/dumpvars.go
@@ -216,6 +216,9 @@
 		// 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",
 
@@ -284,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/kati.go b/ui/build/kati.go
index 307475a..a845c5b 100644
--- a/ui/build/kati.go
+++ b/ui/build/kati.go
@@ -151,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) {
diff --git a/ui/build/ninja.go b/ui/build/ninja.go
index d5baafe..0b56b67 100644
--- a/ui/build/ninja.go
+++ b/ui/build/ninja.go
@@ -18,6 +18,7 @@
 	"fmt"
 	"os"
 	"path/filepath"
+	"sort"
 	"strconv"
 	"strings"
 	"time"
@@ -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)
diff --git a/ui/build/paths/config.go b/ui/build/paths/config.go
index 909d93c..bfe662d 100644
--- a/ui/build/paths/config.go
+++ b/ui/build/paths/config.go
@@ -75,7 +75,6 @@
 
 var Configuration = map[string]PathConfig{
 	"bash":     Allowed,
-	"bc":       Allowed,
 	"dd":       Allowed,
 	"diff":     Allowed,
 	"dlv":      Allowed,
@@ -83,19 +82,15 @@
 	"fuser":    Allowed,
 	"getopt":   Allowed,
 	"git":      Allowed,
-	"gzcat":    Allowed,
-	"gzip":     Allowed,
 	"hexdump":  Allowed,
 	"jar":      Allowed,
 	"java":     Allowed,
 	"javap":    Allowed,
 	"lsof":     Allowed,
-	"m4":       Log,
 	"openssl":  Allowed,
 	"patch":    Allowed,
 	"pstree":   Allowed,
 	"python3":  Allowed,
-	"realpath": Allowed,
 	"rsync":    Allowed,
 	"sh":       Allowed,
 	"tr":       Allowed,
@@ -120,9 +115,6 @@
 	"pgrep": LinuxOnlyPrebuilt,
 	"pkill": LinuxOnlyPrebuilt,
 	"ps":    LinuxOnlyPrebuilt,
-
-	// The toybox xargs is currently breaking the mac build.
-	"xargs": LinuxOnlyPrebuilt,
 }
 
 func init() {
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 3388417..afbc073 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -119,6 +119,7 @@
 			"-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.RunAndStreamOrFatal()
 	}
diff --git a/ui/metrics/metrics.go b/ui/metrics/metrics.go
index bc86f0a..8254e4a 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 (
@@ -30,6 +30,7 @@
 	RunSoong     = "soong"
 	PrimaryNinja = "ninja"
 	TestRun      = "test"
+	Total        = "total"
 )
 
 type Metrics struct {
@@ -56,6 +57,8 @@
 	case PrimaryNinja:
 		m.metrics.NinjaRuns = append(m.metrics.NinjaRuns, &perf)
 		break
+	case Total:
+		m.metrics.Total = &perf
 	default:
 		// ignored
 	}
@@ -137,13 +140,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..3986d0e 100644
--- a/ui/metrics/metrics_proto/metrics.pb.go
+++ b/ui/metrics/metrics_proto/metrics.pb.go
@@ -195,10 +195,12 @@
 	// The metrics for calling Soong.
 	SoongRuns []*PerfInfo `protobuf:"bytes,19,rep,name=soong_runs,json=soongRuns" json:"soong_runs,omitempty"`
 	// The metrics for calling Ninja.
-	NinjaRuns            []*PerfInfo `protobuf:"bytes,20,rep,name=ninja_runs,json=ninjaRuns" json:"ninja_runs,omitempty"`
-	XXX_NoUnkeyedLiteral struct{}    `json:"-"`
-	XXX_unrecognized     []byte      `json:"-"`
-	XXX_sizecache        int32       `json:"-"`
+	NinjaRuns []*PerfInfo `protobuf:"bytes,20,rep,name=ninja_runs,json=ninjaRuns" json:"ninja_runs,omitempty"`
+	// The metrics for the whole build
+	Total                *PerfInfo `protobuf:"bytes,21,opt,name=total" json:"total,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}  `json:"-"`
+	XXX_unrecognized     []byte    `json:"-"`
+	XXX_sizecache        int32     `json:"-"`
 }
 
 func (m *MetricsBase) Reset()         { *m = MetricsBase{} }
@@ -371,6 +373,13 @@
 	return nil
 }
 
+func (m *MetricsBase) GetTotal() *PerfInfo {
+	if m != nil {
+		return m.Total
+	}
+	return nil
+}
+
 type PerfInfo struct {
 	// The description for the phase/action/part while the tool running.
 	Desc *string `protobuf:"bytes,1,opt,name=desc" json:"desc,omitempty"`
@@ -509,6 +518,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 +614,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
-	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,
+	// 847 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x55, 0xdd, 0x6e, 0xdb, 0x36,
+	0x14, 0xae, 0x12, 0x25, 0xb6, 0x8e, 0x62, 0x57, 0x65, 0x52, 0x54, 0x5d, 0x11, 0xcc, 0x10, 0xd6,
+	0x21, 0x17, 0xab, 0x5b, 0x78, 0x45, 0x50, 0x18, 0xc5, 0x80, 0xc4, 0x31, 0x8a, 0x2e, 0xb0, 0x5d,
+	0x28, 0x71, 0x57, 0x6c, 0x17, 0x02, 0x23, 0xd1, 0x8d, 0x3a, 0x4b, 0x14, 0x48, 0xaa, 0x98, 0x1f,
+	0x62, 0x0f, 0xb9, 0x8b, 0xbd, 0xc7, 0xc0, 0x43, 0xc9, 0x51, 0x00, 0x0f, 0x09, 0x7a, 0x47, 0x9d,
+	0xef, 0x87, 0xdf, 0xa1, 0xc4, 0x23, 0xe8, 0x64, 0x4c, 0x89, 0x34, 0x96, 0xfd, 0x42, 0x70, 0xc5,
+	0xc9, 0xbe, 0xe4, 0x3c, 0xff, 0x1c, 0x5d, 0x95, 0xe9, 0x32, 0x89, 0x2a, 0x28, 0xf8, 0xc7, 0x01,
+	0x77, 0x62, 0xd6, 0xa7, 0x54, 0x32, 0xf2, 0x0a, 0x0e, 0x0c, 0x21, 0xa1, 0x8a, 0x45, 0x2a, 0xcd,
+	0x98, 0x54, 0x34, 0x2b, 0x7c, 0xab, 0x67, 0x1d, 0x6d, 0x87, 0x04, 0xb1, 0x33, 0xaa, 0xd8, 0x65,
+	0x8d, 0x90, 0xa7, 0xd0, 0x36, 0x8a, 0x34, 0xf1, 0xb7, 0x7a, 0xd6, 0x91, 0x13, 0xb6, 0xf0, 0xf9,
+	0x7d, 0x42, 0x86, 0xf0, 0xb4, 0x58, 0x52, 0xb5, 0xe0, 0x22, 0x8b, 0xbe, 0x32, 0x21, 0x53, 0x9e,
+	0x47, 0x31, 0x4f, 0x58, 0x4e, 0x33, 0xe6, 0x6f, 0x23, 0xf7, 0x49, 0x4d, 0xf8, 0x68, 0xf0, 0x51,
+	0x05, 0x93, 0xe7, 0xd0, 0x55, 0x54, 0x7c, 0x66, 0x2a, 0x2a, 0x04, 0x4f, 0xca, 0x58, 0xf9, 0x36,
+	0x0a, 0x3a, 0xa6, 0xfa, 0xc1, 0x14, 0x49, 0x02, 0x07, 0x15, 0xcd, 0x84, 0xf8, 0x4a, 0x45, 0x4a,
+	0x73, 0xe5, 0xef, 0xf4, 0xac, 0xa3, 0xee, 0xe0, 0x45, 0x7f, 0x43, 0xcf, 0xfd, 0x46, 0xbf, 0xfd,
+	0x53, 0x8d, 0x7c, 0x34, 0xa2, 0xe1, 0xf6, 0x78, 0xfa, 0x2e, 0x24, 0xc6, 0xaf, 0x09, 0x90, 0x19,
+	0xb8, 0xd5, 0x2e, 0x54, 0xc4, 0xd7, 0xfe, 0x2e, 0x9a, 0x3f, 0xbf, 0xd3, 0xfc, 0x44, 0xc4, 0xd7,
+	0xc3, 0xd6, 0x7c, 0x7a, 0x3e, 0x9d, 0xfd, 0x36, 0x0d, 0xc1, 0x58, 0xe8, 0x22, 0xe9, 0xc3, 0x7e,
+	0xc3, 0x70, 0x9d, 0xba, 0x85, 0x2d, 0x3e, 0xba, 0x21, 0xd6, 0x01, 0x7e, 0x82, 0x2a, 0x56, 0x14,
+	0x17, 0xe5, 0x9a, 0xde, 0x46, 0xba, 0x67, 0x90, 0x51, 0x51, 0xd6, 0xec, 0x73, 0x70, 0xae, 0xb9,
+	0xac, 0xc2, 0x3a, 0xdf, 0x14, 0xb6, 0xad, 0x0d, 0x30, 0x6a, 0x08, 0x1d, 0x34, 0x1b, 0xe4, 0x89,
+	0x31, 0x84, 0x6f, 0x32, 0x74, 0xb5, 0xc9, 0x20, 0x4f, 0xd0, 0xf3, 0x09, 0xb4, 0xd0, 0x93, 0x4b,
+	0xdf, 0xc5, 0x1e, 0x76, 0xf5, 0xe3, 0x4c, 0x92, 0xa0, 0xda, 0x8c, 0xcb, 0x88, 0xfd, 0xa5, 0x04,
+	0xf5, 0xf7, 0x10, 0x76, 0x0d, 0x3c, 0xd6, 0xa5, 0x35, 0x27, 0x16, 0x5c, 0x4a, 0x6d, 0xd1, 0xb9,
+	0xe1, 0x8c, 0x74, 0x6d, 0x26, 0xc9, 0x8f, 0xf0, 0xb0, 0xc1, 0xc1, 0xd8, 0x5d, 0xf3, 0xf9, 0xac,
+	0x59, 0x18, 0xe4, 0x05, 0xec, 0x37, 0x78, 0xeb, 0x16, 0x1f, 0x9a, 0x83, 0x5d, 0x73, 0x1b, 0xb9,
+	0x79, 0xa9, 0xa2, 0x24, 0x15, 0xbe, 0x67, 0x72, 0xf3, 0x52, 0x9d, 0xa5, 0x82, 0xfc, 0x02, 0xae,
+	0x64, 0xaa, 0x2c, 0x22, 0xc5, 0xf9, 0x52, 0xfa, 0x8f, 0x7a, 0xdb, 0x47, 0xee, 0xe0, 0x70, 0xe3,
+	0x11, 0x7d, 0x60, 0x62, 0xf1, 0x3e, 0x5f, 0xf0, 0x10, 0x50, 0x71, 0xa9, 0x05, 0x64, 0x08, 0xce,
+	0x9f, 0x54, 0xa5, 0x91, 0x28, 0x73, 0xe9, 0x93, 0xfb, 0xa8, 0xdb, 0x9a, 0x1f, 0x96, 0xb9, 0x24,
+	0x6f, 0x01, 0x0c, 0x13, 0xc5, 0xfb, 0xf7, 0x11, 0x3b, 0x88, 0xd6, 0xea, 0x3c, 0xcd, 0xbf, 0x50,
+	0xa3, 0x3e, 0xb8, 0x97, 0x1a, 0x05, 0xa8, 0xfe, 0x19, 0x76, 0x14, 0x57, 0x74, 0xe9, 0x3f, 0xee,
+	0x59, 0x77, 0x0b, 0x0d, 0x37, 0x78, 0x05, 0x7b, 0xb7, 0x6e, 0x57, 0x1b, 0xec, 0xf9, 0xc5, 0x38,
+	0xf4, 0x1e, 0x90, 0x0e, 0x38, 0x7a, 0x75, 0x36, 0x3e, 0x9d, 0xbf, 0xf3, 0x2c, 0xd2, 0x02, 0x7d,
+	0x23, 0xbd, 0xad, 0xe0, 0x2d, 0xd8, 0x78, 0xfe, 0x2e, 0xd4, 0xdf, 0x93, 0xf7, 0x40, 0xa3, 0x27,
+	0xe1, 0xc4, 0xb3, 0x88, 0x03, 0x3b, 0x27, 0xe1, 0xe4, 0xf8, 0xb5, 0xb7, 0xa5, 0x6b, 0x9f, 0xde,
+	0x1c, 0x7b, 0xdb, 0x04, 0x60, 0xf7, 0xd3, 0x9b, 0xe3, 0xe8, 0xf8, 0xb5, 0x67, 0x07, 0x7f, 0x5b,
+	0xd0, 0xae, 0x33, 0x10, 0x02, 0x76, 0xc2, 0x64, 0x8c, 0x03, 0xcd, 0x09, 0x71, 0xad, 0x6b, 0x38,
+	0x92, 0xcc, 0xf8, 0xc2, 0x35, 0x39, 0x04, 0x90, 0x8a, 0x0a, 0x85, 0x33, 0x10, 0x87, 0x95, 0x1d,
+	0x3a, 0x58, 0xd1, 0xa3, 0x8f, 0x3c, 0x03, 0x47, 0x30, 0xba, 0x34, 0xa8, 0x8d, 0x68, 0x5b, 0x17,
+	0x10, 0x3c, 0x04, 0xc8, 0x58, 0xc6, 0xc5, 0x2a, 0x2a, 0x25, 0xc3, 0x51, 0x64, 0x87, 0x8e, 0xa9,
+	0xcc, 0x25, 0x0b, 0xfe, 0xb5, 0xa0, 0x3b, 0xe1, 0x49, 0xb9, 0x64, 0x97, 0xab, 0x82, 0x61, 0xaa,
+	0x3f, 0x60, 0xcf, 0x9c, 0x99, 0x5c, 0x49, 0xc5, 0x32, 0x4c, 0xd7, 0x1d, 0xbc, 0xdc, 0x7c, 0xc7,
+	0x6e, 0x49, 0xcd, 0x04, 0xbb, 0x40, 0x59, 0xe3, 0xb6, 0x5d, 0xdd, 0x54, 0xc9, 0xf7, 0xe0, 0x66,
+	0xa8, 0x89, 0xd4, 0xaa, 0xa8, 0xbb, 0x84, 0x6c, 0x6d, 0x43, 0x7e, 0x80, 0x6e, 0x5e, 0x66, 0x11,
+	0x5f, 0x44, 0xa6, 0x28, 0xb1, 0xdf, 0x4e, 0xb8, 0x97, 0x97, 0xd9, 0x6c, 0x61, 0xf6, 0x93, 0xc1,
+	0x4b, 0x70, 0x1b, 0x7b, 0xdd, 0x7e, 0x17, 0x0e, 0xec, 0x5c, 0xcc, 0x66, 0x53, 0xfd, 0xd2, 0xda,
+	0x60, 0x4f, 0x4e, 0xce, 0xc7, 0xde, 0x56, 0xb0, 0x84, 0xef, 0x46, 0x22, 0x55, 0x69, 0x4c, 0x97,
+	0x73, 0xc9, 0xc4, 0xaf, 0xbc, 0x14, 0x39, 0x5b, 0x55, 0x23, 0x62, 0x7d, 0xe8, 0x56, 0xe3, 0xd0,
+	0x87, 0xd0, 0xaa, 0xba, 0xc4, 0x94, 0xee, 0xa0, 0x77, 0xd7, 0x94, 0x09, 0x6b, 0x41, 0x70, 0x05,
+	0xcf, 0x36, 0xec, 0x26, 0xeb, 0xed, 0x46, 0x60, 0xc7, 0xe5, 0x17, 0xe9, 0x5b, 0xf8, 0x85, 0x6f,
+	0x3e, 0xd9, 0xff, 0x4f, 0x1b, 0xa2, 0xf8, 0xf4, 0xf1, 0xef, 0xd5, 0x4f, 0xb4, 0x52, 0x44, 0xf8,
+	0x67, 0xfd, 0x2f, 0x00, 0x00, 0xff, 0xff, 0xc4, 0xbd, 0xe2, 0xb1, 0x69, 0x07, 0x00, 0x00,
 }
diff --git a/ui/metrics/metrics_proto/metrics.proto b/ui/metrics/metrics_proto/metrics.proto
index 93034eb..194aa6b 100644
--- a/ui/metrics/metrics_proto/metrics.proto
+++ b/ui/metrics/metrics_proto/metrics.proto
@@ -89,6 +89,9 @@
 
   // The metrics for calling Ninja.
   repeated PerfInfo ninja_runs = 20;
+
+  // The metrics for the whole build
+  optional PerfInfo total = 21;
 }
 
 message PerfInfo {
@@ -125,3 +128,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/critical_path.go b/ui/status/critical_path.go
index 444327b..8065c60 100644
--- a/ui/status/critical_path.go
+++ b/ui/status/critical_path.go
@@ -112,8 +112,10 @@
 		if !cp.start.IsZero() {
 			elapsedTime := cp.end.Sub(cp.start).Round(time.Second)
 			cp.log.Verbosef("elapsed time %s", elapsedTime.String())
-			cp.log.Verbosef("perfect parallelism ratio %d%%",
-				int(float64(criticalTime)/float64(elapsedTime)*100))
+			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-- {
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/xml/xml_test.go b/xml/xml_test.go
index 0a11566..f8ec823 100644
--- a/xml/xml_test.go
+++ b/xml/xml_test.go
@@ -48,20 +48,18 @@
 }
 
 func testXml(t *testing.T, bp string) *android.TestContext {
-	config := android.TestArchConfig(buildDir, nil)
+	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()
-	mockFiles := map[string][]byte{
-		"Android.bp": []byte(bp),
-		"foo.xml":    nil,
-		"foo.dtd":    nil,
-		"bar.xml":    nil,
-		"bar.xsd":    nil,
-		"baz.xml":    nil,
-	}
-	ctx.MockFileSystem(mockFiles)
+	ctx.Register(config)
 	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
 	android.FailIfErrored(t, errs)
 	_, errs = ctx.PrepareBuildActions(config)