Merge "Install flattened/unflattend apexes for GSI"
diff --git a/Android.bp b/Android.bp
index 6910d75..f72d624 100644
--- a/Android.bp
+++ b/Android.bp
@@ -508,7 +508,10 @@
         "sdk/update.go",
     ],
     testSrcs: [
+        "sdk/cc_sdk_test.go",
+        "sdk/java_sdk_test.go",
         "sdk/sdk_test.go",
+        "sdk/testing.go",
     ],
     pluginFor: ["soong_build"],
 }
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..f3c15e4 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
 }
 
@@ -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/config.go b/android/config.go
index 687cf9b..e1db55d 100644
--- a/android/config.go
+++ b/android/config.go
@@ -418,6 +418,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 {
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/image.go b/android/image.go
index 5ec1b16..8424cf8 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)
@@ -48,9 +48,9 @@
 	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/mutator.go b/android/mutator.go
index 0d253eb..e9ccd7f 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -78,17 +78,18 @@
 	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("arch", archMutator).Parallel()
 	ctx.TopDown("arch_hooks", archHookMutator).Parallel()
+	ctx.BottomUp("image", imageMutator).Parallel()
 }
 
 var preDeps = []RegisterMutatorFunc{
@@ -98,7 +99,7 @@
 var postDeps = []RegisterMutatorFunc{
 	registerPathDepsMutator,
 	RegisterPrebuiltsPostDepsMutators,
-	registerVisibilityRuleEnforcer,
+	RegisterVisibilityRuleEnforcer,
 	registerNeverallowMutator,
 	RegisterOverridePostDepsMutators,
 }
diff --git a/android/override_module.go b/android/override_module.go
index f946587..45f7be0 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
+	properties overridableModuleProperties
 }
 
 func InitOverridableModule(m OverridableModule, overridesProperty *[]string) {
 	m.setOverridableProperties(m.(Module).GetProperties())
 	m.setOverridesProperty(overridesProperty)
+	m.AddProperties(&m.moduleBase().properties)
+}
+
+func (o *OverridableModuleBase) moduleBase() *OverridableModuleBase {
+	return o
 }
 
 func (b *OverridableModuleBase) setOverridableProperties(prop []interface{}) {
@@ -162,11 +174,15 @@
 			}
 		}
 	}
-	b.overriddenBy = o.Name()
+	b.properties.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.properties.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..ed604c6 100644
--- a/android/package.go
+++ b/android/package.go
@@ -98,7 +98,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..d3527fa 100644
--- a/android/package_ctx.go
+++ b/android/package_ctx.go
@@ -16,7 +16,6 @@
 
 import (
 	"fmt"
-	"runtime"
 	"strings"
 
 	"github.com/google/blueprint"
@@ -177,46 +176,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..8071c51 100644
--- a/android/package_test.go
+++ b/android/package_test.go
@@ -88,7 +88,7 @@
 
 	ctx := NewTestArchContext()
 	ctx.RegisterModuleType("package", PackageFactory)
-	ctx.PreArchMutators(registerPackageRenamer)
+	ctx.PreArchMutators(RegisterPackageRenamer)
 	ctx.Register()
 
 	ctx.MockFileSystem(fs)
diff --git a/android/paths.go b/android/paths.go
index 1a37a34..85c861d 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -512,8 +512,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)
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..6bb3ccf 100644
--- a/android/prebuilt_etc_test.go
+++ b/android/prebuilt_etc_test.go
@@ -29,9 +29,6 @@
 	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),
@@ -145,7 +142,7 @@
 	}
 
 	mod := ctx.ModuleForTests("foo", "android_arm64_armv8-a_core").Module().(*PrebuiltEtc)
-	entries := AndroidMkEntriesForTest(t, config, "", mod)
+	entries := AndroidMkEntriesForTest(t, config, "", mod)[0]
 	for k, expectedValue := range expected {
 		if value, ok := entries.EntryMap[k]; ok {
 			if !reflect.DeepEqual(value, expectedValue) {
diff --git a/android/sdk.go b/android/sdk.go
index 01e18ed..533bd0e 100644
--- a/android/sdk.go
+++ b/android/sdk.go
@@ -17,6 +17,7 @@
 import (
 	"strings"
 
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
 
@@ -31,9 +32,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 +178,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 +201,56 @@
 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.
+//
+//    var ModuleTypeSdkMemberType = newModuleTypeSdkMemberType()
+//
+//    func newModuleTypeSdkMemberType() android.SdkMemberType {
+//    	return &moduleTypeSdkMemberType{}
+//    }
+//
+//    type moduleTypeSdkMemberType struct {
+//    }
+//
+//    ...methods...
+//
+type SdkMemberType interface {
+	// 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)
+}
diff --git a/android/sh_binary.go b/android/sh_binary.go
index 2b649c4..3293d4f 100644
--- a/android/sh_binary.go
+++ b/android/sh_binary.go
@@ -135,8 +135,8 @@
 	})
 }
 
-func (s *ShBinary) AndroidMkEntries() AndroidMkEntries {
-	return AndroidMkEntries{
+func (s *ShBinary) AndroidMkEntries() []AndroidMkEntries {
+	return []AndroidMkEntries{AndroidMkEntries{
 		Class:      "EXECUTABLES",
 		OutputFile: OptionalPathForPath(s.outputFilePath),
 		Include:    "$(BUILD_SYSTEM)/soong_cc_prebuilt.mk",
@@ -145,7 +145,7 @@
 				s.customAndroidMkEntries(entries)
 			},
 		},
-	}
+	}}
 }
 
 func (s *ShBinary) customAndroidMkEntries(entries *AndroidMkEntries) {
@@ -163,8 +163,8 @@
 	s.data = PathsForModuleSrc(ctx, s.testProperties.Data)
 }
 
-func (s *ShTest) AndroidMkEntries() AndroidMkEntries {
-	return AndroidMkEntries{
+func (s *ShTest) AndroidMkEntries() []AndroidMkEntries {
+	return []AndroidMkEntries{AndroidMkEntries{
 		Class:      "NATIVE_TESTS",
 		OutputFile: OptionalPathForPath(s.outputFilePath),
 		Include:    "$(BUILD_SYSTEM)/soong_cc_prebuilt.mk",
@@ -185,7 +185,7 @@
 				}
 			},
 		},
-	}
+	}}
 }
 
 func InitShBinaryModule(s *ShBinary) {
diff --git a/android/sh_binary_test.go b/android/sh_binary_test.go
index a138754..8488fe4 100644
--- a/android/sh_binary_test.go
+++ b/android/sh_binary_test.go
@@ -42,7 +42,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/testing.go b/android/testing.go
index 4b55920..26f1e4d 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -403,15 +403,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 {
diff --git a/android/visibility.go b/android/visibility.go
index a7e718b..c28ec93 100644
--- a/android/visibility.go
+++ b/android/visibility.go
@@ -117,12 +117,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).
@@ -189,7 +192,7 @@
 
 // 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 +202,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,8 +387,6 @@
 
 	qualified := createQualifiedModuleName(ctx)
 
-	moduleToVisibilityRule := moduleToVisibilityRuleMap(ctx)
-
 	// Visit all the dependencies making sure that this module has access to them all.
 	ctx.VisitDirectDeps(func(dep Module) {
 		depName := ctx.OtherModuleName(dep)
@@ -397,19 +398,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 +440,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..1984a21 100644
--- a/android/visibility_test.go
+++ b/android/visibility_test.go
@@ -874,11 +874,11 @@
 	ctx.RegisterModuleType("package", PackageFactory)
 	ctx.RegisterModuleType("mock_library", newMockLibraryModule)
 	ctx.RegisterModuleType("mock_defaults", defaultsFactory)
-	ctx.PreArchMutators(registerPackageRenamer)
-	ctx.PreArchMutators(registerVisibilityRuleChecker)
+	ctx.PreArchMutators(RegisterPackageRenamer)
+	ctx.PreArchMutators(RegisterVisibilityRuleChecker)
 	ctx.PreArchMutators(RegisterDefaultsPreArchMutators)
-	ctx.PreArchMutators(registerVisibilityRuleGatherer)
-	ctx.PostDepsMutators(registerVisibilityRuleEnforcer)
+	ctx.PreArchMutators(RegisterVisibilityRuleGatherer)
+	ctx.PostDepsMutators(RegisterVisibilityRuleEnforcer)
 	ctx.Register()
 
 	ctx.MockFileSystem(fs)
diff --git a/apex/apex.go b/apex/apex.go
index 4efba03..16bac94 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -339,6 +339,10 @@
 	// 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
 }
 
 type apexTargetBundleProperties struct {
@@ -731,6 +735,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)
@@ -1161,12 +1169,12 @@
 	})
 
 	// 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()) {
 					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
 				}
 			}
 		}
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 2b09801..a5ccc1b 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -140,7 +140,6 @@
 		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()
@@ -2381,7 +2380,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)
 	}
@@ -3000,6 +2999,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"]
diff --git a/apex/builder.go b/apex/builder.go
index f199bd4..3455ec7 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()
 			}
 		})
 	}
@@ -141,6 +141,8 @@
 		Command: `${zip2zip} -i $in -o $out ` +
 			`apex_payload.img:apex/${abi}.img ` +
 			`apex_manifest.json:root/apex_manifest.json ` +
+			`apex_pubkey:root/apex_pubkey ` +
+			`apex_manifest.pb:root/apex_manifest.pb ` +
 			`AndroidManifest.xml:manifest/AndroidManifest.xml ` +
 			`assets/NOTICE.html.gz:assets/NOTICE.html.gz`,
 		CommandDeps: []string{"${zip2zip}"},
@@ -375,7 +377,11 @@
 			optFlags = append(optFlags, "--assets_dir "+filepath.Dir(noticeFile.String()))
 		}
 
-		if !ctx.Config().UnbundledBuild() && a.installable() {
+		if ctx.ModuleDir() != "system/apex/apexd/apexd_testdata" && a.testOnlyShouldSkipHashtreeGeneration() {
+			ctx.PropertyErrorf("test_only_no_hashtree", "not available")
+			return
+		}
+		if (!ctx.Config().UnbundledBuild() && a.installable()) || a.testOnlyShouldSkipHashtreeGeneration() {
 			// Apexes which are supposed to be installed in builtin dirs(/system, etc)
 			// don't need hashtree for activation. Therefore, by removing hashtree from
 			// apex bundle (filesystem image in it, to be specific), we can save storage.
@@ -476,7 +482,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())
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index db3b5ef..ba5a466 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -181,8 +181,8 @@
 	// TODO(b/143192278): Add compat symlinks for prebuilt_apex
 }
 
-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)",
@@ -194,5 +194,5 @@
 				entries.AddStrings("LOCAL_OVERRIDES_MODULES", p.properties.Overrides...)
 			},
 		},
-	}
+	}}
 }
diff --git a/cc/androidmk.go b/cc/androidmk.go
index ff181d8..c1225bc 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -317,6 +317,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/cc.go b/cc/cc.go
index f306a00..512fe8e 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -37,9 +37,8 @@
 
 	android.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()
@@ -95,6 +94,7 @@
 
 	GeneratedSources []string
 	GeneratedHeaders []string
+	GeneratedDeps    []string
 
 	ReexportGeneratedHeaders []string
 
@@ -121,14 +121,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
@@ -218,7 +220,7 @@
 	// 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"`
@@ -284,6 +286,7 @@
 	isPgoCompile() bool
 	isNDKStubLibrary() bool
 	useClangLld(actx ModuleContext) bool
+	isForPlatform() bool
 	apexName() string
 	hasStubsVariants() bool
 	isStubs() bool
@@ -895,6 +898,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":
@@ -1047,10 +1057,6 @@
 		// 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() {
 		// Stubs do not need ABI dumps.
 		return false
@@ -1077,6 +1083,10 @@
 	return ctx.mod.getVndkExtendsModuleName()
 }
 
+func (ctx *moduleContextImpl) isForPlatform() bool {
+	return ctx.mod.IsForPlatform()
+}
+
 func (ctx *moduleContextImpl) apexName() string {
 	return ctx.mod.ApexName()
 }
@@ -1906,6 +1916,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 +1940,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 +2061,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 {
@@ -2244,10 +2260,12 @@
 	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)
@@ -2459,7 +2477,6 @@
 		&PgoProperties{},
 		&XomProperties{},
 		&android.ProtoProperties{},
-		&android.ApexProperties{},
 	)
 
 	android.InitDefaultsModule(module)
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 0cbdd52..f378f71 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -421,7 +421,7 @@
 		}`, 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"})
 }
 
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/global.go b/cc/config/global.go
index 0943126..0a09fa4 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{}
diff --git a/cc/fuzz.go b/cc/fuzz.go
index bb89bb4..0d3cc74 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -49,6 +49,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 +84,8 @@
 	corpus                android.Paths
 	corpusIntermediateDir android.Path
 	config                android.Path
+	data                  android.Paths
+	dataIntermediateDir   android.Path
 	installedSharedDeps   []string
 }
 
@@ -210,6 +215,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" {
@@ -345,6 +361,11 @@
 			return
 		}
 
+		// Discard modules that are in an unavailable namespace.
+		if !ccModule.ExportedToMake() {
+			return
+		}
+
 		s.fuzzTargets[module.Name()] = true
 
 		hostOrTargetString := "target"
@@ -372,6 +393,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/genrule_test.go b/cc/genrule_test.go
index 785e3e1..9f159e5 100644
--- a/cc/genrule_test.go
+++ b/cc/genrule_test.go
@@ -74,13 +74,13 @@
 		t.Fatal(errs)
 	}
 
-	gen := ctx.ModuleForTests("gen", "android_arm_armv7-a-neon").Output("out_arm")
+	gen := ctx.ModuleForTests("gen", "android_arm_armv7-a-neon_core").Output("out_arm")
 	expected := []string{"foo"}
 	if !reflect.DeepEqual(expected, gen.Inputs.Strings()) {
 		t.Errorf(`want arm inputs %v, got %v`, expected, gen.Inputs.Strings())
 	}
 
-	gen = ctx.ModuleForTests("gen", "android_arm64_armv8-a").Output("out_arm64")
+	gen = ctx.ModuleForTests("gen", "android_arm64_armv8-a_core").Output("out_arm64")
 	expected = []string{"bar"}
 	if !reflect.DeepEqual(expected, gen.Inputs.Strings()) {
 		t.Errorf(`want arm64 inputs %v, got %v`, expected, gen.Inputs.Strings())
diff --git a/cc/library.go b/cc/library.go
index 98cae3d..85533a9 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -18,12 +18,14 @@
 	"fmt"
 	"io"
 	"path/filepath"
+	"reflect"
 	"regexp"
 	"sort"
 	"strconv"
 	"strings"
 	"sync"
 
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/pathtools"
 
 	"android/soong/android"
@@ -237,6 +239,7 @@
 	systemDirs android.Paths
 	flags      []string
 	deps       android.Paths
+	headers    android.Paths
 }
 
 func (f *flagExporter) exportedIncludes(ctx ModuleContext) android.Paths {
@@ -280,6 +283,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 +305,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)
@@ -509,6 +523,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 +993,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 +1014,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 +1035,7 @@
 
 		library.reexportDirs(dir)
 		library.reexportDeps(library.baseCompiler.pathDeps...)
+		library.addExportedGeneratedHeaders(library.baseCompiler.pathDeps...)
 	}
 
 	if library.buildStubs() {
@@ -1403,3 +1438,298 @@
 
 	return outputFile
 }
+
+var SharedLibrarySdkMemberType = &librarySdkMemberType{
+	prebuiltModuleType: "cc_prebuilt_library_shared",
+	linkTypes:          []string{"shared"},
+}
+
+var StaticLibrarySdkMemberType = &librarySdkMemberType{
+	prebuiltModuleType: "cc_prebuilt_library_static",
+	linkTypes:          []string{"static"},
+}
+
+type librarySdkMemberType struct {
+	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 {
+	_, ok := module.(*Module)
+	return ok
+}
+
+// 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/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_library.go b/cc/ndk_library.go
index c47cbf0..d529622 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 {
@@ -389,7 +389,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/prebuilt.go b/cc/prebuilt.go
index 4e6cdd7..f20616f 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -90,6 +90,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 +130,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 +143,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 +173,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/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/testing.go b/cc/testing.go
index 3b10f87..417ea4a 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -265,13 +265,13 @@
 	ctx.RegisterModuleType("toolchain_library", ToolchainLibraryFactory)
 	ctx.RegisterModuleType("llndk_library", LlndkLibraryFactory)
 	ctx.RegisterModuleType("llndk_headers", llndkHeadersFactory)
+	ctx.RegisterModuleType("ndk_library", NdkLibraryFactory)
 	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()
diff --git a/cc/vndk.go b/cc/vndk.go
index f25861a..f531af3 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -460,8 +460,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 +469,7 @@
 				entries.SetString("LOCAL_MODULE_STEM", txt.outputFile.Base())
 			},
 		},
-	}
+	}}
 }
 
 func (txt *vndkLibrariesTxt) OutputFile() android.OutputPath {
@@ -704,7 +704,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/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..4771206 100644
--- a/cmd/multiproduct_kati/main.go
+++ b/cmd/multiproduct_kati/main.go
@@ -64,6 +64,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
 
@@ -278,6 +281,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()
@@ -472,3 +486,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/dexpreopt.go b/dexpreopt/dexpreopt.go
index 40986c3..ee04dfd 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -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_test.go b/dexpreopt/dexpreopt_test.go
index 254be0a..3264d6a 100644
--- a/dexpreopt/dexpreopt_test.go
+++ b/dexpreopt/dexpreopt_test.go
@@ -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())
 			}
 		}
 	}
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/java/androidmk.go b/java/androidmk.go
index c973739..f4e3c34 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -17,101 +17,103 @@
 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() {
+		// If the platform variant is available, 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
+	if library.IsForPlatform() {
+		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,8 +126,9 @@
 	}
 }
 
-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 {
@@ -134,25 +137,26 @@
 		androidMkWriteTestData(j.data, entries)
 	})
 
-	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 +169,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 +197,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 +217,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 +244,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 +261,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 +363,7 @@
 				}
 			},
 		},
-	}
+	}}
 }
 
 func (a *AndroidApp) getOverriddenPackages() []string {
@@ -373,8 +377,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 +388,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 +424,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 +442,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 +527,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",
@@ -636,11 +643,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 +666,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_test.go b/java/app_test.go
index fc8cf8e..1800bb7 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -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
@@ -1440,7 +1529,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 +1615,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) {
diff --git a/java/config/config.go b/java/config/config.go
index 333de32..fee6341 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -124,7 +124,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 +170,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 +180,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 +194,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 +204,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/dex.go b/java/dex.go
index 5b25b21..cd6d90d 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -115,7 +115,6 @@
 	r8Flags = append(r8Flags, proguardRaiseDeps.FormJavaClassPath("-libraryjars"))
 	r8Flags = append(r8Flags, flags.bootClasspath.FormJavaClassPath("-libraryjars"))
 	r8Flags = append(r8Flags, flags.classpath.FormJavaClassPath("-libraryjars"))
-	r8Flags = append(r8Flags, "-forceprocessing")
 
 	r8Deps = append(r8Deps, proguardRaiseDeps...)
 	r8Deps = append(r8Deps, flags.bootClasspath...)
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index 5e72cee..1d363c9 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -178,6 +178,12 @@
 	return false
 }
 
+func skipDexpreoptArtBootJars(ctx android.BuilderContext) bool {
+	// with EMMA_INSTRUMENT_FRAMEWORK=true ART boot class path libraries have dependencies on framework,
+	// therefore dexpreopt ART libraries cannot be dexpreopted in isolation => no ART boot image
+	return ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK")
+}
+
 type dexpreoptBootJars struct {
 	defaultBootImage *bootImage
 	otherImages      []*bootImage
@@ -187,7 +193,7 @@
 
 // Accessor function for the apex package. Returns nil if dexpreopt is disabled.
 func DexpreoptedArtApexJars(ctx android.BuilderContext) map[android.ArchType]android.OutputPaths {
-	if skipDexpreoptBootJars(ctx) {
+	if skipDexpreoptBootJars(ctx) || skipDexpreoptArtBootJars(ctx) {
 		return nil
 	}
 	return artBootImageConfig(ctx).imagesDeps
@@ -216,8 +222,10 @@
 
 	// Always create the default boot image first, to get a unique profile rule for all images.
 	d.defaultBootImage = buildBootImage(ctx, defaultBootImageConfig(ctx))
-	// Create boot image for the ART apex (build artifacts are accessed via the global boot image config).
-	buildBootImage(ctx, artBootImageConfig(ctx))
+	if !skipDexpreoptArtBootJars(ctx) {
+		// Create boot image for the ART apex (build artifacts are accessed via the global boot image config).
+		d.otherImages = append(d.otherImages, buildBootImage(ctx, artBootImageConfig(ctx)))
+	}
 	if global.GenerateApexImage {
 		// Create boot images for the JIT-zygote experiment.
 		d.otherImages = append(d.otherImages, buildBootImage(ctx, apexBootImageConfig(ctx)))
@@ -547,7 +555,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())
@@ -598,7 +606,6 @@
 		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, ":"))
-		ctx.Strict("DEXPREOPT_IMAGE_ZIP_"+image.name, image.zip.String())
 
 		var imageNames []string
 		for _, current := range append(d.otherImages, image) {
@@ -619,6 +626,8 @@
 				if current.zip != nil {
 				}
 			}
+
+			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..29a5abe 100644
--- a/java/dexpreopt_bootjars_test.go
+++ b/java/dexpreopt_bootjars_test.go
@@ -48,7 +48,7 @@
 
 	pathCtx := android.PathContextForTesting(config, nil)
 	dexpreoptConfig := dexpreopt.GlobalConfigForTests(pathCtx)
-	dexpreoptConfig.BootJars = []string{"foo", "bar", "baz"}
+	dexpreoptConfig.ArtApexJars = []string{"foo", "bar", "baz"}
 	setDexpreoptTestGlobalConfig(config, dexpreoptConfig)
 
 	ctx := testContext(bp, nil)
@@ -59,10 +59,9 @@
 
 	dexpreoptBootJars := ctx.SingletonForTests("dex_bootjars")
 
-	bootArt := dexpreoptBootJars.Output("boot-foo.art")
+	bootArt := dexpreoptBootJars.Output("boot.art")
 
 	expectedInputs := []string{
-		"dex_artjars/apex/com.android.art/javalib/arm64/boot.art",
 		"dex_bootjars_input/foo.jar",
 		"dex_bootjars_input/bar.jar",
 		"dex_bootjars_input/baz.jar",
@@ -83,19 +82,19 @@
 	expectedOutputs := []string{
 		"dex_bootjars/system/framework/arm64/boot.invocation",
 
-		"dex_bootjars/system/framework/arm64/boot-foo.art",
+		"dex_bootjars/system/framework/arm64/boot.art",
 		"dex_bootjars/system/framework/arm64/boot-bar.art",
 		"dex_bootjars/system/framework/arm64/boot-baz.art",
 
-		"dex_bootjars/system/framework/arm64/boot-foo.oat",
+		"dex_bootjars/system/framework/arm64/boot.oat",
 		"dex_bootjars/system/framework/arm64/boot-bar.oat",
 		"dex_bootjars/system/framework/arm64/boot-baz.oat",
 
-		"dex_bootjars/system/framework/arm64/boot-foo.vdex",
+		"dex_bootjars/system/framework/arm64/boot.vdex",
 		"dex_bootjars/system/framework/arm64/boot-bar.vdex",
 		"dex_bootjars/system/framework/arm64/boot-baz.vdex",
 
-		"dex_bootjars_unstripped/system/framework/arm64/boot-foo.oat",
+		"dex_bootjars_unstripped/system/framework/arm64/boot.oat",
 		"dex_bootjars_unstripped/system/framework/arm64/boot-bar.oat",
 		"dex_bootjars_unstripped/system/framework/arm64/boot-baz.oat",
 	}
diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go
index 57a770e..91e0dfb 100644
--- a/java/dexpreopt_config.go
+++ b/java/dexpreopt_config.go
@@ -135,10 +135,6 @@
 		deviceDir := android.PathForOutput(ctx, ctx.Config().DeviceName())
 
 		artModules := global.ArtApexJars
-		// In coverage builds ART boot class path jars are instrumented and have additional dependencies.
-		if ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") {
-			artModules = append(artModules, "jacocoagent")
-		}
 		frameworkModules := android.RemoveListFromList(global.BootJars,
 			concat(artModules, getJarsFromApexJarPairs(global.UpdatableBootJars)))
 
@@ -166,15 +162,15 @@
 		}
 
 		// Framework config for the boot image extension.
-		// It includes framework libraries and depends on the ART config.
+		// It includes both the Core libraries and framework.
 		frameworkCfg := bootImageConfig{
-			extension:        true,
+			extension:        false,
 			name:             frameworkBootImageName,
 			stem:             "boot",
 			installSubdir:    frameworkSubdir,
-			modules:          frameworkModules,
-			dexLocations:     frameworkLocations,
-			dexLocationsDeps: append(artLocations, frameworkLocations...),
+			modules:          concat(artModules, frameworkModules),
+			dexLocations:     concat(artLocations, frameworkLocations),
+			dexLocationsDeps: concat(artLocations, frameworkLocations),
 		}
 
 		// Apex config for the  boot image used in the JIT-zygote experiment.
@@ -226,12 +222,9 @@
 				c.images[arch] = imageDir.Join(ctx, imageName)
 				c.imagesDeps[arch] = c.moduleFiles(ctx, imageDir, ".art", ".oat", ".vdex")
 			}
-		}
 
-		// specific to the framework config
-		frameworkCfg.dexPathsDeps = append(artCfg.dexPathsDeps, frameworkCfg.dexPathsDeps...)
-		frameworkCfg.imageLocations = append(artCfg.imageLocations, frameworkCfg.imageLocations...)
-		frameworkCfg.zip = frameworkCfg.dir.Join(ctx, frameworkCfg.stem+".zip")
+			c.zip = c.dir.Join(ctx, c.name+".zip")
+		}
 
 		return configs
 	}).(map[string]*bootImageConfig)
diff --git a/java/droiddoc.go b/java/droiddoc.go
index 16e6921..76cdaea 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"
@@ -1973,12 +1974,32 @@
 	return module
 }
 
-func (d *Droidstubs) BuildSnapshot(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder) {
+var DroidStubsSdkMemberType = &droidStubsSdkMemberType{}
+
+type droidStubsSdkMemberType struct {
+}
+
+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/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..f58e5ba 100644
--- a/java/java.go
+++ b/java/java.go
@@ -34,7 +34,7 @@
 )
 
 func init() {
-	android.RegisterModuleType("java_defaults", defaultsFactory)
+	android.RegisterModuleType("java_defaults", DefaultsFactory)
 
 	android.RegisterModuleType("java_library", LibraryFactory)
 	android.RegisterModuleType("java_library_static", LibraryStaticFactory)
@@ -792,8 +792,7 @@
 			return
 		}
 		switch module.(type) {
-		case *Library:
-		case *AndroidLibrary:
+		case *Library, *AndroidLibrary:
 			if to, ok := module.(linkTypeContext); ok {
 				switch tag {
 				case bootClasspathTag, libTag, staticLibTag:
@@ -1711,23 +1710,47 @@
 }
 
 const (
-	aidlIncludeDir     = "aidl"
-	javaStubDir        = "java"
-	javaStubFileSuffix = ".jar"
+	aidlIncludeDir = "aidl"
+	javaDir        = "java"
+	jarFileSuffix  = ".jar"
 )
 
-// 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 (j *Library) sdkSnapshotFilePathForJar() string {
+	return filepath.Join(javaDir, j.Name()+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()))
+type librarySdkMemberType struct {
+}
+
+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 := j.sdkSnapshotFilePathForJar()
+	builder.CopyToSnapshot(exportedJar, snapshotRelativeJavaLibPath)
 
 	for _, dir := range j.AidlIncludeDirs() {
 		// TODO(jiyong): copy parcelable declarations only
@@ -1737,10 +1760,44 @@
 		}
 	}
 
-	module := builder.AddPrebuiltModule(sdkModuleContext.OtherModuleName(j), "java_import")
+	module := builder.AddPrebuiltModule(member, "java_import")
 	module.AddProperty("jars", []string{snapshotRelativeJavaLibPath})
 }
 
+var HeaderLibrarySdkMemberType = &headerLibrarySdkMemberType{}
+
+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]
+	})
+}
+
+var ImplLibrarySdkMemberType = &implLibrarySdkMemberType{}
+
+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
@@ -2394,10 +2451,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..f4b92e3 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -79,7 +79,7 @@
 	ctx.RegisterModuleType("java_test", TestFactory)
 	ctx.RegisterModuleType("java_import", ImportFactory)
 	ctx.RegisterModuleType("java_import_host", ImportFactoryHost)
-	ctx.RegisterModuleType("java_defaults", defaultsFactory)
+	ctx.RegisterModuleType("java_defaults", DefaultsFactory)
 	ctx.RegisterModuleType("java_system_modules", SystemModulesFactory)
 	ctx.RegisterModuleType("java_genrule", genRuleFactory)
 	ctx.RegisterModuleType("java_plugin", PluginFactory)
@@ -288,6 +288,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 {
@@ -1089,7 +1177,7 @@
 	ctx.ModuleForTests("foo"+sdkDocsSuffix, "android_common")
 	ctx.ModuleForTests("foo"+sdkDocsSuffix+sdkSystemApiSuffix, "android_common")
 	ctx.ModuleForTests("foo"+sdkDocsSuffix+sdkTestApiSuffix, "android_common")
-	ctx.ModuleForTests("foo"+sdkXmlFileSuffix, "android_arm64_armv8-a")
+	ctx.ModuleForTests("foo"+sdkXmlFileSuffix, "android_arm64_armv8-a_core")
 	ctx.ModuleForTests("foo.api.public.28", "")
 	ctx.ModuleForTests("foo.api.system.28", "")
 	ctx.ModuleForTests("foo.api.test.28", "")
diff --git a/java/platform_compat_config.go b/java/platform_compat_config.go
index 23ba2b0..7801634 100644
--- a/java/platform_compat_config.go
+++ b/java/platform_compat_config.go
@@ -71,8 +71,8 @@
 
 }
 
-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 +82,7 @@
 				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.configFile.Base())
 			},
 		},
-	}
+	}}
 }
 
 func platformCompatConfigFactory() android.Module {
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_library.go b/java/sdk_library.go
index b7efcff..e204659 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -194,13 +194,13 @@
 	})
 }
 
-func (module *SdkLibrary) AndroidMkEntries() android.AndroidMkEntries {
-	entries := module.Library.AndroidMkEntries()
+func (module *SdkLibrary) AndroidMkEntries() []android.AndroidMkEntries {
+	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 +252,7 @@
 			}
 		},
 	}
-	return entries
+	return entriesList
 }
 
 // Module name of the stubs library
@@ -293,6 +293,8 @@
 		partition = "odm"
 	} else if module.ProductSpecific() {
 		partition = "product"
+	} else if module.SystemExtSpecific() {
+		partition = "system_ext"
 	}
 	return "/" + partition + "/framework/" + module.implName() + ".jar"
 }
@@ -363,17 +365,18 @@
 // 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
+		Sdk_version         *string
+		Libs                []string
+		Soc_specific        *bool
+		Device_specific     *bool
+		Product_specific    *bool
+		System_ext_specific *bool
+		Compile_dex         *bool
+		System_modules      *string
+		Java_version        *string
+		Product_variables   struct {
 			Unbundled_build struct {
 				Enabled *bool
 			}
@@ -417,6 +420,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)
@@ -561,12 +566,13 @@
 	// 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")
@@ -577,6 +583,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)
 }
@@ -795,10 +803,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 +818,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/python/tests/par_test.py b/python/tests/par_test.py
index 1fafe0f..56a5063 100644
--- a/python/tests/par_test.py
+++ b/python/tests/par_test.py
@@ -44,6 +44,13 @@
 assert_equal("sys.path[1]", sys.path[1], os.path.join(archive, "internal"))
 assert_equal("sys.path[2]", sys.path[2], os.path.join(archive, "internal", "stdlib"))
 
+if os.getenv('ARGTEST', False):
+    assert_equal("len(sys.argv)", len(sys.argv), 3)
+    assert_equal("sys.argv[1]", sys.argv[1], "--arg1")
+    assert_equal("sys.argv[2]", sys.argv[2], "arg2")
+else:
+    assert_equal("len(sys.argv)", len(sys.argv), 1)
+
 if failed:
     sys.exit(1)
 
diff --git a/python/tests/runtest.sh b/python/tests/runtest.sh
index 1ecdebc..21187ed 100755
--- a/python/tests/runtest.sh
+++ b/python/tests/runtest.sh
@@ -36,8 +36,12 @@
 PYTHONHOME=/usr $ANDROID_HOST_OUT/nativetest64/par_test/par_test
 PYTHONPATH=/usr $ANDROID_HOST_OUT/nativetest64/par_test/par_test
 
+ARGTEST=true $ANDROID_HOST_OUT/nativetest64/par_test/par_test --arg1 arg2
+
 PYTHONHOME= PYTHONPATH= $ANDROID_HOST_OUT/nativetest64/par_test3/par_test3
 PYTHONHOME=/usr $ANDROID_HOST_OUT/nativetest64/par_test3/par_test3
 PYTHONPATH=/usr $ANDROID_HOST_OUT/nativetest64/par_test3/par_test3
 
+ARGTEST=true $ANDROID_HOST_OUT/nativetest64/par_test3/par_test3 --arg1 arg2
+
 echo "Passed!"
diff --git a/rust/testing.go b/rust/testing.go
index 45dbbbd..2067f82 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -185,7 +185,6 @@
 	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()
diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go
new file mode 100644
index 0000000..53109ec
--- /dev/null
+++ b/sdk/cc_sdk_test.go
@@ -0,0 +1,610 @@
+// 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_core_shared").(*cc.Module).OutputFile()
+	arm64Output := result.Module("sdkmember", "android_arm64_armv8-a_core_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"],
+		}
+
+		sdk_snapshot {
+			name: "mysdk@1",
+			native_shared_libs: ["sdkmember_mysdk_1"],
+		}
+
+		sdk_snapshot {
+			name: "mysdk@2",
+			native_shared_libs: ["sdkmember_mysdk_2"],
+		}
+
+		cc_prebuilt_library_shared {
+			name: "sdkmember",
+			srcs: ["libfoo.so"],
+			prefer: false,
+			system_shared_libs: [],
+			stl: "none",
+		}
+
+		cc_prebuilt_library_shared {
+			name: "sdkmember_mysdk_1",
+			sdk_member_name: "sdkmember",
+			srcs: ["libfoo.so"],
+			system_shared_libs: [],
+			stl: "none",
+		}
+
+		cc_prebuilt_library_shared {
+			name: "sdkmember_mysdk_2",
+			sdk_member_name: "sdkmember",
+			srcs: ["libfoo.so"],
+			system_shared_libs: [],
+			stl: "none",
+		}
+
+		cc_library_shared {
+			name: "mycpplib",
+			srcs: ["Test.cpp"],
+			shared_libs: ["sdkmember"],
+			system_shared_libs: [],
+			stl: "none",
+		}
+
+		apex {
+			name: "myapex",
+			native_shared_libs: ["mycpplib"],
+			uses_sdks: ["mysdk@1"],
+			key: "myapex.key",
+			certificate: ":myapex.cert",
+		}
+
+		apex {
+			name: "myapex2",
+			native_shared_libs: ["mycpplib"],
+			uses_sdks: ["mysdk@2"],
+			key: "myapex.key",
+			certificate: ":myapex.cert",
+		}
+	`)
+
+	sdkMemberV1 := result.ModuleForTests("sdkmember_mysdk_1", "android_arm64_armv8-a_core_shared_myapex").Rule("toc").Output
+	sdkMemberV2 := result.ModuleForTests("sdkmember_mysdk_2", "android_arm64_armv8-a_core_shared_myapex2").Rule("toc").Output
+
+	cpplibForMyApex := result.ModuleForTests("mycpplib", "android_arm64_armv8-a_core_shared_myapex")
+	cpplibForMyApex2 := result.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())
+}
+
+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_core_shared/mynativelib1.so -> arm64/lib/mynativelib1.so
+.intermediates/mynativelib1/android_arm_armv7-a-neon_core_shared/mynativelib1.so -> arm/lib/mynativelib1.so
+.intermediates/mynativelib2/android_arm64_armv8-a_core_shared/mynativelib2.so -> arm64/lib/mynativelib2.so
+.intermediates/mynativelib2/android_arm_armv7-a-neon_core_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_core_shared/mynativelib.so -> arm64/lib/mynativelib.so
+arm64/include/Arm64Test.h -> arm64/include/arm64/include/Arm64Test.h
+.intermediates/mynativelib/android_arm_armv7-a-neon_core_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_core_shared/mynativelib.so -> arm64/lib/mynativelib.so
+.intermediates/mynativelib/android_arm64_armv8-a_core_shared/gen/aidl/aidl/foo/bar/Test.h -> arm64/include_gen/mynativelib/aidl/foo/bar/Test.h
+.intermediates/mynativelib/android_arm64_armv8-a_core_shared/gen/aidl/aidl/foo/bar/BnTest.h -> arm64/include_gen/mynativelib/aidl/foo/bar/BnTest.h
+.intermediates/mynativelib/android_arm64_armv8-a_core_shared/gen/aidl/aidl/foo/bar/BpTest.h -> arm64/include_gen/mynativelib/aidl/foo/bar/BpTest.h
+.intermediates/mynativelib/android_arm_armv7-a-neon_core_shared/mynativelib.so -> arm/lib/mynativelib.so
+.intermediates/mynativelib/android_arm_armv7-a-neon_core_shared/gen/aidl/aidl/foo/bar/Test.h -> arm/include_gen/mynativelib/aidl/foo/bar/Test.h
+.intermediates/mynativelib/android_arm_armv7-a-neon_core_shared/gen/aidl/aidl/foo/bar/BnTest.h -> arm/include_gen/mynativelib/aidl/foo/bar/BnTest.h
+.intermediates/mynativelib/android_arm_armv7-a-neon_core_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, `
+		sdk {
+			name: "mysdk",
+			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("mysdk", "android_common", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library_static {
+    name: "mysdk_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: [],
+}
+
+sdk_snapshot {
+    name: "mysdk@current",
+    native_static_libs: ["mysdk_mynativelib@current"],
+}
+`),
+		checkAllCopyRules(`
+include/Test.h -> include/include/Test.h
+.intermediates/mynativelib/android_arm64_armv8-a_core_static/mynativelib.a -> arm64/lib/mynativelib.a
+.intermediates/mynativelib/android_arm64_armv8-a_core_static/gen/aidl/aidl/foo/bar/Test.h -> arm64/include_gen/mynativelib/aidl/foo/bar/Test.h
+.intermediates/mynativelib/android_arm64_armv8-a_core_static/gen/aidl/aidl/foo/bar/BnTest.h -> arm64/include_gen/mynativelib/aidl/foo/bar/BnTest.h
+.intermediates/mynativelib/android_arm64_armv8-a_core_static/gen/aidl/aidl/foo/bar/BpTest.h -> arm64/include_gen/mynativelib/aidl/foo/bar/BpTest.h
+.intermediates/mynativelib/android_arm_armv7-a-neon_core_static/mynativelib.a -> arm/lib/mynativelib.a
+.intermediates/mynativelib/android_arm_armv7-a-neon_core_static/gen/aidl/aidl/foo/bar/Test.h -> arm/include_gen/mynativelib/aidl/foo/bar/Test.h
+.intermediates/mynativelib/android_arm_armv7-a-neon_core_static/gen/aidl/aidl/foo/bar/BnTest.h -> arm/include_gen/mynativelib/aidl/foo/bar/BnTest.h
+.intermediates/mynativelib/android_arm_armv7-a-neon_core_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, `
+		sdk {
+			name: "mysdk",
+			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("mysdk", "linux_glibc_common", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library_static {
+    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.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: [],
+}
+
+sdk_snapshot {
+    name: "mysdk@current",
+    device_supported: false,
+    host_supported: true,
+    native_static_libs: ["mysdk_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/java_sdk_test.go b/sdk/java_sdk_test.go
new file mode 100644
index 0000000..1aa9184
--- /dev/null
+++ b/sdk/java_sdk_test.go
@@ -0,0 +1,461 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package sdk
+
+import (
+	"testing"
+)
+
+func testSdkWithJava(t *testing.T, bp string) *testSdkResult {
+	t.Helper()
+
+	fs := map[string][]byte{
+		"Test.java":              nil,
+		"aidl/foo/bar/Test.aidl": nil,
+	}
+	return testSdkWithFs(t, bp, fs)
+}
+
+// Contains tests for SDK members provided by the java package.
+
+func TestBasicSdkWithJavaLibrary(t *testing.T) {
+	result := testSdkWithJava(t, `
+		sdk {
+			name: "mysdk",
+			java_header_libs: ["myjavalib"],
+		}
+
+		sdk_snapshot {
+			name: "mysdk@1",
+			java_header_libs: ["sdkmember_mysdk_1"],
+		}
+
+		sdk_snapshot {
+			name: "mysdk@2",
+			java_header_libs: ["sdkmember_mysdk_2"],
+		}
+
+		java_import {
+			name: "sdkmember",
+			prefer: false,
+			host_supported: true,
+		}
+
+		java_import {
+			name: "sdkmember_mysdk_1",
+			sdk_member_name: "sdkmember",
+			host_supported: true,
+		}
+
+		java_import {
+			name: "sdkmember_mysdk_2",
+			sdk_member_name: "sdkmember",
+			host_supported: true,
+		}
+
+		java_library {
+			name: "myjavalib",
+			srcs: ["Test.java"],
+			libs: ["sdkmember"],
+			system_modules: "none",
+			sdk_version: "none",
+			compile_dex: true,
+			host_supported: true,
+		}
+
+		apex {
+			name: "myapex",
+			java_libs: ["myjavalib"],
+			uses_sdks: ["mysdk@1"],
+			key: "myapex.key",
+			certificate: ":myapex.cert",
+		}
+
+		apex {
+			name: "myapex2",
+			java_libs: ["myjavalib"],
+			uses_sdks: ["mysdk@2"],
+			key: "myapex.key",
+			certificate: ":myapex.cert",
+		}
+	`)
+
+	sdkMemberV1 := result.ctx.ModuleForTests("sdkmember_mysdk_1", "android_common_myapex").Rule("combineJar").Output
+	sdkMemberV2 := result.ctx.ModuleForTests("sdkmember_mysdk_2", "android_common_myapex2").Rule("combineJar").Output
+
+	javalibForMyApex := result.ctx.ModuleForTests("myjavalib", "android_common_myapex")
+	javalibForMyApex2 := result.ctx.ModuleForTests("myjavalib", "android_common_myapex2")
+
+	// Depending on the uses_sdks value, different libs are linked
+	ensureListContains(t, pathsToStrings(javalibForMyApex.Rule("javac").Implicits), sdkMemberV1.String())
+	ensureListContains(t, pathsToStrings(javalibForMyApex2.Rule("javac").Implicits), sdkMemberV2.String())
+}
+
+func TestSnapshotWithJavaHeaderLibrary(t *testing.T) {
+	result := testSdkWithJava(t, `
+		sdk {
+			name: "mysdk",
+			java_header_libs: ["myjavalib"],
+		}
+
+		java_library {
+			name: "myjavalib",
+			srcs: ["Test.java"],
+			aidl: {
+				export_include_dirs: ["aidl"],
+			},
+			system_modules: "none",
+			sdk_version: "none",
+			compile_dex: true,
+			host_supported: true,
+		}
+	`)
+
+	result.CheckSnapshot("mysdk", "android_common", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+    name: "mysdk_myjavalib@current",
+    sdk_member_name: "myjavalib",
+    jars: ["java/myjavalib.jar"],
+}
+
+java_import {
+    name: "myjavalib",
+    prefer: false,
+    jars: ["java/myjavalib.jar"],
+}
+
+sdk_snapshot {
+    name: "mysdk@current",
+    java_header_libs: ["mysdk_myjavalib@current"],
+}
+
+`),
+		checkAllCopyRules(`
+.intermediates/myjavalib/android_common/turbine-combined/myjavalib.jar -> java/myjavalib.jar
+aidl/foo/bar/Test.aidl -> aidl/aidl/foo/bar/Test.aidl
+`),
+	)
+}
+
+func TestHostSnapshotWithJavaHeaderLibrary(t *testing.T) {
+	// b/145598135 - Generating host snapshots for anything other than linux is not supported.
+	SkipIfNotLinux(t)
+
+	result := testSdkWithJava(t, `
+		sdk {
+			name: "mysdk",
+			device_supported: false,
+			host_supported: true,
+			java_header_libs: ["myjavalib"],
+		}
+
+		java_library {
+			name: "myjavalib",
+			device_supported: false,
+			host_supported: true,
+			srcs: ["Test.java"],
+			aidl: {
+				export_include_dirs: ["aidl"],
+			},
+			system_modules: "none",
+			sdk_version: "none",
+			compile_dex: true,
+		}
+	`)
+
+	result.CheckSnapshot("mysdk", "linux_glibc_common", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+    name: "mysdk_myjavalib@current",
+    sdk_member_name: "myjavalib",
+    device_supported: false,
+    host_supported: true,
+    jars: ["java/myjavalib.jar"],
+}
+
+java_import {
+    name: "myjavalib",
+    prefer: false,
+    device_supported: false,
+    host_supported: true,
+    jars: ["java/myjavalib.jar"],
+}
+
+sdk_snapshot {
+    name: "mysdk@current",
+    device_supported: false,
+    host_supported: true,
+    java_header_libs: ["mysdk_myjavalib@current"],
+}
+`),
+		checkAllCopyRules(`
+.intermediates/myjavalib/linux_glibc_common/javac/myjavalib.jar -> java/myjavalib.jar
+aidl/foo/bar/Test.aidl -> aidl/aidl/foo/bar/Test.aidl
+`),
+	)
+}
+
+func TestSnapshotWithJavaImplLibrary(t *testing.T) {
+	result := testSdkWithJava(t, `
+		sdk {
+			name: "mysdk",
+			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("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_libs: ["mysdk_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, `
+		sdk {
+			name: "mysdk",
+			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("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_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 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, `
+		sdk {
+			name: "mysdk",
+			stubs_sources: ["myjavaapistubs"],
+		}
+
+		droidstubs {
+			name: "myjavaapistubs",
+			srcs: ["foo/bar/Foo.java"],
+			system_modules: "none",
+			sdk_version: "none",
+		}
+	`)
+
+	result.CheckSnapshot("mysdk", "android_common", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+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"],
+}
+
+sdk_snapshot {
+    name: "mysdk@current",
+    stubs_sources: ["mysdk_myjavaapistubs@current"],
+}
+
+`),
+		checkAllCopyRules(""),
+		checkMergeZip(".intermediates/mysdk/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, `
+		sdk {
+			name: "mysdk",
+			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("mysdk", "linux_glibc_common", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+prebuilt_stubs_sources {
+    name: "mysdk_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"],
+}
+
+sdk_snapshot {
+    name: "mysdk@current",
+    device_supported: false,
+    host_supported: true,
+    stubs_sources: ["mysdk_myjavaapistubs@current"],
+}
+`),
+		checkAllCopyRules(""),
+		checkMergeZip(".intermediates/mysdk/linux_glibc_common/tmp/java/myjavaapistubs_stubs_sources.zip"),
+	)
+}
diff --git a/sdk/sdk.go b/sdk/sdk.go
index 431ace9..cd9aafa 100644
--- a/sdk/sdk.go
+++ b/sdk/sdk.go
@@ -27,14 +27,25 @@
 	// registered before mutators in this package. See RegisterPostDepsMutators for more details.
 	_ "android/soong/apex"
 	"android/soong/cc"
+	"android/soong/java"
 )
 
 func init() {
 	pctx.Import("android/soong/android")
+	pctx.Import("android/soong/java/config")
+
 	android.RegisterModuleType("sdk", ModuleFactory)
 	android.RegisterModuleType("sdk_snapshot", SnapshotModuleFactory)
 	android.PreDepsMutators(RegisterPreDepsMutators)
 	android.PostDepsMutators(RegisterPostDepsMutators)
+
+	// Populate the dependency tags for each member list property.  This needs to
+	// be done here to break an initialization cycle.
+	for _, memberListProperty := range sdkMemberListProperties {
+		memberListProperty.dependencyTag = &sdkMemberDependencyTag{
+			memberListProperty: memberListProperty,
+		}
+	}
 }
 
 type sdk struct {
@@ -50,16 +61,87 @@
 }
 
 type sdkProperties struct {
-	// The list of java libraries in this SDK
-	Java_libs []string
-	// The list of native libraries in this SDK
+	// For module types from the cc package
+
+	// The list of shared native libraries in this SDK
 	Native_shared_libs []string
+
+	// The list of static native libraries in this SDK
+	Native_static_libs []string
+
+	// For module types from the java package
+
+	// The list of java header libraries in this SDK
+	//
+	// This should be used for java libraries that are provided separately at runtime,
+	// e.g. through an APEX.
+	Java_header_libs []string
+
+	// The list of java implementation libraries in this SDK
+	Java_libs []string
+
 	// The list of stub sources in this SDK
 	Stubs_sources []string
 
 	Snapshot bool `blueprint:"mutated"`
 }
 
+type sdkMemberDependencyTag struct {
+	blueprint.BaseDependencyTag
+	memberListProperty *sdkMemberListProperty
+}
+
+// Contains information about the sdk properties that list sdk members, e.g.
+// Java_header_libs.
+type sdkMemberListProperty struct {
+	// the name of the property as used in a .bp file
+	name string
+
+	// getter for the list of member names
+	getter func(properties *sdkProperties) []string
+
+	// the type of member referenced in the list
+	memberType android.SdkMemberType
+
+	// the dependency tag used for items in this list.
+	dependencyTag *sdkMemberDependencyTag
+}
+
+// Information about how to handle each member list property.
+//
+// It is organized first by package and then by name within the package.
+// Packages are in alphabetical order and properties are in alphabetical order
+// within each package.
+var sdkMemberListProperties = []*sdkMemberListProperty{
+	// Members from cc package.
+	{
+		name:       "native_shared_libs",
+		getter:     func(properties *sdkProperties) []string { return properties.Native_shared_libs },
+		memberType: cc.SharedLibrarySdkMemberType,
+	},
+	{
+		name:       "native_static_libs",
+		getter:     func(properties *sdkProperties) []string { return properties.Native_static_libs },
+		memberType: cc.StaticLibrarySdkMemberType,
+	},
+	// Members from java package.
+	{
+		name:       "java_header_libs",
+		getter:     func(properties *sdkProperties) []string { return properties.Java_header_libs },
+		memberType: java.HeaderLibrarySdkMemberType,
+	},
+	{
+		name:       "java_libs",
+		getter:     func(properties *sdkProperties) []string { return properties.Java_libs },
+		memberType: java.ImplLibrarySdkMemberType,
+	},
+	{
+		name:       "stubs_sources",
+		getter:     func(properties *sdkProperties) []string { return properties.Stubs_sources },
+		memberType: java.DroidStubsSdkMemberType,
+	},
+}
+
 // 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 {
@@ -96,12 +178,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 +195,7 @@
 				fmt.Fprintln(w, s.Name()+":", s.snapshotFile.String())
 			},
 		},
-	}
+	}}
 }
 
 // RegisterPreDepsMutators registers pre-deps mutators to support modules implementing SdkAware
@@ -143,10 +225,6 @@
 	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 {
@@ -158,22 +236,10 @@
 // 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)
-			}
+		for _, memberListProperty := range sdkMemberListProperties {
+			names := memberListProperty.getter(&m.properties)
+			tag := memberListProperty.dependencyTag
+			memberListProperty.memberType.AddDependencies(mctx, tag, names)
 		}
 	}
 }
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..5082ab4
--- /dev/null
+++ b/sdk/testing.go
@@ -0,0 +1,407 @@
+// 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) {
+	config := android.TestArchConfig(buildDir, nil)
+	ctx := android.NewTestArchContext()
+
+	// from android package
+	ctx.PreArchMutators(android.RegisterPackageRenamer)
+	ctx.PreArchMutators(android.RegisterVisibilityRuleChecker)
+	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
+	ctx.PreArchMutators(android.RegisterVisibilityRuleGatherer)
+	ctx.PostDepsMutators(android.RegisterVisibilityRuleEnforcer)
+
+	ctx.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()
+	})
+	ctx.RegisterModuleType("package", android.PackageFactory)
+
+	// from java package
+	ctx.RegisterModuleType("android_app_certificate", java.AndroidAppCertificateFactory)
+	ctx.RegisterModuleType("java_defaults", java.DefaultsFactory)
+	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_library_static", cc.LibraryStaticFactory)
+	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("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)
+
+	mockFS := 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,
+	}
+
+	for k, v := range fs {
+		mockFS[k] = v
+	}
+
+	ctx.MockFileSystem(mockFS)
+
+	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 := r.pathRelativeToBuildDir(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 = r.pathRelativeToBuildDir(bp.Output)
+
+		case mergeZips.String():
+			// Copy the current outputZip to the intermediateZip.
+			info.intermediateZip = info.outputZip
+			mergeInput := r.pathRelativeToBuildDir(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 = r.pathRelativeToBuildDir(bp.Output)
+
+			// Save the zips to be merged into the intermediate zip.
+			info.mergeZips = r.pathsRelativeToBuildDir(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)
+}
+
+func (r *testSdkResult) pathRelativeToBuildDir(path android.Path) string {
+	buildDir := filepath.Clean(r.config.BuildDir()) + "/"
+	return strings.TrimPrefix(filepath.Clean(path.String()), buildDir)
+}
+
+func (r *testSdkResult) pathsRelativeToBuildDir(paths android.Paths) []string {
+	var result []string
+	for _, path := range paths {
+		result = append(result, r.pathRelativeToBuildDir(path))
+	}
+	return result
+}
+
+// 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..52d21ed 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,46 @@
 	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 sdkMemberListProperties. The
+// names are in order in which the dependencies were added.
+func 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 {
+			memberListProperty := memberTag.memberListProperty
+			memberType := memberListProperty.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), memberListProperty.name)
 			}
-			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 sdkMemberListProperties {
+		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 +162,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,28 +184,15 @@
 		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 collectMembers(ctx) {
+		member.memberType.BuildSnapshot(ctx, builder, member)
 	}
 
 	for _, unversioned := range builder.prebuiltOrder {
@@ -294,15 +212,19 @@
 	snapshotName := ctx.ModuleName() + string(android.SdkVersionSeparator) + builder.version
 	snapshotModule := bpFile.newModule("sdk_snapshot")
 	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 sdkMemberListProperties {
+		names := memberListProperty.getter(&s.properties)
+		if len(names) > 0 {
+			snapshotModule.AddProperty(memberListProperty.name, builder.versionedSdkMemberNames(names))
+		}
 	}
 	bpFile.AddModule(snapshotModule)
 
@@ -316,41 +238,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
@@ -364,7 +284,6 @@
 		outputPropertySet(contents, &bpModule.bpPropertySet)
 		contents.Printfln("}")
 	}
-	contents.Printfln("")
 }
 
 func outputPropertySet(contents *generatedContents, set *bpPropertySet) {
@@ -413,104 +332,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 +351,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 +375,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 +434,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..1fc94db 100644
--- a/sysprop/sysprop_library.go
+++ b/sysprop/sysprop_library.go
@@ -375,8 +375,8 @@
 	if isProduct {
 		// product can't own any sysprop_library now, so product must use public scope
 		scope = "public"
-	} else if isVendor && !isOwnerPlatform {
-		// vendor and odm can't use system's internal property.
+	} else if isVendor && isOwnerPlatform {
+		// vendor and odm can only use the public properties from the platform
 		scope = "public"
 	}
 
diff --git a/sysprop/sysprop_test.go b/sysprop/sysprop_test.go
index 5e0eb35..81f4c53 100644
--- a/sysprop/sysprop_test.go
+++ b/sysprop/sysprop_test.go
@@ -73,7 +73,6 @@
 	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("version", cc.VersionMutator).Parallel()
diff --git a/ui/build/build.go b/ui/build/build.go
index 6a19314..7dfb900 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -33,6 +33,15 @@
 	// can be parsed as ninja output.
 	ensureEmptyFileExists(ctx, filepath.Join(config.OutDir(), "ninja_build"))
 	ensureEmptyFileExists(ctx, filepath.Join(config.OutDir(), ".out-dir"))
+
+	if buildDateTimeFile, ok := config.environ.Get("BUILD_DATETIME_FILE"); ok {
+		err := ioutil.WriteFile(buildDateTimeFile, []byte(config.buildDateTime), 0777)
+		if err != nil {
+			ctx.Fatalln("Failed to write BUILD_DATETIME to file:", err)
+		}
+	} else {
+		ctx.Fatalln("Missing BUILD_DATETIME_FILE")
+	}
 }
 
 var combinedBuildNinjaTemplate = template.Must(template.New("combined").Parse(`
diff --git a/ui/build/config.go b/ui/build/config.go
index 876bfe0..565f033 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -15,7 +15,6 @@
 package build
 
 import (
-	"io/ioutil"
 	"os"
 	"path/filepath"
 	"runtime"
@@ -30,10 +29,11 @@
 
 type configImpl struct {
 	// From the environment
-	arguments []string
-	goma      bool
-	environ   *Environment
-	distDir   string
+	arguments     []string
+	goma          bool
+	environ       *Environment
+	distDir       string
+	buildDateTime string
 
 	// From the arguments
 	parallel   int
@@ -244,18 +244,14 @@
 
 	outDir := ret.OutDir()
 	buildDateTimeFile := filepath.Join(outDir, "build_date.txt")
-	var content string
 	if buildDateTime, ok := ret.environ.Get("BUILD_DATETIME"); ok && buildDateTime != "" {
-		content = buildDateTime
+		ret.buildDateTime = buildDateTime
 	} else {
-		content = strconv.FormatInt(time.Now().Unix(), 10)
+		ret.buildDateTime = strconv.FormatInt(time.Now().Unix(), 10)
 	}
+
 	if ctx.Metrics != nil {
-		ctx.Metrics.SetBuildDateTime(content)
-	}
-	err := ioutil.WriteFile(buildDateTimeFile, []byte(content), 0777)
-	if err != nil {
-		ctx.Fatalln("Failed to write BUILD_DATETIME to file:", err)
+		ctx.Metrics.SetBuildDateTime(ret.buildDateTime)
 	}
 	ret.environ.Set("BUILD_DATETIME_FILE", buildDateTimeFile)
 
diff --git a/ui/build/paths/config.go b/ui/build/paths/config.go
index 909d93c..f347a11 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,
diff --git a/ui/metrics/metrics.go b/ui/metrics/metrics.go
index bc86f0a..64bbbf3 100644
--- a/ui/metrics/metrics.go
+++ b/ui/metrics/metrics.go
@@ -19,9 +19,9 @@
 	"os"
 	"strconv"
 
-	"android/soong/ui/metrics/metrics_proto"
-
 	"github.com/golang/protobuf/proto"
+
+	soong_metrics_proto "android/soong/ui/metrics/metrics_proto"
 )
 
 const (
@@ -137,13 +137,32 @@
 	}
 }
 
-func (m *Metrics) Serialize() (data []byte, err error) {
-	return proto.Marshal(&m.metrics)
-}
-
 // exports the output to the file at outputPath
 func (m *Metrics) Dump(outputPath string) (err error) {
-	data, err := m.Serialize()
+	return writeMessageToFile(&m.metrics, outputPath)
+}
+
+type CriticalUserJourneysMetrics struct {
+	cujs soong_metrics_proto.CriticalUserJourneysMetrics
+}
+
+func NewCriticalUserJourneysMetrics() *CriticalUserJourneysMetrics {
+	return &CriticalUserJourneysMetrics{}
+}
+
+func (c *CriticalUserJourneysMetrics) Add(name string, metrics *Metrics) {
+	c.cujs.Cujs = append(c.cujs.Cujs, &soong_metrics_proto.CriticalUserJourneyMetrics{
+		Name:    proto.String(name),
+		Metrics: &metrics.metrics,
+	})
+}
+
+func (c *CriticalUserJourneysMetrics) Dump(outputPath string) (err error) {
+	return writeMessageToFile(&c.cujs, outputPath)
+}
+
+func writeMessageToFile(pb proto.Message, outputPath string) (err error) {
+	data, err := proto.Marshal(pb)
 	if err != nil {
 		return err
 	}
diff --git a/ui/metrics/metrics_proto/metrics.pb.go b/ui/metrics/metrics_proto/metrics.pb.go
index 5486ec1..0fe5a0d 100644
--- a/ui/metrics/metrics_proto/metrics.pb.go
+++ b/ui/metrics/metrics_proto/metrics.pb.go
@@ -509,6 +509,95 @@
 	return 0
 }
 
+type CriticalUserJourneyMetrics struct {
+	// The name of a critical user journey test.
+	Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
+	// The metrics produced when running the critical user journey test.
+	Metrics              *MetricsBase `protobuf:"bytes,2,opt,name=metrics" json:"metrics,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}     `json:"-"`
+	XXX_unrecognized     []byte       `json:"-"`
+	XXX_sizecache        int32        `json:"-"`
+}
+
+func (m *CriticalUserJourneyMetrics) Reset()         { *m = CriticalUserJourneyMetrics{} }
+func (m *CriticalUserJourneyMetrics) String() string { return proto.CompactTextString(m) }
+func (*CriticalUserJourneyMetrics) ProtoMessage()    {}
+func (*CriticalUserJourneyMetrics) Descriptor() ([]byte, []int) {
+	return fileDescriptor_6039342a2ba47b72, []int{3}
+}
+
+func (m *CriticalUserJourneyMetrics) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_CriticalUserJourneyMetrics.Unmarshal(m, b)
+}
+func (m *CriticalUserJourneyMetrics) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_CriticalUserJourneyMetrics.Marshal(b, m, deterministic)
+}
+func (m *CriticalUserJourneyMetrics) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_CriticalUserJourneyMetrics.Merge(m, src)
+}
+func (m *CriticalUserJourneyMetrics) XXX_Size() int {
+	return xxx_messageInfo_CriticalUserJourneyMetrics.Size(m)
+}
+func (m *CriticalUserJourneyMetrics) XXX_DiscardUnknown() {
+	xxx_messageInfo_CriticalUserJourneyMetrics.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_CriticalUserJourneyMetrics proto.InternalMessageInfo
+
+func (m *CriticalUserJourneyMetrics) GetName() string {
+	if m != nil && m.Name != nil {
+		return *m.Name
+	}
+	return ""
+}
+
+func (m *CriticalUserJourneyMetrics) GetMetrics() *MetricsBase {
+	if m != nil {
+		return m.Metrics
+	}
+	return nil
+}
+
+type CriticalUserJourneysMetrics struct {
+	// A set of metrics from a run of the critical user journey tests.
+	Cujs                 []*CriticalUserJourneyMetrics `protobuf:"bytes,1,rep,name=cujs" json:"cujs,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}                      `json:"-"`
+	XXX_unrecognized     []byte                        `json:"-"`
+	XXX_sizecache        int32                         `json:"-"`
+}
+
+func (m *CriticalUserJourneysMetrics) Reset()         { *m = CriticalUserJourneysMetrics{} }
+func (m *CriticalUserJourneysMetrics) String() string { return proto.CompactTextString(m) }
+func (*CriticalUserJourneysMetrics) ProtoMessage()    {}
+func (*CriticalUserJourneysMetrics) Descriptor() ([]byte, []int) {
+	return fileDescriptor_6039342a2ba47b72, []int{4}
+}
+
+func (m *CriticalUserJourneysMetrics) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_CriticalUserJourneysMetrics.Unmarshal(m, b)
+}
+func (m *CriticalUserJourneysMetrics) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_CriticalUserJourneysMetrics.Marshal(b, m, deterministic)
+}
+func (m *CriticalUserJourneysMetrics) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_CriticalUserJourneysMetrics.Merge(m, src)
+}
+func (m *CriticalUserJourneysMetrics) XXX_Size() int {
+	return xxx_messageInfo_CriticalUserJourneysMetrics.Size(m)
+}
+func (m *CriticalUserJourneysMetrics) XXX_DiscardUnknown() {
+	xxx_messageInfo_CriticalUserJourneysMetrics.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_CriticalUserJourneysMetrics proto.InternalMessageInfo
+
+func (m *CriticalUserJourneysMetrics) GetCujs() []*CriticalUserJourneyMetrics {
+	if m != nil {
+		return m.Cujs
+	}
+	return nil
+}
+
 func init() {
 	proto.RegisterEnum("soong_build_metrics.MetricsBase_BuildVariant", MetricsBase_BuildVariant_name, MetricsBase_BuildVariant_value)
 	proto.RegisterEnum("soong_build_metrics.MetricsBase_Arch", MetricsBase_Arch_name, MetricsBase_Arch_value)
@@ -516,59 +605,65 @@
 	proto.RegisterType((*MetricsBase)(nil), "soong_build_metrics.MetricsBase")
 	proto.RegisterType((*PerfInfo)(nil), "soong_build_metrics.PerfInfo")
 	proto.RegisterType((*ModuleTypeInfo)(nil), "soong_build_metrics.ModuleTypeInfo")
+	proto.RegisterType((*CriticalUserJourneyMetrics)(nil), "soong_build_metrics.CriticalUserJourneyMetrics")
+	proto.RegisterType((*CriticalUserJourneysMetrics)(nil), "soong_build_metrics.CriticalUserJourneysMetrics")
 }
 
 func init() { proto.RegisterFile("metrics.proto", fileDescriptor_6039342a2ba47b72) }
 
 var fileDescriptor_6039342a2ba47b72 = []byte{
-	// 769 bytes of a gzipped FileDescriptorProto
+	// 834 bytes of a gzipped FileDescriptorProto
 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x55, 0x6f, 0x6b, 0xdb, 0x46,
-	0x18, 0xaf, 0x62, 0x25, 0x96, 0x1e, 0xc5, 0xae, 0x7a, 0xc9, 0xa8, 0xca, 0x08, 0x33, 0x66, 0x1d,
-	0x7e, 0xb1, 0xba, 0xc5, 0x14, 0x53, 0x4c, 0x19, 0xd8, 0x89, 0x29, 0x25, 0xd8, 0x2e, 0x4a, 0xdc,
-	0x95, 0xed, 0xc5, 0xa1, 0x4a, 0xe7, 0x46, 0x9b, 0xa5, 0x13, 0x77, 0xa7, 0x32, 0x7f, 0x88, 0x7d,
-	0x93, 0x7d, 0xad, 0x7d, 0x8f, 0x71, 0xcf, 0x49, 0x8e, 0x02, 0x81, 0x85, 0xbe, 0x3b, 0x3d, 0xbf,
-	0x3f, 0xf7, 0x7b, 0x4e, 0xba, 0x47, 0xd0, 0xc9, 0x98, 0x12, 0x69, 0x2c, 0x87, 0x85, 0xe0, 0x8a,
-	0x93, 0x13, 0xc9, 0x79, 0xfe, 0x85, 0x7e, 0x2e, 0xd3, 0x6d, 0x42, 0x2b, 0xa8, 0xff, 0x8f, 0x0b,
-	0xde, 0xc2, 0xac, 0x67, 0x91, 0x64, 0xe4, 0x15, 0x9c, 0x1a, 0x42, 0x12, 0x29, 0x46, 0x55, 0x9a,
-	0x31, 0xa9, 0xa2, 0xac, 0x08, 0xac, 0x9e, 0x35, 0x68, 0x85, 0x04, 0xb1, 0x8b, 0x48, 0xb1, 0xeb,
-	0x1a, 0x21, 0xcf, 0xc0, 0x31, 0x8a, 0x34, 0x09, 0x0e, 0x7a, 0xd6, 0xc0, 0x0d, 0xdb, 0xf8, 0xfc,
-	0x3e, 0x21, 0x13, 0x78, 0x56, 0x6c, 0x23, 0xb5, 0xe1, 0x22, 0xa3, 0x5f, 0x99, 0x90, 0x29, 0xcf,
-	0x69, 0xcc, 0x13, 0x96, 0x47, 0x19, 0x0b, 0x5a, 0xc8, 0x7d, 0x5a, 0x13, 0x3e, 0x1a, 0xfc, 0xbc,
-	0x82, 0xc9, 0x73, 0xe8, 0xaa, 0x48, 0x7c, 0x61, 0x8a, 0x16, 0x82, 0x27, 0x65, 0xac, 0x02, 0x1b,
-	0x05, 0x1d, 0x53, 0xfd, 0x60, 0x8a, 0x24, 0x81, 0xd3, 0x8a, 0x66, 0x42, 0x7c, 0x8d, 0x44, 0x1a,
-	0xe5, 0x2a, 0x38, 0xec, 0x59, 0x83, 0xee, 0xe8, 0xc5, 0xf0, 0x9e, 0x9e, 0x87, 0x8d, 0x7e, 0x87,
-	0x33, 0x8d, 0x7c, 0x34, 0xa2, 0x49, 0x6b, 0xbe, 0x7c, 0x17, 0x12, 0xe3, 0xd7, 0x04, 0xc8, 0x0a,
-	0xbc, 0x6a, 0x97, 0x48, 0xc4, 0x37, 0xc1, 0x11, 0x9a, 0x3f, 0xff, 0x5f, 0xf3, 0xa9, 0x88, 0x6f,
-	0x26, 0xed, 0xf5, 0xf2, 0x72, 0xb9, 0xfa, 0x75, 0x19, 0x82, 0xb1, 0xd0, 0x45, 0x32, 0x84, 0x93,
-	0x86, 0xe1, 0x3e, 0x75, 0x1b, 0x5b, 0x7c, 0x72, 0x4b, 0xac, 0x03, 0xfc, 0x0c, 0x55, 0x2c, 0x1a,
-	0x17, 0xe5, 0x9e, 0xee, 0x20, 0xdd, 0x37, 0xc8, 0x79, 0x51, 0xd6, 0xec, 0x4b, 0x70, 0x6f, 0xb8,
-	0xac, 0xc2, 0xba, 0xdf, 0x14, 0xd6, 0xd1, 0x06, 0x18, 0x35, 0x84, 0x0e, 0x9a, 0x8d, 0xf2, 0xc4,
-	0x18, 0xc2, 0x37, 0x19, 0x7a, 0xda, 0x64, 0x94, 0x27, 0xe8, 0xf9, 0x14, 0xda, 0xe8, 0xc9, 0x65,
-	0xe0, 0x61, 0x0f, 0x47, 0xfa, 0x71, 0x25, 0x49, 0xbf, 0xda, 0x8c, 0x4b, 0xca, 0xfe, 0x52, 0x22,
-	0x0a, 0x8e, 0x11, 0xf6, 0x0c, 0x3c, 0xd7, 0xa5, 0x3d, 0x27, 0x16, 0x5c, 0x4a, 0x6d, 0xd1, 0xb9,
-	0xe5, 0x9c, 0xeb, 0xda, 0x4a, 0x92, 0x9f, 0xe0, 0x71, 0x83, 0x83, 0xb1, 0xbb, 0xe6, 0xf3, 0xd9,
-	0xb3, 0x30, 0xc8, 0x0b, 0x38, 0x69, 0xf0, 0xf6, 0x2d, 0x3e, 0x36, 0x07, 0xbb, 0xe7, 0x36, 0x72,
-	0xf3, 0x52, 0xd1, 0x24, 0x15, 0x81, 0x6f, 0x72, 0xf3, 0x52, 0x5d, 0xa4, 0x82, 0xfc, 0x02, 0x9e,
-	0x64, 0xaa, 0x2c, 0xa8, 0xe2, 0x7c, 0x2b, 0x83, 0x27, 0xbd, 0xd6, 0xc0, 0x1b, 0x9d, 0xdd, 0x7b,
-	0x44, 0x1f, 0x98, 0xd8, 0xbc, 0xcf, 0x37, 0x3c, 0x04, 0x54, 0x5c, 0x6b, 0x01, 0x99, 0x80, 0xfb,
-	0x67, 0xa4, 0x52, 0x2a, 0xca, 0x5c, 0x06, 0xe4, 0x21, 0x6a, 0x47, 0xf3, 0xc3, 0x32, 0x97, 0xe4,
-	0x2d, 0x80, 0x61, 0xa2, 0xf8, 0xe4, 0x21, 0x62, 0x17, 0xd1, 0x5a, 0x9d, 0xa7, 0xf9, 0x1f, 0x91,
-	0x51, 0x9f, 0x3e, 0x48, 0x8d, 0x02, 0xad, 0xee, 0xbf, 0x82, 0xe3, 0x3b, 0x17, 0xc5, 0x01, 0x7b,
-	0x7d, 0x35, 0x0f, 0xfd, 0x47, 0xa4, 0x03, 0xae, 0x5e, 0x5d, 0xcc, 0x67, 0xeb, 0x77, 0xbe, 0x45,
-	0xda, 0xa0, 0x2f, 0x97, 0x7f, 0xd0, 0x7f, 0x0b, 0x36, 0x1e, 0xa5, 0x07, 0xf5, 0xa7, 0xe1, 0x3f,
-	0xd2, 0xe8, 0x34, 0x5c, 0xf8, 0x16, 0x71, 0xe1, 0x70, 0x1a, 0x2e, 0xc6, 0xaf, 0xfd, 0x03, 0x5d,
-	0xfb, 0xf4, 0x66, 0xec, 0xb7, 0x08, 0xc0, 0xd1, 0xa7, 0x37, 0x63, 0x3a, 0x7e, 0xed, 0xdb, 0xfd,
-	0xbf, 0x2d, 0x70, 0xea, 0x1c, 0x84, 0x80, 0x9d, 0x30, 0x19, 0xe3, 0x6c, 0x72, 0x43, 0x5c, 0xeb,
-	0x1a, 0x4e, 0x17, 0x33, 0x89, 0x70, 0x4d, 0xce, 0x00, 0xa4, 0x8a, 0x84, 0xc2, 0x71, 0x86, 0x73,
-	0xc7, 0x0e, 0x5d, 0xac, 0xe8, 0x29, 0x46, 0xbe, 0x07, 0x57, 0xb0, 0x68, 0x6b, 0x50, 0x1b, 0x51,
-	0x47, 0x17, 0x10, 0x3c, 0x03, 0xc8, 0x58, 0xc6, 0xc5, 0x8e, 0x96, 0x92, 0xe1, 0x54, 0xb1, 0x43,
-	0xd7, 0x54, 0xd6, 0x92, 0xf5, 0xff, 0xb5, 0xa0, 0xbb, 0xe0, 0x49, 0xb9, 0x65, 0xd7, 0xbb, 0x82,
-	0x61, 0xaa, 0xdf, 0xe1, 0xd8, 0x9c, 0x9b, 0xdc, 0x49, 0xc5, 0x32, 0x4c, 0xd7, 0x1d, 0xbd, 0xbc,
-	0xff, 0xba, 0xdc, 0x91, 0x9a, 0x61, 0x74, 0x85, 0xb2, 0xc6, 0xc5, 0xf9, 0x7c, 0x5b, 0x25, 0x3f,
-	0x80, 0x97, 0xa1, 0x86, 0xaa, 0x5d, 0x51, 0x77, 0x09, 0xd9, 0xde, 0x86, 0xfc, 0x08, 0xdd, 0xbc,
-	0xcc, 0x28, 0xdf, 0x50, 0x53, 0x94, 0xd8, 0x6f, 0x27, 0x3c, 0xce, 0xcb, 0x6c, 0xb5, 0x31, 0xfb,
-	0xc9, 0xfe, 0x4b, 0xf0, 0x1a, 0x7b, 0xdd, 0x7d, 0x17, 0x2e, 0x1c, 0x5e, 0xad, 0x56, 0x4b, 0xfd,
-	0xd2, 0x1c, 0xb0, 0x17, 0xd3, 0xcb, 0xb9, 0x7f, 0x30, 0xfb, 0xee, 0xb7, 0xea, 0xef, 0x51, 0x25,
-	0xa7, 0xf8, 0x4b, 0xf9, 0x2f, 0x00, 0x00, 0xff, 0xff, 0x81, 0xd0, 0x84, 0x23, 0x62, 0x06, 0x00,
-	0x00,
+	0x18, 0xaf, 0x62, 0x25, 0xb6, 0x1e, 0xc5, 0xae, 0x7a, 0xc9, 0xa8, 0xba, 0x12, 0x66, 0xc4, 0x3a,
+	0xf2, 0x62, 0x75, 0x8b, 0x29, 0xa1, 0x98, 0x32, 0x48, 0x1c, 0x53, 0xba, 0x60, 0xbb, 0x28, 0x71,
+	0x57, 0xb6, 0x17, 0x87, 0x22, 0x9d, 0x1b, 0x75, 0x96, 0x4e, 0xdc, 0x9d, 0xca, 0xfc, 0x21, 0xf6,
+	0x4d, 0xf6, 0xb5, 0xf6, 0x3d, 0xc6, 0x3d, 0x27, 0x39, 0x0a, 0x78, 0x34, 0xf4, 0xdd, 0xe9, 0xf9,
+	0xfd, 0xb9, 0xdf, 0x73, 0xd2, 0x3d, 0x82, 0x6e, 0xc6, 0x94, 0x48, 0x63, 0x39, 0x28, 0x04, 0x57,
+	0x9c, 0x1c, 0x48, 0xce, 0xf3, 0x4f, 0xf4, 0xba, 0x4c, 0x57, 0x09, 0xad, 0xa0, 0xe0, 0x1f, 0x07,
+	0xdc, 0xa9, 0x59, 0x9f, 0x45, 0x92, 0x91, 0x97, 0x70, 0x68, 0x08, 0x49, 0xa4, 0x18, 0x55, 0x69,
+	0xc6, 0xa4, 0x8a, 0xb2, 0xc2, 0xb7, 0xfa, 0xd6, 0x71, 0x2b, 0x24, 0x88, 0x9d, 0x47, 0x8a, 0x5d,
+	0xd5, 0x08, 0x79, 0x02, 0x1d, 0xa3, 0x48, 0x13, 0x7f, 0xa7, 0x6f, 0x1d, 0x3b, 0x61, 0x1b, 0x9f,
+	0xdf, 0x25, 0x64, 0x04, 0x4f, 0x8a, 0x55, 0xa4, 0x96, 0x5c, 0x64, 0xf4, 0x0b, 0x13, 0x32, 0xe5,
+	0x39, 0x8d, 0x79, 0xc2, 0xf2, 0x28, 0x63, 0x7e, 0x0b, 0xb9, 0x8f, 0x6b, 0xc2, 0x07, 0x83, 0x8f,
+	0x2b, 0x98, 0x3c, 0x83, 0x9e, 0x8a, 0xc4, 0x27, 0xa6, 0x68, 0x21, 0x78, 0x52, 0xc6, 0xca, 0xb7,
+	0x51, 0xd0, 0x35, 0xd5, 0xf7, 0xa6, 0x48, 0x12, 0x38, 0xac, 0x68, 0x26, 0xc4, 0x97, 0x48, 0xa4,
+	0x51, 0xae, 0xfc, 0xdd, 0xbe, 0x75, 0xdc, 0x1b, 0x3e, 0x1f, 0x6c, 0xe9, 0x79, 0xd0, 0xe8, 0x77,
+	0x70, 0xa6, 0x91, 0x0f, 0x46, 0x34, 0x6a, 0x4d, 0x66, 0x6f, 0x43, 0x62, 0xfc, 0x9a, 0x00, 0x99,
+	0x83, 0x5b, 0xed, 0x12, 0x89, 0xf8, 0xc6, 0xdf, 0x43, 0xf3, 0x67, 0x5f, 0x35, 0x3f, 0x15, 0xf1,
+	0xcd, 0xa8, 0xbd, 0x98, 0x5d, 0xcc, 0xe6, 0xbf, 0xcd, 0x42, 0x30, 0x16, 0xba, 0x48, 0x06, 0x70,
+	0xd0, 0x30, 0xdc, 0xa4, 0x6e, 0x63, 0x8b, 0x8f, 0x6e, 0x89, 0x75, 0x80, 0x9f, 0xa1, 0x8a, 0x45,
+	0xe3, 0xa2, 0xdc, 0xd0, 0x3b, 0x48, 0xf7, 0x0c, 0x32, 0x2e, 0xca, 0x9a, 0x7d, 0x01, 0xce, 0x0d,
+	0x97, 0x55, 0x58, 0xe7, 0x9b, 0xc2, 0x76, 0xb4, 0x01, 0x46, 0x0d, 0xa1, 0x8b, 0x66, 0xc3, 0x3c,
+	0x31, 0x86, 0xf0, 0x4d, 0x86, 0xae, 0x36, 0x19, 0xe6, 0x09, 0x7a, 0x3e, 0x86, 0x36, 0x7a, 0x72,
+	0xe9, 0xbb, 0xd8, 0xc3, 0x9e, 0x7e, 0x9c, 0x4b, 0x12, 0x54, 0x9b, 0x71, 0x49, 0xd9, 0x5f, 0x4a,
+	0x44, 0xfe, 0x3e, 0xc2, 0xae, 0x81, 0x27, 0xba, 0xb4, 0xe1, 0xc4, 0x82, 0x4b, 0xa9, 0x2d, 0xba,
+	0xb7, 0x9c, 0xb1, 0xae, 0xcd, 0x25, 0xf9, 0x09, 0x1e, 0x36, 0x38, 0x18, 0xbb, 0x67, 0x3e, 0x9f,
+	0x0d, 0x0b, 0x83, 0x3c, 0x87, 0x83, 0x06, 0x6f, 0xd3, 0xe2, 0x43, 0x73, 0xb0, 0x1b, 0x6e, 0x23,
+	0x37, 0x2f, 0x15, 0x4d, 0x52, 0xe1, 0x7b, 0x26, 0x37, 0x2f, 0xd5, 0x79, 0x2a, 0xc8, 0x2f, 0xe0,
+	0x4a, 0xa6, 0xca, 0x82, 0x2a, 0xce, 0x57, 0xd2, 0x7f, 0xd4, 0x6f, 0x1d, 0xbb, 0xc3, 0xa3, 0xad,
+	0x47, 0xf4, 0x9e, 0x89, 0xe5, 0xbb, 0x7c, 0xc9, 0x43, 0x40, 0xc5, 0x95, 0x16, 0x90, 0x11, 0x38,
+	0x7f, 0x46, 0x2a, 0xa5, 0xa2, 0xcc, 0xa5, 0x4f, 0xee, 0xa3, 0xee, 0x68, 0x7e, 0x58, 0xe6, 0x92,
+	0xbc, 0x01, 0x30, 0x4c, 0x14, 0x1f, 0xdc, 0x47, 0xec, 0x20, 0x5a, 0xab, 0xf3, 0x34, 0xff, 0x1c,
+	0x19, 0xf5, 0xe1, 0xbd, 0xd4, 0x28, 0xd0, 0xea, 0xe0, 0x25, 0xec, 0xdf, 0xb9, 0x28, 0x1d, 0xb0,
+	0x17, 0x97, 0x93, 0xd0, 0x7b, 0x40, 0xba, 0xe0, 0xe8, 0xd5, 0xf9, 0xe4, 0x6c, 0xf1, 0xd6, 0xb3,
+	0x48, 0x1b, 0xf4, 0xe5, 0xf2, 0x76, 0x82, 0x37, 0x60, 0xe3, 0x51, 0xba, 0x50, 0x7f, 0x1a, 0xde,
+	0x03, 0x8d, 0x9e, 0x86, 0x53, 0xcf, 0x22, 0x0e, 0xec, 0x9e, 0x86, 0xd3, 0x93, 0x57, 0xde, 0x8e,
+	0xae, 0x7d, 0x7c, 0x7d, 0xe2, 0xb5, 0x08, 0xc0, 0xde, 0xc7, 0xd7, 0x27, 0xf4, 0xe4, 0x95, 0x67,
+	0x07, 0x7f, 0x5b, 0xd0, 0xa9, 0x73, 0x10, 0x02, 0x76, 0xc2, 0x64, 0x8c, 0xb3, 0xc9, 0x09, 0x71,
+	0xad, 0x6b, 0x38, 0x5d, 0xcc, 0x24, 0xc2, 0x35, 0x39, 0x02, 0x90, 0x2a, 0x12, 0x0a, 0xc7, 0x19,
+	0xce, 0x1d, 0x3b, 0x74, 0xb0, 0xa2, 0xa7, 0x18, 0x79, 0x0a, 0x8e, 0x60, 0xd1, 0xca, 0xa0, 0x36,
+	0xa2, 0x1d, 0x5d, 0x40, 0xf0, 0x08, 0x20, 0x63, 0x19, 0x17, 0x6b, 0x5a, 0x4a, 0x86, 0x53, 0xc5,
+	0x0e, 0x1d, 0x53, 0x59, 0x48, 0x16, 0xfc, 0x6b, 0x41, 0x6f, 0xca, 0x93, 0x72, 0xc5, 0xae, 0xd6,
+	0x05, 0xc3, 0x54, 0x7f, 0xc0, 0xbe, 0x39, 0x37, 0xb9, 0x96, 0x8a, 0x65, 0x98, 0xae, 0x37, 0x7c,
+	0xb1, 0xfd, 0xba, 0xdc, 0x91, 0x9a, 0x61, 0x74, 0x89, 0xb2, 0xc6, 0xc5, 0xb9, 0xbe, 0xad, 0x92,
+	0x1f, 0xc0, 0xcd, 0x50, 0x43, 0xd5, 0xba, 0xa8, 0xbb, 0x84, 0x6c, 0x63, 0x43, 0x7e, 0x84, 0x5e,
+	0x5e, 0x66, 0x94, 0x2f, 0xa9, 0x29, 0x4a, 0xec, 0xb7, 0x1b, 0xee, 0xe7, 0x65, 0x36, 0x5f, 0x9a,
+	0xfd, 0x64, 0xf0, 0x02, 0xdc, 0xc6, 0x5e, 0x77, 0xdf, 0x85, 0x03, 0xbb, 0x97, 0xf3, 0xf9, 0x4c,
+	0xbf, 0xb4, 0x0e, 0xd8, 0xd3, 0xd3, 0x8b, 0x89, 0xb7, 0x13, 0xac, 0xe0, 0xfb, 0xb1, 0x48, 0x55,
+	0x1a, 0x47, 0xab, 0x85, 0x64, 0xe2, 0x57, 0x5e, 0x8a, 0x9c, 0xad, 0xab, 0xdb, 0xbe, 0x39, 0x74,
+	0xab, 0x71, 0xe8, 0x23, 0x68, 0x57, 0x5d, 0x62, 0x4a, 0x77, 0xd8, 0xff, 0xda, 0xc0, 0x08, 0x6b,
+	0x41, 0x70, 0x0d, 0x4f, 0xb7, 0xec, 0x26, 0xeb, 0xed, 0xc6, 0x60, 0xc7, 0xe5, 0x67, 0xe9, 0x5b,
+	0xf8, 0xb1, 0x6e, 0x3f, 0xd9, 0xff, 0x4f, 0x1b, 0xa2, 0xf8, 0xec, 0xbb, 0xdf, 0xab, 0xff, 0x61,
+	0xa5, 0xa0, 0xf8, 0x93, 0xfc, 0x2f, 0x00, 0x00, 0xff, 0xff, 0x20, 0x07, 0xbc, 0xf0, 0x34, 0x07,
+	0x00, 0x00,
 }
diff --git a/ui/metrics/metrics_proto/metrics.proto b/ui/metrics/metrics_proto/metrics.proto
index 93034eb..1ea24bf 100644
--- a/ui/metrics/metrics_proto/metrics.proto
+++ b/ui/metrics/metrics_proto/metrics.proto
@@ -125,3 +125,16 @@
   // The number of logical modules.
   optional uint32 num_of_modules = 3;
 }
+
+message CriticalUserJourneyMetrics {
+  // The name of a critical user journey test.
+  optional string name = 1;
+
+  // The metrics produced when running the critical user journey test.
+  optional MetricsBase metrics = 2;
+}
+
+message CriticalUserJourneysMetrics {
+  // A set of metrics from a run of the critical user journey tests.
+  repeated CriticalUserJourneyMetrics cujs = 1;
+}
\ No newline at end of file
diff --git a/ui/status/log.go b/ui/status/log.go
index 9090f49..d407248 100644
--- a/ui/status/log.go
+++ b/ui/status/log.go
@@ -180,12 +180,12 @@
 func (e *errorProtoLog) Flush() {
 	data, err := proto.Marshal(&e.errorProto)
 	if err != nil {
-		e.log.Println("Failed to marshal build status proto: %v", err)
+		e.log.Printf("Failed to marshal build status proto: %v\n", err)
 		return
 	}
 	err = ioutil.WriteFile(e.filename, []byte(data), 0644)
 	if err != nil {
-		e.log.Println("Failed to write file %s: %v", e.errorProto, err)
+		e.log.Printf("Failed to write file %s: %v\n", e.filename, err)
 	}
 }
 
diff --git a/xml/xml_test.go b/xml/xml_test.go
index 0a11566..f42ba77 100644
--- a/xml/xml_test.go
+++ b/xml/xml_test.go
@@ -104,7 +104,7 @@
 		{rule: "xmllint-minimal", input: "baz.xml"},
 	} {
 		t.Run(tc.schemaType, func(t *testing.T) {
-			rule := ctx.ModuleForTests(tc.input, "android_arm64_armv8-a").Rule(tc.rule)
+			rule := ctx.ModuleForTests(tc.input, "android_arm64_armv8-a_core").Rule(tc.rule)
 			assertEqual(t, "input", tc.input, rule.Input.String())
 			if tc.schemaType != "" {
 				assertEqual(t, "schema", tc.schema, rule.Args[tc.schemaType])
@@ -112,6 +112,6 @@
 		})
 	}
 
-	m := ctx.ModuleForTests("foo.xml", "android_arm64_armv8-a").Module().(*prebuiltEtcXml)
+	m := ctx.ModuleForTests("foo.xml", "android_arm64_armv8-a_core").Module().(*prebuiltEtcXml)
 	assertEqual(t, "installDir", buildDir+"/target/product/test_device/system/etc", m.InstallDirPath().String())
 }