Merge "Have bpfix not remove empty lists"
diff --git a/Android.bp b/Android.bp
index ae31a60..32b89d1 100644
--- a/Android.bp
+++ b/Android.bp
@@ -211,6 +211,7 @@
         "java/java.go",
         "java/proto.go",
         "java/resources.go",
+        "java/system_modules.go",
     ],
     testSrcs: [
         "java/java_test.go",
@@ -228,6 +229,7 @@
     srcs: [
         "java/config/config.go",
         "java/config/error_prone.go",
+        "java/config/kotlin.go",
         "java/config/makevars.go",
     ],
 }
diff --git a/android/androidmk.go b/android/androidmk.go
index 5ce486d..759d328 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -43,6 +43,7 @@
 	OutputFile OptionalPath
 	Disabled   bool
 	Include    string
+	Required   []string
 
 	Custom func(w io.Writer, name, prefix, moduleDir string, data AndroidMkData)
 
@@ -168,6 +169,8 @@
 		data.Include = "$(BUILD_PREBUILT)"
 	}
 
+	data.Required = amod.commonProperties.Required
+
 	// Make does not understand LinuxBionic
 	if amod.Os() == LinuxBionic {
 		return nil
@@ -197,8 +200,8 @@
 	fmt.Fprintln(&data.preamble, "LOCAL_MODULE_CLASS :=", data.Class)
 	fmt.Fprintln(&data.preamble, "LOCAL_PREBUILT_MODULE_FILE :=", data.OutputFile.String())
 
-	if len(amod.commonProperties.Required) > 0 {
-		fmt.Fprintln(&data.preamble, "LOCAL_REQUIRED_MODULES := "+strings.Join(amod.commonProperties.Required, " "))
+	if len(data.Required) > 0 {
+		fmt.Fprintln(&data.preamble, "LOCAL_REQUIRED_MODULES := "+strings.Join(data.Required, " "))
 	}
 
 	archStr := amod.Arch().ArchType.String()
diff --git a/android/arch.go b/android/arch.go
index db017fd..eaa35a4 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -468,7 +468,7 @@
 		"Android64",
 		"Android32",
 		"Bionic",
-		// TODO(dwillemsen): "Linux",
+		"Linux",
 		"Not_windows",
 		"Arm_on_x86",
 		"Arm_on_x86_64",
@@ -479,7 +479,7 @@
 		for _, archType := range osArchTypeMap[os] {
 			targets = append(targets, os.Field+"_"+archType.Name)
 
-			if false { // TODO(dwillemsen): os.Linux()
+			if os.Linux() {
 				target := "Linux_" + archType.Name
 				if !inList(target, targets) {
 					targets = append(targets, target)
@@ -696,7 +696,7 @@
 		//         key: value,
 		//     },
 		// }
-		if false { // TODO(dwillemsen): os.Linux()
+		if os.Linux() {
 			field = "Linux"
 			prefix = "target.linux"
 			a.appendProperties(ctx, genProps, targetProp, field, prefix)
diff --git a/android/config.go b/android/config.go
index a70fa76..dca998f 100644
--- a/android/config.go
+++ b/android/config.go
@@ -78,6 +78,7 @@
 	srcDir   string // the path of the root source directory
 	buildDir string // the path of the build output directory
 
+	env       map[string]string
 	envLock   sync.Mutex
 	envDeps   map[string]string
 	envFrozen bool
@@ -87,6 +88,9 @@
 	captureBuild      bool // true for tests, saves build parameters for each module
 	ignoreEnvironment bool // true for tests, returns empty from all Getenv calls
 
+	useOpenJDK9    bool // Use OpenJDK9, but possibly target 1.8
+	targetOpenJDK9 bool // Use OpenJDK9 and target 1.9
+
 	OncePer
 }
 
@@ -168,32 +172,36 @@
 }
 
 // TestConfig returns a Config object suitable for using for tests
-func TestConfig(buildDir string) Config {
+func TestConfig(buildDir string, env map[string]string) Config {
 	config := &config{
 		ProductVariables: productVariables{
 			DeviceName: stringPtr("test_device"),
 		},
 
-		buildDir:          buildDir,
-		captureBuild:      true,
-		ignoreEnvironment: true,
+		buildDir:     buildDir,
+		captureBuild: true,
+		env:          env,
 	}
 	config.deviceConfig = &deviceConfig{
 		config: config,
 	}
 
+	if err := config.fromEnv(); err != nil {
+		panic(err)
+	}
+
 	return Config{config}
 }
 
 // TestConfig returns a Config object suitable for using for tests that need to run the arch mutator
-func TestArchConfig(buildDir string) Config {
-	testConfig := TestConfig(buildDir)
+func TestArchConfig(buildDir string, env map[string]string) Config {
+	testConfig := TestConfig(buildDir, env)
 	config := testConfig.config
 
 	config.Targets = map[OsClass][]Target{
 		Device: []Target{
-			{Android, Arch{ArchType: Arm64, Native: true}},
-			{Android, Arch{ArchType: Arm, Native: true}},
+			{Android, Arch{ArchType: Arm64, ArchVariant: "armv8-a", Native: true}},
+			{Android, Arch{ArchType: Arm, ArchVariant: "armv7-a-neon", Native: true}},
 		},
 		Host: []Target{
 			{BuildOs, Arch{ArchType: X86_64}},
@@ -212,6 +220,8 @@
 		ConfigFileName:           filepath.Join(buildDir, configFileName),
 		ProductVariablesFileName: filepath.Join(buildDir, productVariablesFileName),
 
+		env: originalEnv,
+
 		srcDir:   srcDir,
 		buildDir: buildDir,
 	}
@@ -270,9 +280,31 @@
 	config.Targets = targets
 	config.BuildOsVariant = targets[Host][0].String()
 
+	if err := config.fromEnv(); err != nil {
+		return Config{}, err
+	}
+
 	return Config{config}, nil
 }
 
+func (c *config) fromEnv() error {
+	switch c.Getenv("EXPERIMENTAL_USE_OPENJDK9") {
+	case "":
+		// Use OpenJDK8
+	case "1.8":
+		// Use OpenJDK9, but target 1.8
+		c.useOpenJDK9 = true
+	case "true":
+		// Use OpenJDK9 and target 1.9
+		c.useOpenJDK9 = true
+		c.targetOpenJDK9 = true
+	default:
+		return fmt.Errorf(`Invalid value for EXPERIMENTAL_USE_OPENJDK9, should be "", "1.8", or "true"`)
+	}
+
+	return nil
+}
+
 func (c *config) RemoveAbandonedFiles() bool {
 	return false
 }
@@ -335,9 +367,7 @@
 		if c.envFrozen {
 			panic("Cannot access new environment variables after envdeps are frozen")
 		}
-		if !c.ignoreEnvironment {
-			val, _ = originalEnv[key]
-		}
+		val, _ = c.env[key]
 		c.envDeps[key] = val
 	}
 	return val
@@ -517,6 +547,16 @@
 	return Bool(c.ProductVariables.UseGoma)
 }
 
+// Returns true if OpenJDK9 prebuilts are being used
+func (c *config) UseOpenJDK9() bool {
+	return c.useOpenJDK9
+}
+
+// Returns true if -source 1.9 -target 1.9 is being passed to javac
+func (c *config) TargetOpenJDK9() bool {
+	return c.targetOpenJDK9
+}
+
 func (c *config) ClangTidy() bool {
 	return Bool(c.ProductVariables.ClangTidy)
 }
diff --git a/android/module.go b/android/module.go
index 2890b60..9afc8a7 100644
--- a/android/module.go
+++ b/android/module.go
@@ -61,7 +61,7 @@
 	Windows() bool
 	Debug() bool
 	PrimaryArch() bool
-	Vendor() bool
+	InstallOnVendorPartition() bool
 	AConfig() Config
 	DeviceConfig() DeviceConfig
 }
@@ -99,7 +99,11 @@
 type Module interface {
 	blueprint.Module
 
+	// GenerateAndroidBuildActions is analogous to Blueprints' GenerateBuildActions,
+	// but GenerateAndroidBuildActions also has access to Android-specific information.
+	// For more information, see Module.GenerateBuildActions within Blueprint's module_ctx.go
 	GenerateAndroidBuildActions(ModuleContext)
+
 	DepsMutator(BottomUpMutatorContext)
 
 	base() *ModuleBase
@@ -690,7 +694,7 @@
 	return DeviceConfig{a.config.deviceConfig}
 }
 
-func (a *androidBaseContextImpl) Vendor() bool {
+func (a *androidBaseContextImpl) InstallOnVendorPartition() bool {
 	return a.vendor
 }
 
@@ -882,10 +886,10 @@
 			}
 		} else if pathtools.IsGlob(s) {
 			globbedSrcFiles := ctx.Glob(filepath.Join(prefix, s), excludes)
-			expandedSrcFiles = append(expandedSrcFiles, globbedSrcFiles...)
-			for i, s := range expandedSrcFiles {
-				expandedSrcFiles[i] = s.(ModuleSrcPath).WithSubDir(ctx, subDir)
+			for i, s := range globbedSrcFiles {
+				globbedSrcFiles[i] = s.(ModuleSrcPath).WithSubDir(ctx, subDir)
 			}
+			expandedSrcFiles = append(expandedSrcFiles, globbedSrcFiles...)
 		} else {
 			s := PathForModuleSrc(ctx, s).WithSubDir(ctx, subDir)
 			expandedSrcFiles = append(expandedSrcFiles, s)
diff --git a/android/mutator.go b/android/mutator.go
index 04407eb..66a1bad 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -18,12 +18,13 @@
 	"github.com/google/blueprint"
 )
 
-// Mutator phases:
-//   Pre-arch
-//   Arch
-//   Pre-deps
-//   Deps
-//   PostDeps
+// Phases:
+//   run Pre-arch mutators
+//   run archMutator
+//   run Pre-deps mutators
+//   run depsMutator
+//   run PostDeps mutators
+//   continue on to GenerateAndroidBuildActions
 
 func registerMutatorsToContext(ctx *blueprint.Context, mutators []*mutator) {
 	for _, t := range mutators {
diff --git a/android/paths.go b/android/paths.go
index 69a7b0d..f88d650 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -303,6 +303,39 @@
 	return list[:k]
 }
 
+// HasExt returns true of any of the paths have extension ext, otherwise false
+func (p Paths) HasExt(ext string) bool {
+	for _, path := range p {
+		if path.Ext() == ext {
+			return true
+		}
+	}
+
+	return false
+}
+
+// FilterByExt returns the subset of the paths that have extension ext
+func (p Paths) FilterByExt(ext string) Paths {
+	ret := make(Paths, 0, len(p))
+	for _, path := range p {
+		if path.Ext() == ext {
+			ret = append(ret, path)
+		}
+	}
+	return ret
+}
+
+// FilterOutByExt returns the subset of the paths that do not have extension ext
+func (p Paths) FilterOutByExt(ext string) Paths {
+	ret := make(Paths, 0, len(p))
+	for _, path := range p {
+		if path.Ext() != ext {
+			ret = append(ret, path)
+		}
+	}
+	return ret
+}
+
 // WritablePaths is a slice of WritablePaths, used for multiple outputs.
 type WritablePaths []WritablePath
 
@@ -706,7 +739,7 @@
 		var partition string
 		if ctx.InstallInData() {
 			partition = "data"
-		} else if ctx.Vendor() {
+		} else if ctx.InstallOnVendorPartition() {
 			partition = ctx.DeviceConfig().VendorPath()
 		} else {
 			partition = "system"
diff --git a/android/paths_test.go b/android/paths_test.go
index 3986b71..248f4d4 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -209,7 +209,7 @@
 }
 
 func TestPathForModuleInstall(t *testing.T) {
-	testConfig := TestConfig("")
+	testConfig := TestConfig("", nil)
 
 	hostTarget := Target{Os: Linux}
 	deviceTarget := Target{Os: Android}
diff --git a/android/prebuilt_test.go b/android/prebuilt_test.go
index 9a1de9c..93f5805 100644
--- a/android/prebuilt_test.go
+++ b/android/prebuilt_test.go
@@ -118,7 +118,7 @@
 	}
 	defer os.RemoveAll(buildDir)
 
-	config := TestConfig(buildDir)
+	config := TestConfig(buildDir, nil)
 
 	for _, test := range prebuiltsTests {
 		t.Run(test.name, func(t *testing.T) {
diff --git a/android/variable.go b/android/variable.go
index 9dd9d25..16a6b11 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -92,8 +92,8 @@
 		}
 
 		Pdk struct {
-			Enabled *bool
-		}
+			Enabled *bool `android:"arch_variant"`
+		} `android:"arch_variant"`
 
 		Uml struct {
 			Cppflags []string
diff --git a/androidmk/cmd/androidmk/androidmk.go b/androidmk/cmd/androidmk/androidmk.go
index 5fad586..a49f620 100644
--- a/androidmk/cmd/androidmk/androidmk.go
+++ b/androidmk/cmd/androidmk/androidmk.go
@@ -2,6 +2,7 @@
 
 import (
 	"bytes"
+	"flag"
 	"fmt"
 	"io/ioutil"
 	"os"
@@ -15,6 +16,13 @@
 	bpparser "github.com/google/blueprint/parser"
 )
 
+var usage = func() {
+	fmt.Fprintf(os.Stderr, "usage: androidmk [flags] <inputFile>\n"+
+		"\nandroidmk parses <inputFile> as an Android.mk file and attempts to output an analogous Android.bp file (to standard out)\n")
+	flag.PrintDefaults()
+	os.Exit(1)
+}
+
 // TODO: non-expanded variables with expressions
 
 type bpFile struct {
@@ -85,7 +93,13 @@
 }
 
 func main() {
-	b, err := ioutil.ReadFile(os.Args[1])
+	flag.Usage = usage
+	flag.Parse()
+	if len(flag.Args()) != 1 {
+		usage()
+	}
+	filePathToRead := flag.Arg(0)
+	b, err := ioutil.ReadFile(filePathToRead)
 	if err != nil {
 		fmt.Println(err.Error())
 		return
diff --git a/build_test.bash b/build_test.bash
index 065d7f6..4c43224 100755
--- a/build_test.bash
+++ b/build_test.bash
@@ -28,11 +28,14 @@
 export TRACE_BEGIN_SOONG=$(date +%s%N)
 
 export TOP=$(cd $(dirname ${BASH_SOURCE[0]})/../..; PWD= /bin/pwd)
+cd "${TOP}"
 source "${TOP}/build/soong/scripts/microfactory.bash"
 
 case $(uname) in
   Linux)
     export LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so
+    export SEGFAULT_USE_ALTSTACK=1
+    ulimit -a
     ;;
 esac
 
diff --git a/cc/androidmk.go b/cc/androidmk.go
index eb63065..194faab 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -30,7 +30,7 @@
 type AndroidMkContext interface {
 	Target() android.Target
 	subAndroidMk(*android.AndroidMkData, interface{})
-	vndk() bool
+	useVndk() bool
 }
 
 type subAndroidMkProvider interface {
@@ -64,14 +64,14 @@
 				if len(c.Properties.AndroidMkSharedLibs) > 0 {
 					fmt.Fprintln(w, "LOCAL_SHARED_LIBRARIES := "+strings.Join(c.Properties.AndroidMkSharedLibs, " "))
 				}
-				if c.Target().Os == android.Android && c.Properties.Sdk_version != "" && !c.vndk() {
+				if c.Target().Os == android.Android && c.Properties.Sdk_version != "" && !c.useVndk() {
 					fmt.Fprintln(w, "LOCAL_SDK_VERSION := "+c.Properties.Sdk_version)
 					fmt.Fprintln(w, "LOCAL_NDK_STL_VARIANT := none")
 				} else {
 					// These are already included in LOCAL_SHARED_LIBRARIES
 					fmt.Fprintln(w, "LOCAL_CXX_STL := none")
 				}
-				if c.vndk() {
+				if c.useVndk() {
 					fmt.Fprintln(w, "LOCAL_USE_VNDK := true")
 				}
 			},
@@ -89,7 +89,7 @@
 	}
 	c.subAndroidMk(&ret, c.installer)
 
-	if c.vndk() && Bool(c.VendorProperties.Vendor_available) {
+	if c.useVndk() && c.hasVendorVariant() {
 		// .vendor suffix is added only when we will have two variants: core and vendor.
 		// The suffix is not added for vendor-only module.
 		ret.SubName += vendorSuffix
@@ -161,7 +161,7 @@
 				}
 				fmt.Fprintln(w, "LOCAL_MODULE_HOST_OS :=", makeOs)
 				fmt.Fprintln(w, "LOCAL_IS_HOST_MODULE := true")
-			} else if ctx.vndk() {
+			} else if ctx.useVndk() {
 				fmt.Fprintln(w, "LOCAL_USE_VNDK := true")
 			}
 
diff --git a/cc/binary.go b/cc/binary.go
index b2405b6..30e017f 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -108,7 +108,7 @@
 	deps = binary.baseLinker.linkerDeps(ctx, deps)
 	if ctx.toolchain().Bionic() {
 		if !Bool(binary.baseLinker.Properties.Nocrt) {
-			if !ctx.sdk() {
+			if !ctx.useSdk() {
 				if binary.static() {
 					deps.CrtBegin = "crtbegin_static"
 				} else {
diff --git a/cc/builder.go b/cc/builder.go
index b5bdc3d..742f7fb 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -219,7 +219,7 @@
 	arFlags       string
 	asFlags       string
 	cFlags        string
-	toolingCFlags string // Seperate set of Cflags for clang LibTooling tools
+	toolingCFlags string // A separate set of Cflags for clang LibTooling tools
 	conlyFlags    string
 	cppFlags      string
 	ldFlags       string
@@ -584,7 +584,7 @@
 }
 
 // Generate a rule for compiling multiple .o files, plus static libraries, whole static libraries,
-// and shared libraires, to a shared library (.so) or dynamic executable
+// and shared libraries, to a shared library (.so) or dynamic executable
 func TransformObjToDynamicBinary(ctx android.ModuleContext,
 	objFiles, sharedLibs, staticLibs, lateStaticLibs, wholeStaticLibs, deps android.Paths,
 	crtBegin, crtEnd android.OptionalPath, groupLate bool, flags builderFlags, outputFile android.WritablePath) {
@@ -714,8 +714,8 @@
 	return android.OptionalPathForPath(outputFile)
 }
 
-// Generate a rule for extract a table of contents from a shared library (.so)
-func TransformSharedObjectToToc(ctx android.ModuleContext, inputFile android.WritablePath,
+// Generate a rule for extracting a table of contents from a shared library (.so)
+func TransformSharedObjectToToc(ctx android.ModuleContext, inputFile android.Path,
 	outputFile android.WritablePath, flags builderFlags) {
 
 	crossCompile := gccCmd(flags.toolchain, "")
diff --git a/cc/cc.go b/cc/cc.go
index 4f10a11..2fafaa2 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -34,9 +34,9 @@
 	android.RegisterModuleType("cc_defaults", defaultsFactory)
 
 	android.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
+		ctx.BottomUp("image", vendorMutator).Parallel()
 		ctx.BottomUp("link", linkageMutator).Parallel()
 		ctx.BottomUp("vndk", vndkMutator).Parallel()
-		ctx.BottomUp("image", vendorMutator).Parallel()
 		ctx.BottomUp("ndk_api", ndkApiMutator).Parallel()
 		ctx.BottomUp("test_per_src", testPerSrcMutator).Parallel()
 		ctx.BottomUp("begin", beginMutator).Parallel()
@@ -168,16 +168,21 @@
 }
 
 type VendorProperties struct {
-	// whether this module should be allowed to install onto /vendor as
-	// well as /system. The two variants will be built separately, one
-	// like normal, and the other limited to the set of libraries and
-	// headers that are exposed to /vendor modules.
+	// whether this module should be allowed to be directly depended by other
+	// modules with `vendor: true`, `proprietary: true`, or `vendor_available:true`.
+	// If set to true, two variants will be built separately, one like
+	// normal, and the other limited to the set of libraries and headers
+	// that are exposed to /vendor modules.
 	//
 	// The vendor variant may be used with a different (newer) /system,
 	// so it shouldn't have any unversioned runtime dependencies, or
 	// make assumptions about the system that may not be true in the
 	// future.
 	//
+	// If set to false, this module becomes inaccessible from /vendor modules.
+	//
+	// Default value is true when vndk: {enabled: true} or vendor: true.
+	//
 	// Nothing happens if BOARD_VNDK_VERSION isn't set in the BoardConfig.mk
 	Vendor_available *bool
 }
@@ -192,9 +197,9 @@
 	clang() bool
 	toolchain() config.Toolchain
 	noDefaultCompilerFlags() bool
-	sdk() bool
+	useSdk() bool
 	sdkVersion() string
-	vndk() bool
+	useVndk() bool
 	isVndk() bool
 	isVndkSp() bool
 	createVndkSourceAbiDump() bool
@@ -377,7 +382,7 @@
 	return false
 }
 
-func (c *Module) vndk() bool {
+func (c *Module) useVndk() bool {
 	return c.Properties.UseVndk
 }
 
@@ -388,6 +393,12 @@
 	return false
 }
 
+// Returns true only when this module is configured to have core and vendor
+// variants.
+func (c *Module) hasVendorVariant() bool {
+	return c.isVndk() || Bool(c.VendorProperties.Vendor_available)
+}
+
 type baseModuleContext struct {
 	android.BaseContext
 	moduleContextImpl
@@ -403,10 +414,8 @@
 	moduleContextImpl
 }
 
-// Vendor returns true for vendor modules excluding VNDK libraries so that
-// they get installed onto the correct partition
-func (ctx *moduleContext) Vendor() bool {
-	return ctx.ModuleContext.Vendor() || (ctx.mod.vndk() && !ctx.mod.isVndk())
+func (ctx *moduleContext) InstallOnVendorPartition() bool {
+	return ctx.ModuleContext.InstallOnVendorPartition() || (ctx.mod.useVndk() && !ctx.mod.isVndk())
 }
 
 type moduleContextImpl struct {
@@ -444,8 +453,8 @@
 	return Bool(ctx.mod.Properties.No_default_compiler_flags)
 }
 
-func (ctx *moduleContextImpl) sdk() bool {
-	if ctx.ctx.Device() && !ctx.vndk() {
+func (ctx *moduleContextImpl) useSdk() bool {
+	if ctx.ctx.Device() && !ctx.useVndk() {
 		return ctx.mod.Properties.Sdk_version != ""
 	}
 	return false
@@ -453,7 +462,7 @@
 
 func (ctx *moduleContextImpl) sdkVersion() string {
 	if ctx.ctx.Device() {
-		if ctx.vndk() {
+		if ctx.useVndk() {
 			return "current"
 		} else {
 			return ctx.mod.Properties.Sdk_version
@@ -462,13 +471,12 @@
 	return ""
 }
 
-func (ctx *moduleContextImpl) vndk() bool {
-	return ctx.mod.vndk()
-}
-
 func (ctx *moduleContextImpl) isVndk() bool {
 	return ctx.mod.isVndk()
 }
+func (ctx *moduleContextImpl) useVndk() bool {
+	return ctx.mod.useVndk()
+}
 
 func (ctx *moduleContextImpl) isVndkSp() bool {
 	if vndk := ctx.mod.vndkdep; vndk != nil {
@@ -479,7 +487,7 @@
 
 // Create source abi dumps if the module belongs to the list of VndkLibraries.
 func (ctx *moduleContextImpl) createVndkSourceAbiDump() bool {
-	return ctx.ctx.Device() && ((ctx.vndk() && ctx.isVndk()) || inList(ctx.baseModuleName(), llndkLibraries))
+	return ctx.ctx.Device() && ((ctx.useVndk() && ctx.isVndk()) || inList(ctx.baseModuleName(), llndkLibraries))
 }
 
 func (ctx *moduleContextImpl) selectedStl() string {
@@ -533,6 +541,7 @@
 }
 
 func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) {
+
 	ctx := &moduleContext{
 		ModuleContext: actx,
 		moduleContextImpl: moduleContextImpl{
@@ -658,7 +667,7 @@
 	for _, feature := range c.features {
 		feature.begin(ctx)
 	}
-	if ctx.sdk() {
+	if ctx.useSdk() {
 		version, err := normalizeNdkApiLevel(ctx, ctx.sdkVersion(), ctx.Arch())
 		if err != nil {
 			ctx.PropertyErrorf("sdk_version", err.Error())
@@ -770,27 +779,31 @@
 	if ctx.Os() == android.Android {
 		version := ctx.sdkVersion()
 
-		// Rewrites the names of shared libraries into the names of the NDK
-		// libraries where appropriate. This returns two slices.
+		// rewriteNdkLibs takes a list of names of shared libraries and scans it for three types
+		// of names:
 		//
-		// The first is a list of non-variant shared libraries (either rewritten
-		// NDK libraries to the modules in prebuilts/ndk, or not rewritten
-		// because they are not NDK libraries).
+		// 1. Name of an NDK library that refers to a prebuilt module.
+		//    For each of these, it adds the name of the prebuilt module (which will be in
+		//    prebuilts/ndk) to the list of nonvariant libs.
+		// 2. Name of an NDK library that refers to an ndk_library module.
+		//    For each of these, it adds the name of the ndk_library module to the list of
+		//    variant libs.
+		// 3. Anything else (so anything that isn't an NDK library).
+		//    It adds these to the nonvariantLibs list.
 		//
-		// The second is a list of ndk_library modules. These need to be
-		// separated because they are a variation dependency and must be added
-		// in a different manner.
-		rewriteNdkLibs := func(list []string) ([]string, []string) {
-			variantLibs := []string{}
-			nonvariantLibs := []string{}
+		// The caller can then know to add the variantLibs dependencies differently from the
+		// nonvariantLibs
+		rewriteNdkLibs := func(list []string) (nonvariantLibs []string, variantLibs []string) {
+			variantLibs = []string{}
+			nonvariantLibs = []string{}
 			for _, entry := range list {
-				if ctx.sdk() && inList(entry, ndkPrebuiltSharedLibraries) {
+				if ctx.useSdk() && inList(entry, ndkPrebuiltSharedLibraries) {
 					if !inList(entry, ndkMigratedLibs) {
 						nonvariantLibs = append(nonvariantLibs, entry+".ndk."+version)
 					} else {
 						variantLibs = append(variantLibs, entry+ndkLibrarySuffix)
 					}
-				} else if ctx.vndk() && inList(entry, llndkLibraries) {
+				} else if ctx.useVndk() && inList(entry, llndkLibraries) {
 					nonvariantLibs = append(nonvariantLibs, entry+llndkLibrarySuffix)
 				} else {
 					nonvariantLibs = append(nonvariantLibs, entry)
@@ -892,117 +905,118 @@
 	return clang
 }
 
+// Whether a module can link to another module, taking into
+// account NDK linking.
+func checkLinkType(ctx android.ModuleContext, from *Module, to *Module) {
+	if from.Target().Os != android.Android {
+		// Host code is not restricted
+		return
+	}
+	if from.Properties.UseVndk {
+		// Though vendor code is limited by the vendor mutator,
+		// each vendor-available module needs to check
+		// link-type for VNDK.
+		if from.vndkdep != nil {
+			from.vndkdep.vndkCheckLinkType(ctx, to)
+		}
+		return
+	}
+	if from.Properties.Sdk_version == "" {
+		// Platform code can link to anything
+		return
+	}
+	if _, ok := to.linker.(*toolchainLibraryDecorator); ok {
+		// These are always allowed
+		return
+	}
+	if _, ok := to.linker.(*ndkPrebuiltLibraryLinker); ok {
+		// These are allowed, but they don't set sdk_version
+		return
+	}
+	if _, ok := to.linker.(*ndkPrebuiltStlLinker); ok {
+		// These are allowed, but they don't set sdk_version
+		return
+	}
+	if _, ok := to.linker.(*stubDecorator); ok {
+		// These aren't real libraries, but are the stub shared libraries that are included in
+		// the NDK.
+		return
+	}
+	if to.Properties.Sdk_version == "" {
+		// NDK code linking to platform code is never okay.
+		ctx.ModuleErrorf("depends on non-NDK-built library %q",
+			ctx.OtherModuleName(to))
+	}
+
+	// At this point we know we have two NDK libraries, but we need to
+	// check that we're not linking against anything built against a higher
+	// API level, as it is only valid to link against older or equivalent
+	// APIs.
+
+	if from.Properties.Sdk_version == "current" {
+		// Current can link against anything.
+		return
+	} else if to.Properties.Sdk_version == "current" {
+		// Current can't be linked against by anything else.
+		ctx.ModuleErrorf("links %q built against newer API version %q",
+			ctx.OtherModuleName(to), "current")
+	}
+
+	fromApi, err := strconv.Atoi(from.Properties.Sdk_version)
+	if err != nil {
+		ctx.PropertyErrorf("sdk_version",
+			"Invalid sdk_version value (must be int): %q",
+			from.Properties.Sdk_version)
+	}
+	toApi, err := strconv.Atoi(to.Properties.Sdk_version)
+	if err != nil {
+		ctx.PropertyErrorf("sdk_version",
+			"Invalid sdk_version value (must be int): %q",
+			to.Properties.Sdk_version)
+	}
+
+	if toApi > fromApi {
+		ctx.ModuleErrorf("links %q built against newer API version %q",
+			ctx.OtherModuleName(to), to.Properties.Sdk_version)
+	}
+}
+
 // Convert dependencies to paths.  Returns a PathDeps containing paths
 func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
 	var depPaths PathDeps
 
-	// Whether a module can link to another module, taking into
-	// account NDK linking.
-	checkLinkType := func(from, to *Module) {
-		if from.Target().Os != android.Android {
-			// Host code is not restricted
-			return
-		}
-		if from.Properties.UseVndk {
-			// Though vendor code is limited by the vendor mutator,
-			// each vendor-available module needs to check
-			// link-type for VNDK.
-			if from.vndkdep != nil {
-				from.vndkdep.vndkCheckLinkType(ctx, to)
-			}
-			return
-		}
-		if from.Properties.Sdk_version == "" {
-			// Platform code can link to anything
-			return
-		}
-		if _, ok := to.linker.(*toolchainLibraryDecorator); ok {
-			// These are always allowed
-			return
-		}
-		if _, ok := to.linker.(*ndkPrebuiltLibraryLinker); ok {
-			// These are allowed, but don't set sdk_version
-			return
-		}
-		if _, ok := to.linker.(*ndkPrebuiltStlLinker); ok {
-			// These are allowed, but don't set sdk_version
-			return
-		}
-		if _, ok := to.linker.(*stubDecorator); ok {
-			// These aren't real libraries, but are the stub shared libraries that are included in
-			// the NDK.
-			return
-		}
-		if to.Properties.Sdk_version == "" {
-			// NDK code linking to platform code is never okay.
-			ctx.ModuleErrorf("depends on non-NDK-built library %q",
-				ctx.OtherModuleName(to))
-		}
+	ctx.VisitDirectDeps(func(dep blueprint.Module) {
+		depName := ctx.OtherModuleName(dep)
+		depTag := ctx.OtherModuleDependencyTag(dep)
 
-		// All this point we know we have two NDK libraries, but we need to
-		// check that we're not linking against anything built against a higher
-		// API level, as it is only valid to link against older or equivalent
-		// APIs.
-
-		if from.Properties.Sdk_version == "current" {
-			// Current can link against anything.
-			return
-		} else if to.Properties.Sdk_version == "current" {
-			// Current can't be linked against by anything else.
-			ctx.ModuleErrorf("links %q built against newer API version %q",
-				ctx.OtherModuleName(to), "current")
-		}
-
-		fromApi, err := strconv.Atoi(from.Properties.Sdk_version)
-		if err != nil {
-			ctx.PropertyErrorf("sdk_version",
-				"Invalid sdk_version value (must be int): %q",
-				from.Properties.Sdk_version)
-		}
-		toApi, err := strconv.Atoi(to.Properties.Sdk_version)
-		if err != nil {
-			ctx.PropertyErrorf("sdk_version",
-				"Invalid sdk_version value (must be int): %q",
-				to.Properties.Sdk_version)
-		}
-
-		if toApi > fromApi {
-			ctx.ModuleErrorf("links %q built against newer API version %q",
-				ctx.OtherModuleName(to), to.Properties.Sdk_version)
-		}
-	}
-
-	ctx.VisitDirectDeps(func(m blueprint.Module) {
-		name := ctx.OtherModuleName(m)
-		tag := ctx.OtherModuleDependencyTag(m)
-
-		a, _ := m.(android.Module)
-		if a == nil {
-			ctx.ModuleErrorf("module %q not an android module", name)
+		aDep, _ := dep.(android.Module)
+		if aDep == nil {
+			ctx.ModuleErrorf("module %q not an android module", depName)
 			return
 		}
 
-		cc, _ := m.(*Module)
-		if cc == nil {
-			switch tag {
+		ccDep, _ := dep.(*Module)
+		if ccDep == nil {
+			// handling for a few module types that aren't cc Module but that are also supported
+			switch depTag {
 			case android.DefaultsDepTag, android.SourceDepTag:
 				// Nothing to do
 			case genSourceDepTag:
-				if genRule, ok := m.(genrule.SourceFileGenerator); ok {
+				if genRule, ok := dep.(genrule.SourceFileGenerator); ok {
 					depPaths.GeneratedSources = append(depPaths.GeneratedSources,
 						genRule.GeneratedSourceFiles()...)
 				} else {
-					ctx.ModuleErrorf("module %q is not a gensrcs or genrule", name)
+					ctx.ModuleErrorf("module %q is not a gensrcs or genrule", depName)
 				}
 				// Support exported headers from a generated_sources dependency
 				fallthrough
 			case genHeaderDepTag, genHeaderExportDepTag:
-				if genRule, ok := m.(genrule.SourceFileGenerator); ok {
+				if genRule, ok := dep.(genrule.SourceFileGenerator); ok {
 					depPaths.GeneratedHeaders = append(depPaths.GeneratedHeaders,
 						genRule.GeneratedSourceFiles()...)
 					flags := includeDirsToFlags(genRule.GeneratedHeaderDirs())
 					depPaths.Flags = append(depPaths.Flags, flags)
-					if tag == genHeaderExportDepTag {
+					if depTag == genHeaderExportDepTag {
 						depPaths.ReexportedFlags = append(depPaths.ReexportedFlags, flags)
 						depPaths.ReexportedFlagsDeps = append(depPaths.ReexportedFlagsDeps,
 							genRule.GeneratedSourceFiles()...)
@@ -1011,46 +1025,46 @@
 
 					}
 				} else {
-					ctx.ModuleErrorf("module %q is not a genrule", name)
+					ctx.ModuleErrorf("module %q is not a genrule", depName)
 				}
 			case linkerScriptDepTag:
-				if genRule, ok := m.(genrule.SourceFileGenerator); ok {
+				if genRule, ok := dep.(genrule.SourceFileGenerator); ok {
 					files := genRule.GeneratedSourceFiles()
 					if len(files) == 1 {
 						depPaths.LinkerScript = android.OptionalPathForPath(files[0])
 					} else if len(files) > 1 {
-						ctx.ModuleErrorf("module %q can only generate a single file if used for a linker script", name)
+						ctx.ModuleErrorf("module %q can only generate a single file if used for a linker script", depName)
 					}
 				} else {
-					ctx.ModuleErrorf("module %q is not a genrule", name)
+					ctx.ModuleErrorf("module %q is not a genrule", depName)
 				}
 			default:
-				ctx.ModuleErrorf("depends on non-cc module %q", name)
+				ctx.ModuleErrorf("depends on non-cc module %q", depName)
 			}
 			return
 		}
 
-		if !a.Enabled() {
+		// some validation
+		if !aDep.Enabled() {
 			if ctx.AConfig().AllowMissingDependencies() {
-				ctx.AddMissingDependencies([]string{name})
+				ctx.AddMissingDependencies([]string{depName})
 			} else {
-				ctx.ModuleErrorf("depends on disabled module %q", name)
+				ctx.ModuleErrorf("depends on disabled module %q", depName)
 			}
 			return
 		}
-
-		if a.Target().Os != ctx.Os() {
-			ctx.ModuleErrorf("OS mismatch between %q and %q", ctx.ModuleName(), name)
+		if aDep.Target().Os != ctx.Os() {
+			ctx.ModuleErrorf("OS mismatch between %q and %q", ctx.ModuleName(), depName)
+			return
+		}
+		if aDep.Target().Arch.ArchType != ctx.Arch().ArchType {
+			ctx.ModuleErrorf("Arch mismatch between %q and %q", ctx.ModuleName(), depName)
 			return
 		}
 
-		if a.Target().Arch.ArchType != ctx.Arch().ArchType {
-			ctx.ModuleErrorf("Arch mismatch between %q and %q", ctx.ModuleName(), name)
-			return
-		}
-
-		if tag == reuseObjTag {
-			if l, ok := cc.compiler.(libraryInterface); ok {
+		// re-exporting flags
+		if depTag == reuseObjTag {
+			if l, ok := ccDep.compiler.(libraryInterface); ok {
 				objs, flags, deps := l.reuseObjs()
 				depPaths.Objs = depPaths.Objs.Append(objs)
 				depPaths.ReexportedFlags = append(depPaths.ReexportedFlags, flags...)
@@ -1058,9 +1072,8 @@
 				return
 			}
 		}
-
-		if t, ok := tag.(dependencyTag); ok && t.library {
-			if i, ok := cc.linker.(exportedFlagsProducer); ok {
+		if t, ok := depTag.(dependencyTag); ok && t.library {
+			if i, ok := ccDep.linker.(exportedFlagsProducer); ok {
 				flags := i.exportedFlags()
 				deps := i.exportedFlagsDeps()
 				depPaths.Flags = append(depPaths.Flags, flags...)
@@ -1076,38 +1089,38 @@
 				}
 			}
 
-			checkLinkType(c, cc)
+			checkLinkType(ctx, c, ccDep)
 		}
 
 		var ptr *android.Paths
 		var depPtr *android.Paths
 
-		linkFile := cc.outputFile
+		linkFile := ccDep.outputFile
 		depFile := android.OptionalPath{}
 
-		switch tag {
+		switch depTag {
 		case ndkStubDepTag, sharedDepTag, sharedExportDepTag:
 			ptr = &depPaths.SharedLibs
 			depPtr = &depPaths.SharedLibsDeps
-			depFile = cc.linker.(libraryInterface).toc()
+			depFile = ccDep.linker.(libraryInterface).toc()
 		case lateSharedDepTag, ndkLateStubDepTag:
 			ptr = &depPaths.LateSharedLibs
 			depPtr = &depPaths.LateSharedLibsDeps
-			depFile = cc.linker.(libraryInterface).toc()
+			depFile = ccDep.linker.(libraryInterface).toc()
 		case staticDepTag, staticExportDepTag:
 			ptr = &depPaths.StaticLibs
 		case lateStaticDepTag:
 			ptr = &depPaths.LateStaticLibs
 		case wholeStaticDepTag:
 			ptr = &depPaths.WholeStaticLibs
-			staticLib, ok := cc.linker.(libraryInterface)
+			staticLib, ok := ccDep.linker.(libraryInterface)
 			if !ok || !staticLib.static() {
-				ctx.ModuleErrorf("module %q not a static library", name)
+				ctx.ModuleErrorf("module %q not a static library", depName)
 				return
 			}
 
 			if missingDeps := staticLib.getWholeStaticMissingDeps(); missingDeps != nil {
-				postfix := " (required by " + ctx.OtherModuleName(m) + ")"
+				postfix := " (required by " + ctx.OtherModuleName(dep) + ")"
 				for i := range missingDeps {
 					missingDeps[i] += postfix
 				}
@@ -1124,11 +1137,11 @@
 			depPaths.CrtEnd = linkFile
 		}
 
-		switch tag {
+		switch depTag {
 		case staticDepTag, staticExportDepTag, lateStaticDepTag:
-			staticLib, ok := cc.linker.(libraryInterface)
+			staticLib, ok := ccDep.linker.(libraryInterface)
 			if !ok || !staticLib.static() {
-				ctx.ModuleErrorf("module %q not a static library", name)
+				ctx.ModuleErrorf("module %q not a static library", depName)
 				return
 			}
 
@@ -1139,11 +1152,12 @@
 				staticLib.objs().coverageFiles...)
 			depPaths.StaticLibObjs.sAbiDumpFiles = append(depPaths.StaticLibObjs.sAbiDumpFiles,
 				staticLib.objs().sAbiDumpFiles...)
+
 		}
 
 		if ptr != nil {
 			if !linkFile.Valid() {
-				ctx.ModuleErrorf("module %q missing output file", name)
+				ctx.ModuleErrorf("module %q missing output file", depName)
 				return
 			}
 			*ptr = append(*ptr, linkFile.Path())
@@ -1157,24 +1171,24 @@
 			*depPtr = append(*depPtr, dep.Path())
 		}
 
-		// Export the shared libs to the make world. In doing so, .vendor suffix
-		// is added if the lib has both core and vendor variants and this module
-		// is building against vndk. This is because the vendor variant will be
-		// have .vendor suffix in its name in the make world. However, if the
-		// lib is a vendor-only lib or this lib is not building against vndk,
-		// then the suffix is not added.
-		switch tag {
+		// Export the shared libs to Make.
+		switch depTag {
 		case sharedDepTag, sharedExportDepTag, lateSharedDepTag:
-			libName := strings.TrimSuffix(name, llndkLibrarySuffix)
+			libName := strings.TrimSuffix(depName, llndkLibrarySuffix)
 			libName = strings.TrimPrefix(libName, "prebuilt_")
 			isLLndk := inList(libName, llndkLibraries)
-			if c.vndk() && (Bool(cc.VendorProperties.Vendor_available) || isLLndk) {
-				libName += vendorSuffix
+			var makeLibName string
+			bothVendorAndCoreVariantsExist := ccDep.hasVendorVariant() || isLLndk
+			if c.useVndk() && bothVendorAndCoreVariantsExist {
+				// The vendor module in Make will have been renamed to not conflict with the core
+				// module, so update the dependency name here accordingly.
+				makeLibName = libName + vendorSuffix
+			} else {
+				makeLibName = libName
 			}
 			// Note: the order of libs in this list is not important because
-			// they merely serve as dependencies in the make world and do not
-			// affect this lib itself.
-			c.Properties.AndroidMkSharedLibs = append(c.Properties.AndroidMkSharedLibs, libName)
+			// they merely serve as Make dependencies and do not affect this lib itself.
+			c.Properties.AndroidMkSharedLibs = append(c.Properties.AndroidMkSharedLibs, makeLibName)
 		}
 	})
 
@@ -1286,6 +1300,16 @@
 	vendorMode = "vendor"
 )
 
+func squashVendorSrcs(m *Module) {
+	if lib, ok := m.compiler.(*libraryDecorator); ok {
+		lib.baseCompiler.Properties.Srcs = append(lib.baseCompiler.Properties.Srcs,
+			lib.baseCompiler.Properties.Target.Vendor.Srcs...)
+
+		lib.baseCompiler.Properties.Exclude_srcs = append(lib.baseCompiler.Properties.Exclude_srcs,
+			lib.baseCompiler.Properties.Target.Vendor.Exclude_srcs...)
+	}
+}
+
 func vendorMutator(mctx android.BottomUpMutatorContext) {
 	if mctx.Os() != android.Android {
 		return
@@ -1297,7 +1321,7 @@
 				mctx.CreateVariations(coreMode)
 			} else if Bool(props.Vendor_available) {
 				mctx.CreateVariations(coreMode, vendorMode)
-			} else if mctx.Vendor() {
+			} else if mctx.InstallOnVendorPartition() {
 				mctx.CreateVariations(vendorMode)
 			} else {
 				mctx.CreateVariations(coreMode)
@@ -1311,15 +1335,15 @@
 	}
 
 	// Sanity check
-	if Bool(m.VendorProperties.Vendor_available) && mctx.Vendor() {
+	if m.VendorProperties.Vendor_available != nil && mctx.InstallOnVendorPartition() {
 		mctx.PropertyErrorf("vendor_available",
 			"doesn't make sense at the same time as `vendor: true` or `proprietary: true`")
 		return
 	}
 	if vndk := m.vndkdep; vndk != nil {
-		if vndk.isVndk() && !Bool(m.VendorProperties.Vendor_available) {
+		if vndk.isVndk() && m.VendorProperties.Vendor_available == nil {
 			mctx.PropertyErrorf("vndk",
-				"has to define `vendor_available: true` to enable vndk")
+				"vendor_available must be set to either true or false when `vndk: {enabled: true}`")
 			return
 		}
 		if !vndk.isVndk() && vndk.isVndkSp() {
@@ -1337,15 +1361,19 @@
 		// LL-NDK stubs only exist in the vendor variant, since the
 		// real libraries will be used in the core variant.
 		mctx.CreateVariations(vendorMode)
-	} else if Bool(m.VendorProperties.Vendor_available) {
+	} else if m.hasVendorVariant() {
 		// This will be available in both /system and /vendor
 		// or a /system directory that is available to vendor.
 		mod := mctx.CreateVariations(coreMode, vendorMode)
-		mod[1].(*Module).Properties.UseVndk = true
-	} else if mctx.Vendor() && m.Properties.Sdk_version == "" {
+		vendor := mod[1].(*Module)
+		vendor.Properties.UseVndk = true
+		squashVendorSrcs(vendor)
+	} else if mctx.InstallOnVendorPartition() && m.Properties.Sdk_version == "" {
 		// This will be available in /vendor only
 		mod := mctx.CreateVariations(vendorMode)
-		mod[0].(*Module).Properties.UseVndk = true
+		vendor := mod[0].(*Module)
+		vendor.Properties.UseVndk = true
+		squashVendorSrcs(vendor)
 	} else {
 		// This is either in /system (or similar: /data), or is a
 		// modules built with the NDK. Modules built with the NDK
@@ -1371,8 +1399,8 @@
 	return list[:k]
 }
 
-// lastUniqueElements returns all unique elements of a slice, keeping the last copy of each
-// modifies the slice contents in place, and returns a subslice of the original slice
+// lastUniqueElements returns all unique elements of a slice, keeping the last copy of each.
+// It modifies the slice contents in place, and returns a subslice of the original slice
 func lastUniqueElements(list []string) []string {
 	totalSkip := 0
 	for i := len(list) - 1; i >= totalSkip; i-- {
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 92120a5..94e3e66 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -2,10 +2,136 @@
 
 import (
 	"android/soong/android"
+	"io/ioutil"
+	"os"
 	"reflect"
 	"testing"
+
+	"github.com/google/blueprint/proptools"
 )
 
+var buildDir string
+
+func setUp() {
+	var err error
+	buildDir, err = ioutil.TempDir("", "soong_cc_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 testCc(t *testing.T, bp string) *android.TestContext {
+	config := android.TestArchConfig(buildDir, nil)
+	config.ProductVariables.DeviceVndkVersion = proptools.StringPtr("current")
+
+	ctx := android.NewTestArchContext()
+	ctx.RegisterModuleType("cc_library", android.ModuleFactoryAdaptor(libraryFactory))
+	ctx.RegisterModuleType("toolchain_library", android.ModuleFactoryAdaptor(toolchainLibraryFactory))
+	ctx.RegisterModuleType("llndk_library", android.ModuleFactoryAdaptor(llndkLibraryFactory))
+	ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
+		ctx.BottomUp("image", vendorMutator).Parallel()
+		ctx.BottomUp("link", linkageMutator).Parallel()
+		ctx.BottomUp("vndk", vndkMutator).Parallel()
+	})
+	ctx.Register()
+
+	ctx.MockFileSystem(map[string][]byte{
+		"Android.bp": []byte(bp),
+		"foo.c":      nil,
+		"bar.c":      nil,
+	})
+
+	_, errs := ctx.ParseBlueprintsFiles("Android.bp")
+	fail(t, errs)
+	_, errs = ctx.PrepareBuildActions(config)
+	fail(t, errs)
+
+	return ctx
+}
+
+func TestVendorSrc(t *testing.T) {
+	ctx := testCc(t, `
+		cc_library {
+			name: "libTest",
+			srcs: ["foo.c"],
+			no_libgcc : true,
+			nocrt : true,
+			system_shared_libs : [],
+			vendor_available: true,
+			target: {
+				vendor: {
+					srcs: ["bar.c"],
+				},
+			},
+		}
+		toolchain_library {
+			name: "libatomic",
+			vendor_available: true,
+		}
+		toolchain_library {
+			name: "libcompiler_rt-extras",
+			vendor_available: true,
+		}
+		cc_library {
+			name: "libc",
+			no_libgcc : true,
+			nocrt : true,
+			system_shared_libs: [],
+		}
+		llndk_library {
+			name: "libc",
+			symbol_file: "",
+		}
+		cc_library {
+			name: "libm",
+			no_libgcc : true,
+			nocrt : true,
+			system_shared_libs: [],
+		}
+		llndk_library {
+			name: "libm",
+			symbol_file: "",
+		}
+		cc_library {
+			name: "libdl",
+			no_libgcc : true,
+			nocrt : true,
+			system_shared_libs: [],
+		}
+		llndk_library {
+			name: "libdl",
+			symbol_file: "",
+		}
+	`)
+
+	ld := ctx.ModuleForTests("libTest", "android_arm_armv7-a-neon_vendor_shared").Rule("ld")
+	var objs []string
+	for _, o := range ld.Inputs {
+		objs = append(objs, o.Base())
+	}
+	if len(objs) != 2 {
+		t.Errorf("inputs of libTest is expected to 2, but was %d.", len(objs))
+	}
+	if objs[0] != "foo.o" || objs[1] != "bar.o" {
+		t.Errorf("inputs of libTest must be []string{\"foo.o\", \"bar.o\"}, but was %#v.", objs)
+	}
+}
+
 var firstUniqueElementsTestCases = []struct {
 	in  []string
 	out []string
diff --git a/cc/compiler.go b/cc/compiler.go
index a65ddf8..102bc7a 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -236,7 +236,7 @@
 		flags.GlobalFlags = append(flags.GlobalFlags, "-I"+android.PathForModuleSrc(ctx).String())
 		flags.YasmFlags = append(flags.YasmFlags, "-I"+android.PathForModuleSrc(ctx).String())
 
-		if !(ctx.sdk() || ctx.vndk()) || ctx.Host() {
+		if !(ctx.useSdk() || ctx.useVndk()) || ctx.Host() {
 			flags.SystemIncludeFlags = append(flags.SystemIncludeFlags,
 				"${config.CommonGlobalIncludes}",
 				tc.IncludeFlags(),
@@ -244,7 +244,7 @@
 		}
 	}
 
-	if ctx.sdk() {
+	if ctx.useSdk() {
 		// The NDK headers are installed to a common sysroot. While a more
 		// typical Soong approach would be to only make the headers for the
 		// library you're using available, we're trying to emulate the NDK
@@ -272,7 +272,7 @@
 		flags.SystemIncludeFlags = append(flags.SystemIncludeFlags, "-isystem "+legacyIncludes)
 	}
 
-	if ctx.vndk() {
+	if ctx.useVndk() {
 		flags.GlobalFlags = append(flags.GlobalFlags,
 			"-D__ANDROID_API__=__ANDROID_API_FUTURE__", "-D__ANDROID_VNDK__")
 	}
@@ -366,7 +366,7 @@
 		flags.GlobalFlags = append(flags.GlobalFlags, tc.ToolchainCflags())
 	}
 
-	if !ctx.sdk() {
+	if !ctx.useSdk() {
 		cStd := config.CStdVersion
 		if compiler.Properties.C_std == "experimental" {
 			cStd = config.ExperimentalCStdVersion
@@ -405,7 +405,7 @@
 		flags.CppFlags = append([]string{"-std=" + cppStd}, flags.CppFlags...)
 	}
 
-	if ctx.vndk() {
+	if ctx.useVndk() {
 		flags.CFlags = append(flags.CFlags, esc(compiler.Properties.Target.Vendor.Cflags)...)
 	}
 
@@ -478,7 +478,7 @@
 var gnuToCReplacer = strings.NewReplacer("gnu", "c")
 
 func ndkPathDeps(ctx ModuleContext) android.Paths {
-	if ctx.sdk() {
+	if ctx.useSdk() {
 		// The NDK sysroot timestamp file depends on all the NDK sysroot files
 		// (headers and libraries).
 		return android.Paths{getNdkSysrootTimestampFile(ctx)}
@@ -490,14 +490,6 @@
 	pathDeps := deps.GeneratedHeaders
 	pathDeps = append(pathDeps, ndkPathDeps(ctx)...)
 
-	if ctx.vndk() {
-		compiler.Properties.Srcs = append(compiler.Properties.Srcs,
-			compiler.Properties.Target.Vendor.Srcs...)
-
-		compiler.Properties.Exclude_srcs = append(compiler.Properties.Exclude_srcs,
-			compiler.Properties.Target.Vendor.Exclude_srcs...)
-	}
-
 	srcs := ctx.ExpandSources(compiler.Properties.Srcs, compiler.Properties.Exclude_srcs)
 	srcs = append(srcs, deps.GeneratedSources...)
 
diff --git a/cc/config/arm64_device.go b/cc/config/arm64_device.go
index 0d22ed4..390936a 100644
--- a/cc/config/arm64_device.go
+++ b/cc/config/arm64_device.go
@@ -117,7 +117,7 @@
 	pctx.StaticVariable("Arm64Cflags", strings.Join(arm64Cflags, " "))
 	pctx.StaticVariable("Arm64Ldflags", strings.Join(arm64Ldflags, " "))
 	pctx.StaticVariable("Arm64Cppflags", strings.Join(arm64Cppflags, " "))
-	pctx.StaticVariable("Arm64IncludeFlags", bionicHeaders("arm64", "arm64"))
+	pctx.StaticVariable("Arm64IncludeFlags", bionicHeaders("arm64"))
 
 	pctx.StaticVariable("Arm64ClangCflags", strings.Join(ClangFilterUnknownCflags(arm64Cflags), " "))
 	pctx.StaticVariable("Arm64ClangLdflags", strings.Join(ClangFilterUnknownCflags(arm64Ldflags), " "))
diff --git a/cc/config/arm_device.go b/cc/config/arm_device.go
index f093563..7110ccb 100644
--- a/cc/config/arm_device.go
+++ b/cc/config/arm_device.go
@@ -216,7 +216,7 @@
 	pctx.StaticVariable("ArmCflags", strings.Join(armCflags, " "))
 	pctx.StaticVariable("ArmLdflags", strings.Join(armLdflags, " "))
 	pctx.StaticVariable("ArmCppflags", strings.Join(armCppflags, " "))
-	pctx.StaticVariable("ArmIncludeFlags", bionicHeaders("arm", "arm"))
+	pctx.StaticVariable("ArmIncludeFlags", bionicHeaders("arm"))
 
 	// Extended cflags
 
diff --git a/cc/config/global.go b/cc/config/global.go
index 82a44e6..e9f5473 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -180,9 +180,8 @@
 
 var HostPrebuiltTag = pctx.VariableConfigMethod("HostPrebuiltTag", android.Config.PrebuiltOS)
 
-func bionicHeaders(bionicArch, kernelArch string) string {
+func bionicHeaders(kernelArch string) string {
 	return strings.Join([]string{
-		"-isystem bionic/libc/arch-" + bionicArch + "/include",
 		"-isystem bionic/libc/include",
 		"-isystem bionic/libc/kernel/uapi",
 		"-isystem bionic/libc/kernel/uapi/asm-" + kernelArch,
diff --git a/cc/config/mips64_device.go b/cc/config/mips64_device.go
index 3a49e7b..e05dbf1 100644
--- a/cc/config/mips64_device.go
+++ b/cc/config/mips64_device.go
@@ -28,8 +28,6 @@
 		"-fomit-frame-pointer",
 		"-fno-strict-aliasing",
 		"-funswitch-loops",
-		"-U__unix",
-		"-U__unix__",
 		"-Umips",
 		"-ffunction-sections",
 		"-fdata-sections",
@@ -108,7 +106,7 @@
 	pctx.StaticVariable("Mips64Cflags", strings.Join(mips64Cflags, " "))
 	pctx.StaticVariable("Mips64Ldflags", strings.Join(mips64Ldflags, " "))
 	pctx.StaticVariable("Mips64Cppflags", strings.Join(mips64Cppflags, " "))
-	pctx.StaticVariable("Mips64IncludeFlags", bionicHeaders("mips64", "mips"))
+	pctx.StaticVariable("Mips64IncludeFlags", bionicHeaders("mips"))
 
 	// Clang cflags
 	pctx.StaticVariable("Mips64ClangCflags", strings.Join(ClangFilterUnknownCflags(mips64ClangCflags), " "))
diff --git a/cc/config/mips_device.go b/cc/config/mips_device.go
index c135029..78e95b6 100644
--- a/cc/config/mips_device.go
+++ b/cc/config/mips_device.go
@@ -28,8 +28,6 @@
 		"-fomit-frame-pointer",
 		"-fno-strict-aliasing",
 		"-funswitch-loops",
-		"-U__unix",
-		"-U__unix__",
 		"-Umips",
 		"-ffunction-sections",
 		"-fdata-sections",
@@ -147,7 +145,7 @@
 	pctx.StaticVariable("MipsCflags", strings.Join(mipsCflags, " "))
 	pctx.StaticVariable("MipsLdflags", strings.Join(mipsLdflags, " "))
 	pctx.StaticVariable("MipsCppflags", strings.Join(mipsCppflags, " "))
-	pctx.StaticVariable("MipsIncludeFlags", bionicHeaders("mips", "mips"))
+	pctx.StaticVariable("MipsIncludeFlags", bionicHeaders("mips"))
 
 	// Clang cflags
 	pctx.StaticVariable("MipsClangCflags", strings.Join(ClangFilterUnknownCflags(mipsClangCflags), " "))
diff --git a/cc/config/x86_64_device.go b/cc/config/x86_64_device.go
index 2a6fe2a..a98001e 100644
--- a/cc/config/x86_64_device.go
+++ b/cc/config/x86_64_device.go
@@ -159,7 +159,7 @@
 	pctx.StaticVariable("X86_64Cflags", strings.Join(x86_64Cflags, " "))
 	pctx.StaticVariable("X86_64Ldflags", strings.Join(x86_64Ldflags, " "))
 	pctx.StaticVariable("X86_64Cppflags", strings.Join(x86_64Cppflags, " "))
-	pctx.StaticVariable("X86_64IncludeFlags", bionicHeaders("x86_64", "x86"))
+	pctx.StaticVariable("X86_64IncludeFlags", bionicHeaders("x86"))
 
 	// Clang cflags
 	pctx.StaticVariable("X86_64ClangCflags", strings.Join(ClangFilterUnknownCflags(x86_64Cflags), " "))
diff --git a/cc/config/x86_darwin_host.go b/cc/config/x86_darwin_host.go
index 3f079a4..6d361b5 100644
--- a/cc/config/x86_darwin_host.go
+++ b/cc/config/x86_darwin_host.go
@@ -159,6 +159,8 @@
 		strings.Join(ClangFilterUnknownCflags(darwinX8664Cflags), " "))
 	pctx.StaticVariable("DarwinX86ClangLdflags", strings.Join(darwinX86ClangLdflags, " "))
 	pctx.StaticVariable("DarwinX8664ClangLdflags", strings.Join(darwinX8664ClangLdflags, " "))
+	pctx.StaticVariable("DarwinX86YasmFlags", "-f macho -m x86")
+	pctx.StaticVariable("DarwinX8664YasmFlags", "-f macho -m amd64")
 }
 
 func xcrun(config android.Config, args ...string) (string, error) {
@@ -276,6 +278,14 @@
 	return "${config.DarwinClangLdflags} ${config.DarwinX8664ClangLdflags}"
 }
 
+func (t *toolchainDarwinX86) YasmFlags() string {
+	return "${config.DarwinX86YasmFlags}"
+}
+
+func (t *toolchainDarwinX8664) YasmFlags() string {
+	return "${config.DarwinX8664YasmFlags}"
+}
+
 func (t *toolchainDarwin) ShlibSuffix() string {
 	return ".dylib"
 }
diff --git a/cc/config/x86_device.go b/cc/config/x86_device.go
index 23518b6..53d2265 100644
--- a/cc/config/x86_device.go
+++ b/cc/config/x86_device.go
@@ -181,7 +181,7 @@
 	pctx.StaticVariable("X86Cflags", strings.Join(x86Cflags, " "))
 	pctx.StaticVariable("X86Ldflags", strings.Join(x86Ldflags, " "))
 	pctx.StaticVariable("X86Cppflags", strings.Join(x86Cppflags, " "))
-	pctx.StaticVariable("X86IncludeFlags", bionicHeaders("x86", "x86"))
+	pctx.StaticVariable("X86IncludeFlags", bionicHeaders("x86"))
 
 	// Clang cflags
 	pctx.StaticVariable("X86ClangCflags", strings.Join(ClangFilterUnknownCflags(x86ClangCflags), " "))
diff --git a/cc/config/x86_linux_bionic_host.go b/cc/config/x86_linux_bionic_host.go
index 093876d..277361b 100644
--- a/cc/config/x86_linux_bionic_host.go
+++ b/cc/config/x86_linux_bionic_host.go
@@ -78,7 +78,7 @@
 	pctx.StaticVariable("LinuxBionicCflags", strings.Join(linuxBionicCflags, " "))
 	pctx.StaticVariable("LinuxBionicLdflags", strings.Join(linuxBionicLdflags, " "))
 
-	pctx.StaticVariable("LinuxBionicIncludeFlags", bionicHeaders("x86_64", "x86"))
+	pctx.StaticVariable("LinuxBionicIncludeFlags", bionicHeaders("x86"))
 
 	// Use the device gcc toolchain for now
 	pctx.StaticVariable("LinuxBionicGccRoot", "${X86_64GccRoot}")
diff --git a/cc/config/x86_linux_host.go b/cc/config/x86_linux_host.go
index 75416bd..88bd514 100644
--- a/cc/config/x86_linux_host.go
+++ b/cc/config/x86_linux_host.go
@@ -159,6 +159,9 @@
 	pctx.StaticVariable("LinuxX8664ClangLdflags", strings.Join(linuxX8664ClangLdflags, " "))
 	pctx.StaticVariable("LinuxX86ClangCppflags", strings.Join(linuxX86ClangCppflags, " "))
 	pctx.StaticVariable("LinuxX8664ClangCppflags", strings.Join(linuxX8664ClangCppflags, " "))
+	// Yasm flags
+	pctx.StaticVariable("LinuxX86YasmFlags", "-f elf32 -m x86")
+	pctx.StaticVariable("LinuxX8664YasmFlags", "-f elf64 -m amd64")
 }
 
 type toolchainLinux struct {
@@ -251,6 +254,14 @@
 	return "${config.LinuxClangLdflags} ${config.LinuxX8664ClangLdflags}"
 }
 
+func (t *toolchainLinuxX86) YasmFlags() string {
+	return "${config.LinuxX86YasmFlags}"
+}
+
+func (t *toolchainLinuxX8664) YasmFlags() string {
+	return "${config.LinuxX8664YasmFlags}"
+}
+
 func (t *toolchainLinux) AvailableLibraries() []string {
 	return linuxAvailableLibraries
 }
diff --git a/cc/installer.go b/cc/installer.go
index 027d191..92076e5 100644
--- a/cc/installer.go
+++ b/cc/installer.go
@@ -69,7 +69,7 @@
 	if !ctx.Host() && !ctx.Arch().Native {
 		dir = filepath.Join(dir, ctx.Arch().ArchType.String())
 	}
-	if installer.location == InstallInData && ctx.vndk() {
+	if installer.location == InstallInData && ctx.useVndk() {
 		dir = filepath.Join(dir, "vendor")
 	}
 	return android.PathForModuleInstall(ctx, dir, installer.subDir, installer.Properties.Relative_install_path, installer.relative)
diff --git a/cc/library.go b/cc/library.go
index f1681db..e963ecb 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -82,7 +82,8 @@
 type FlagExporterProperties struct {
 	// list of directories relative to the Blueprints file that will
 	// be added to the include path (using -I) for this module and any module that links
-	// against this module
+	// against this module.  Directories listed in export_include_dirs do not need to be
+	// listed in local_include_dirs.
 	Export_include_dirs []string `android:"arch_variant"`
 
 	Target struct {
@@ -155,7 +156,7 @@
 }
 
 func (f *flagExporter) exportedIncludes(ctx ModuleContext) android.Paths {
-	if ctx.vndk() && f.Properties.Target.Vendor.Export_include_dirs != nil {
+	if ctx.useVndk() && f.Properties.Target.Vendor.Export_include_dirs != nil {
 		return android.PathsForModuleSrc(ctx, f.Properties.Target.Vendor.Export_include_dirs)
 	} else {
 		return android.PathsForModuleSrc(ctx, f.Properties.Export_include_dirs)
@@ -434,7 +435,7 @@
 		deps.SharedLibs = append(deps.SharedLibs, library.Properties.Static.Shared_libs...)
 	} else if library.shared() {
 		if ctx.toolchain().Bionic() && !Bool(library.baseLinker.Properties.Nocrt) {
-			if !ctx.sdk() {
+			if !ctx.useSdk() {
 				deps.CrtBegin = "crtbegin_so"
 				deps.CrtEnd = "crtend_so"
 			} else {
@@ -697,7 +698,7 @@
 func (library *libraryDecorator) install(ctx ModuleContext, file android.Path) {
 	if library.shared() {
 		if ctx.Device() {
-			if ctx.vndk() {
+			if ctx.useVndk() {
 				if ctx.isVndkSp() {
 					library.baseInstaller.subDir = "vndk-sp"
 				} else if ctx.isVndk() {
diff --git a/cc/linker.go b/cc/linker.go
index 02d3ba5..6ec5630 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -132,7 +132,7 @@
 	deps.ReexportSharedLibHeaders = append(deps.ReexportSharedLibHeaders, linker.Properties.Export_shared_lib_headers...)
 	deps.ReexportGeneratedHeaders = append(deps.ReexportGeneratedHeaders, linker.Properties.Export_generated_headers...)
 
-	if ctx.vndk() {
+	if ctx.useVndk() {
 		deps.SharedLibs = removeListFromList(deps.SharedLibs, linker.Properties.Target.Vendor.Exclude_shared_libs)
 		deps.ReexportSharedLibHeaders = removeListFromList(deps.ReexportSharedLibHeaders, linker.Properties.Target.Vendor.Exclude_shared_libs)
 	}
@@ -174,7 +174,7 @@
 			}
 
 			deps.LateSharedLibs = append(deps.LateSharedLibs, systemSharedLibs...)
-		} else if ctx.sdk() || ctx.vndk() {
+		} else if ctx.useSdk() || ctx.useVndk() {
 			deps.LateSharedLibs = append(deps.LateSharedLibs, "libc", "libm", "libdl")
 		}
 	}
@@ -242,7 +242,7 @@
 		}
 	}
 
-	if ctx.sdk() && (ctx.Arch().ArchType != android.Mips && ctx.Arch().ArchType != android.Mips64) {
+	if ctx.useSdk() && (ctx.Arch().ArchType != android.Mips && ctx.Arch().ArchType != android.Mips64) {
 		// The bionic linker now has support gnu style hashes (which are much faster!), but shipping
 		// to older devices requires the old style hash. Fortunately, we can build with both and
 		// it'll work anywhere.
diff --git a/cc/llndk_library.go b/cc/llndk_library.go
index c3d3462..30c4d4c 100644
--- a/cc/llndk_library.go
+++ b/cc/llndk_library.go
@@ -50,6 +50,11 @@
 
 	// Whether the system library uses symbol versions.
 	Unversioned bool
+
+	// whether this module can be directly depended upon by libs that are installed to /vendor.
+	// When set to false, this module can only be depended on by VNDK libraries, not vendor
+	// libraries. This effectively hides this module from vendors. Default value is true.
+	Vendor_available bool
 }
 
 type llndkStubDecorator struct {
@@ -149,6 +154,7 @@
 	stub := &llndkStubDecorator{
 		libraryDecorator: library,
 	}
+	stub.Properties.Vendor_available = true
 	module.compiler = stub
 	module.linker = stub
 	module.installer = nil
diff --git a/cc/makevars.go b/cc/makevars.go
index 2c6af70..295b4ac 100644
--- a/cc/makevars.go
+++ b/cc/makevars.go
@@ -62,6 +62,7 @@
 	ctx.Strict("VNDK_CORE_LIBRARIES", strings.Join(vndkCoreLibraries, " "))
 	ctx.Strict("VNDK_SAMEPROCESS_LIBRARIES", strings.Join(vndkSpLibraries, " "))
 	ctx.Strict("LLNDK_LIBRARIES", strings.Join(llndkLibraries, " "))
+	ctx.Strict("VNDK_PRIVATE_LIBRARIES", strings.Join(vndkPrivateLibraries, " "))
 
 	ctx.Strict("ADDRESS_SANITIZER_CONFIG_EXTRA_CFLAGS", strings.Join(asanCflags, " "))
 	ctx.Strict("ADDRESS_SANITIZER_CONFIG_EXTRA_LDFLAGS", strings.Join(asanLdflags, " "))
diff --git a/cc/object.go b/cc/object.go
index 59d523d..402b105 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -43,7 +43,7 @@
 }
 
 func (object *objectLinker) appendLdflags(flags []string) {
-	panic(fmt.Errorf("appendLdflags on object Linker not supported"))
+	panic(fmt.Errorf("appendLdflags on objectLinker not supported"))
 }
 
 func (object *objectLinker) linkerProps() []interface{} {
diff --git a/cc/pgo.go b/cc/pgo.go
index a99cbad..ea23124 100644
--- a/cc/pgo.go
+++ b/cc/pgo.go
@@ -37,10 +37,11 @@
 
 type PgoProperties struct {
 	Pgo struct {
-		Instrumentation *bool
-		Sampling        *bool
-		Profile_file    *string `android:"arch_variant"`
-		Benchmarks      []string
+		Instrumentation    *bool
+		Sampling           *bool
+		Profile_file       *string `android:"arch_variant"`
+		Benchmarks         []string
+		Enable_profile_use *bool `android:"arch_variant"`
 	} `android:"arch_variant"`
 
 	PgoPresent          bool `blueprint:"mutated"`
@@ -63,37 +64,60 @@
 	return []interface{}{&pgo.Properties}
 }
 
-func (pgo *pgo) addProfileGatherFlags(ctx ModuleContext, flags Flags) Flags {
-	if pgo.Properties.isInstrumentation() {
+func (props *PgoProperties) addProfileGatherFlags(ctx ModuleContext, flags Flags) Flags {
+	if props.isInstrumentation() {
 		flags.CFlags = append(flags.CFlags, profileInstrumentFlag)
 		// The profile runtime is added below in deps().  Add the below
 		// flag, which is the only other link-time action performed by
 		// the Clang driver during link.
 		flags.LdFlags = append(flags.LdFlags, "-u__llvm_profile_runtime")
 	}
-	if pgo.Properties.isSampling() {
+	if props.isSampling() {
 		flags.CFlags = append(flags.CFlags, profileSamplingFlag)
 		flags.LdFlags = append(flags.LdFlags, profileSamplingFlag)
 	}
 	return flags
 }
 
-func (pgo *pgo) profileUseFlag(ctx ModuleContext, file string) string {
-	if pgo.Properties.isInstrumentation() {
+func (props *PgoProperties) profileUseFlag(ctx ModuleContext, file string) string {
+	if props.isInstrumentation() {
 		return fmt.Sprintf(profileUseInstrumentFormat, file)
 	}
-	if pgo.Properties.isSampling() {
+	if props.isSampling() {
 		return fmt.Sprintf(profileUseSamplingFormat, file)
 	}
 	return ""
 }
 
-func (pgo *pgo) profileUseFlags(ctx ModuleContext, file string) []string {
-	flags := []string{pgo.profileUseFlag(ctx, file)}
+func (props *PgoProperties) profileUseFlags(ctx ModuleContext, file string) []string {
+	flags := []string{props.profileUseFlag(ctx, file)}
 	flags = append(flags, profileUseOtherFlags...)
 	return flags
 }
 
+func (props *PgoProperties) addProfileUseFlags(ctx ModuleContext, flags Flags) Flags {
+	// Skip -fprofile-use if 'enable_profile_use' property is set
+	if props.Pgo.Enable_profile_use != nil && *props.Pgo.Enable_profile_use == false {
+		return flags
+	}
+
+	// If the PGO profiles project is found, and this module has PGO
+	// enabled, add flags to use the profile
+	if profilesDir := getPgoProfilesDir(ctx); props.PgoPresent && profilesDir.Valid() {
+		profileFile := android.PathForSource(ctx, profilesDir.String(), *props.Pgo.Profile_file)
+		profileUseFlags := props.profileUseFlags(ctx, profileFile.String())
+
+		flags.CFlags = append(flags.CFlags, profileUseFlags...)
+		flags.LdFlags = append(flags.LdFlags, profileUseFlags...)
+
+		// Update CFlagsDeps and LdFlagsDeps so the module is rebuilt
+		// if profileFile gets updated
+		flags.CFlagsDeps = append(flags.CFlagsDeps, profileFile)
+		flags.LdFlagsDeps = append(flags.LdFlagsDeps, profileFile)
+	}
+	return flags
+}
+
 func (props *PgoProperties) isPGO(ctx BaseModuleContext) bool {
 	isInstrumentation := props.isInstrumentation()
 	isSampling := props.isSampling()
@@ -188,22 +212,11 @@
 
 	// Add flags to profile this module based on its profile_kind
 	if props.ShouldProfileModule {
-		return pgo.addProfileGatherFlags(ctx, flags)
+		return props.addProfileGatherFlags(ctx, flags)
 	}
 
-	// If the PGO profiles project is found, and this module has PGO
-	// enabled, add flags to use the profile
-	if profilesDir := getPgoProfilesDir(ctx); props.PgoPresent && profilesDir.Valid() {
-		profileFile := android.PathForSource(ctx, profilesDir.String(), *(props.Pgo.Profile_file))
-		profileUseFlags := pgo.profileUseFlags(ctx, profileFile.String())
-
-		flags.CFlags = append(flags.CFlags, profileUseFlags...)
-		flags.LdFlags = append(flags.LdFlags, profileUseFlags...)
-
-		// Update CFlagsDeps and LdFlagsDeps so the module is rebuilt
-		// if profileFile gets updated
-		flags.CFlagsDeps = append(flags.CFlagsDeps, profileFile)
-		flags.LdFlagsDeps = append(flags.LdFlagsDeps, profileFile)
+	if !ctx.AConfig().IsEnvTrue("ANDROID_PGO_NO_PROFILE_USE") {
+		return props.addProfileUseFlags(ctx, flags)
 	}
 
 	return flags
diff --git a/cc/proto.go b/cc/proto.go
index a01951f..6e3cce7 100644
--- a/cc/proto.go
+++ b/cc/proto.go
@@ -60,14 +60,14 @@
 
 	switch proptools.String(p.Proto.Type) {
 	case "full":
-		if ctx.sdk() {
+		if ctx.useSdk() {
 			lib = "libprotobuf-cpp-full-ndk"
 			static = true
 		} else {
 			lib = "libprotobuf-cpp-full"
 		}
 	case "lite", "":
-		if ctx.sdk() {
+		if ctx.useSdk() {
 			lib = "libprotobuf-cpp-lite-ndk"
 			static = true
 		} else {
diff --git a/cc/relocation_packer.go b/cc/relocation_packer.go
index c9f82ba..d9b367c 100644
--- a/cc/relocation_packer.go
+++ b/cc/relocation_packer.go
@@ -56,7 +56,7 @@
 	if ctx.AConfig().Getenv("DISABLE_RELOCATION_PACKER") == "true" {
 		enabled = false
 	}
-	if ctx.sdk() {
+	if ctx.useSdk() {
 		enabled = false
 	}
 	if p.Properties.Pack_relocations != nil &&
diff --git a/cc/rs.go b/cc/rs.go
index 976107e..e7eb3bb 100644
--- a/cc/rs.go
+++ b/cc/rs.go
@@ -82,7 +82,7 @@
 
 func rsFlags(ctx ModuleContext, flags Flags, properties *BaseCompilerProperties) Flags {
 	targetApi := proptools.String(properties.Renderscript.Target_api)
-	if targetApi == "" && ctx.sdk() {
+	if targetApi == "" && ctx.useSdk() {
 		switch ctx.sdkVersion() {
 		case "current", "system_current", "test_current":
 			// Nothing
diff --git a/cc/sabi.go b/cc/sabi.go
index e45b040..8086f5b 100644
--- a/cc/sabi.go
+++ b/cc/sabi.go
@@ -79,7 +79,7 @@
 
 func sabiDepsMutator(mctx android.TopDownMutatorContext) {
 	if c, ok := mctx.Module().(*Module); ok &&
-		((c.isVndk() && c.vndk()) || inList(c.Name(), llndkLibraries) ||
+		((c.isVndk() && c.useVndk()) || inList(c.Name(), llndkLibraries) ||
 			(c.sabi != nil && c.sabi.Properties.CreateSAbiDumps)) {
 		mctx.VisitDirectDeps(func(m blueprint.Module) {
 			tag := mctx.OtherModuleDependencyTag(m)
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 090d490..b8b5ffa 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -128,7 +128,7 @@
 	s := &sanitize.Properties.Sanitize
 
 	// Don't apply sanitizers to NDK code.
-	if ctx.sdk() {
+	if ctx.useSdk() {
 		s.Never = true
 	}
 
@@ -421,7 +421,7 @@
 
 		// When linking against VNDK, use the vendor variant of the runtime lib
 		sanitize.androidMkRuntimeLibrary = sanitize.runtimeLibrary
-		if ctx.vndk() {
+		if ctx.useVndk() {
 			sanitize.androidMkRuntimeLibrary = sanitize.runtimeLibrary + vendorSuffix
 		}
 	}
diff --git a/cc/stl.go b/cc/stl.go
index 17cde59..347db99 100644
--- a/cc/stl.go
+++ b/cc/stl.go
@@ -42,7 +42,7 @@
 		if stl.Properties.Stl != nil {
 			s = *stl.Properties.Stl
 		}
-		if ctx.sdk() && ctx.Device() {
+		if ctx.useSdk() && ctx.Device() {
 			switch s {
 			case "":
 				return "ndk_system"
diff --git a/cc/test.go b/cc/test.go
index 12cc2ad..fa75f48 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -157,7 +157,7 @@
 
 func (test *testDecorator) linkerDeps(ctx BaseModuleContext, deps Deps) Deps {
 	if test.gtest() {
-		if ctx.sdk() && ctx.Device() {
+		if ctx.useSdk() && ctx.Device() {
 			switch ctx.selectedStl() {
 			case "ndk_libc++_shared", "ndk_libc++_static":
 				deps.StaticLibs = append(deps.StaticLibs, "libgtest_main_ndk_libcxx", "libgtest_ndk_libcxx")
diff --git a/cc/test_data_test.go b/cc/test_data_test.go
index 962bde5..434edcd 100644
--- a/cc/test_data_test.go
+++ b/cc/test_data_test.go
@@ -117,7 +117,7 @@
 	}
 	defer os.RemoveAll(buildDir)
 
-	config := android.TestConfig(buildDir)
+	config := android.TestConfig(buildDir, nil)
 
 	for _, test := range testDataTests {
 		t.Run(test.name, func(t *testing.T) {
diff --git a/cc/vndk.go b/cc/vndk.go
index 395069b..860678d 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -27,8 +27,8 @@
 		// declared as a VNDK or VNDK-SP module. The vendor variant
 		// will be installed in /system instead of /vendor partition.
 		//
-		// `vendor_available: true` must set to together for VNDK
-		// modules.
+		// `vendor_vailable` must be explicitly set to either true or
+		// false together with `vndk: {enabled: true}`.
 		Enabled *bool
 
 		// declared as a VNDK-SP module, which is a subset of VNDK.
@@ -81,6 +81,24 @@
 	if to.linker == nil {
 		return
 	}
+	if !vndk.isVndk() {
+		// Non-VNDK modules (those installed to /vendor) can't depend on modules marked with
+		// vendor_available: false.
+		violation := false
+		if lib, ok := to.linker.(*llndkStubDecorator); ok && !lib.Properties.Vendor_available {
+			violation = true
+		} else {
+			if _, ok := to.linker.(libraryInterface); ok && to.VendorProperties.Vendor_available != nil && !Bool(to.VendorProperties.Vendor_available) {
+				// Vendor_available == nil && !Bool(Vendor_available) should be okay since
+				// it means a vendor-only library which is a valid dependency for non-VNDK
+				// modules.
+				violation = true
+			}
+		}
+		if violation {
+			ctx.ModuleErrorf("Vendor module that is not VNDK should not link to %q which is marked as `vendor_available: false`", to.Name())
+		}
+	}
 	if lib, ok := to.linker.(*libraryDecorator); !ok || !lib.shared() {
 		// Check only shared libraries.
 		// Other (static and LL-NDK) libraries are allowed to link.
@@ -102,16 +120,17 @@
 }
 
 var (
-	vndkCoreLibraries []string
-	vndkSpLibraries   []string
-	llndkLibraries    []string
-	vndkLibrariesLock sync.Mutex
+	vndkCoreLibraries    []string
+	vndkSpLibraries      []string
+	llndkLibraries       []string
+	vndkPrivateLibraries []string
+	vndkLibrariesLock    sync.Mutex
 )
 
 // gather list of vndk-core, vndk-sp, and ll-ndk libs
 func vndkMutator(mctx android.BottomUpMutatorContext) {
 	if m, ok := mctx.Module().(*Module); ok {
-		if _, ok := m.linker.(*llndkStubDecorator); ok {
+		if lib, ok := m.linker.(*llndkStubDecorator); ok {
 			vndkLibrariesLock.Lock()
 			defer vndkLibrariesLock.Unlock()
 			name := strings.TrimSuffix(m.Name(), llndkLibrarySuffix)
@@ -119,22 +138,40 @@
 				llndkLibraries = append(llndkLibraries, name)
 				sort.Strings(llndkLibraries)
 			}
-		} else if lib, ok := m.linker.(*libraryDecorator); ok && lib.shared() {
-			if m.vndkdep.isVndk() {
-				vndkLibrariesLock.Lock()
-				defer vndkLibrariesLock.Unlock()
-				if m.vndkdep.isVndkSp() {
-					if !inList(m.Name(), vndkSpLibraries) {
-						vndkSpLibraries = append(vndkSpLibraries, m.Name())
-						sort.Strings(vndkSpLibraries)
+			if !lib.Properties.Vendor_available {
+				if !inList(name, vndkPrivateLibraries) {
+					vndkPrivateLibraries = append(vndkPrivateLibraries, name)
+					sort.Strings(vndkPrivateLibraries)
+				}
+			}
+		} else {
+			lib, is_lib := m.linker.(*libraryDecorator)
+			prebuilt_lib, is_prebuilt_lib := m.linker.(*prebuiltLibraryLinker)
+			if (is_lib && lib.shared()) || (is_prebuilt_lib && prebuilt_lib.shared()) {
+				name := strings.TrimPrefix(m.Name(), "prebuilt_")
+				if m.vndkdep.isVndk() {
+					vndkLibrariesLock.Lock()
+					defer vndkLibrariesLock.Unlock()
+					if m.vndkdep.isVndkSp() {
+						if !inList(name, vndkSpLibraries) {
+							vndkSpLibraries = append(vndkSpLibraries, name)
+							sort.Strings(vndkSpLibraries)
+						}
+					} else {
+						if !inList(name, vndkCoreLibraries) {
+							vndkCoreLibraries = append(vndkCoreLibraries, name)
+							sort.Strings(vndkCoreLibraries)
+						}
 					}
-				} else {
-					if !inList(m.Name(), vndkCoreLibraries) {
-						vndkCoreLibraries = append(vndkCoreLibraries, m.Name())
-						sort.Strings(vndkCoreLibraries)
+					if !Bool(m.VendorProperties.Vendor_available) {
+						if !inList(name, vndkPrivateLibraries) {
+							vndkPrivateLibraries = append(vndkPrivateLibraries, name)
+							sort.Strings(vndkPrivateLibraries)
+						}
 					}
 				}
 			}
 		}
+
 	}
 }
diff --git a/cmd/javac_wrapper/javac_wrapper.go b/cmd/javac_wrapper/javac_wrapper.go
index ab4d23f..4df4938 100644
--- a/cmd/javac_wrapper/javac_wrapper.go
+++ b/cmd/javac_wrapper/javac_wrapper.go
@@ -17,6 +17,11 @@
 //
 // It also hides the unhelpful and unhideable "warning there is a warning"
 // messages.
+//
+// Each javac build statement has an order-only dependency on the
+// soong_javac_wrapper tool, which means the javac command will not be rerun
+// if soong_javac_wrapper changes.  That means that soong_javac_wrapper must
+// not do anything that will affect the results of the build.
 package main
 
 import (
@@ -170,4 +175,5 @@
 	regexp.MustCompile(`Note: Recompile with -Xlint:deprecation for details.`),
 	regexp.MustCompile(`Note: (Some input files|.*\.java) uses? unchecked or unsafe operations.`),
 	regexp.MustCompile(`Note: Recompile with -Xlint:unchecked for details.`),
+	regexp.MustCompile(`bootstrap class path not set in conjunction with -source`),
 }
diff --git a/cmd/javac_wrapper/javac_wrapper_test.go b/cmd/javac_wrapper/javac_wrapper_test.go
index c345479..d76793f 100644
--- a/cmd/javac_wrapper/javac_wrapper_test.go
+++ b/cmd/javac_wrapper/javac_wrapper_test.go
@@ -41,8 +41,8 @@
 		out: "\x1b[1mFile.java:398: \x1b[35mwarning:\x1b[0m\x1b[1m [RectIntersectReturnValueIgnored] Return value of com.blah.function() must be checked\x1b[0m\n",
 	},
 	{
-		in:  "warning: [options] bootstrap class path not set in conjunction with -source 1.7\n",
-		out: "\x1b[1m\x1b[35mwarning:\x1b[0m\x1b[1m [options] bootstrap class path not set in conjunction with -source 1.7\x1b[0m\n",
+		in:  "warning: [options] blah\n",
+		out: "\x1b[1m\x1b[35mwarning:\x1b[0m\x1b[1m [options] blah\x1b[0m\n",
 	},
 	{
 		in:  "    (see http://go/errorprone/bugpattern/RectIntersectReturnValueIgnored.md)\n",
@@ -56,6 +56,7 @@
 Note: Recompile with -Xlint:unchecked for details.
 Note: dir/file.java uses or overrides a deprecated API.
 Note: dir/file.java uses unchecked or unsafe operations.
+warning: [options] bootstrap class path not set in conjunction with -source 1.7
 `,
 		out: "\n",
 	},
diff --git a/cmd/multiproduct_kati/main.go b/cmd/multiproduct_kati/main.go
index e771c15..1c853d6 100644
--- a/cmd/multiproduct_kati/main.go
+++ b/cmd/multiproduct_kati/main.go
@@ -55,6 +55,8 @@
 
 var buildVariant = flag.String("variant", "eng", "build variant to use")
 
+var skipProducts = flag.String("skip-products", "", "comma-separated list of products to skip (known failures, etc)")
+
 const errorLeadingLines = 20
 const errorTrailingLines = 20
 
@@ -221,12 +223,31 @@
 		trace.SetOutput(filepath.Join(config.OutDir(), "build.trace"))
 	}
 
-	vars, err := build.DumpMakeVars(buildCtx, config, nil, nil, []string{"all_named_products"})
+	vars, err := build.DumpMakeVars(buildCtx, config, nil, []string{"all_named_products"})
 	if err != nil {
 		log.Fatal(err)
 	}
-	products := strings.Fields(vars["all_named_products"])
-	log.Verbose("Got product list:", products)
+	productsList := strings.Fields(vars["all_named_products"])
+
+	products := make([]string, 0, len(productsList))
+	skipList := strings.Split(*skipProducts, ",")
+	skipProduct := func(p string) bool {
+		for _, s := range skipList {
+			if p == s {
+				return true
+			}
+		}
+		return false
+	}
+	for _, product := range productsList {
+		if !skipProduct(product) {
+			products = append(products, product)
+		} else {
+			log.Verbose("Skipping: ", product)
+		}
+	}
+
+	log.Verbose("Got product list: ", products)
 
 	status.SetTotal(len(products))
 
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index 8a26171..0619b5c 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -16,6 +16,8 @@
 
 import (
 	"context"
+	"flag"
+	"fmt"
 	"os"
 	"path/filepath"
 	"strconv"
@@ -45,7 +47,10 @@
 	log := logger.New(os.Stderr)
 	defer log.Cleanup()
 
-	if len(os.Args) < 2 || !inList("--make-mode", os.Args) {
+	if len(os.Args) < 2 || !(inList("--make-mode", os.Args) ||
+		os.Args[1] == "--dumpvars-mode" ||
+		os.Args[1] == "--dumpvar-mode") {
+
 		log.Fatalln("The `soong` native UI is not yet available.")
 	}
 
@@ -66,7 +71,12 @@
 		Tracer:         trace,
 		StdioInterface: build.StdioImpl{},
 	}}
-	config := build.NewConfig(buildCtx, os.Args[1:]...)
+	var config build.Config
+	if os.Args[1] == "--dumpvars-mode" || os.Args[1] == "--dumpvar-mode" {
+		config = build.NewConfig(buildCtx)
+	} else {
+		config = build.NewConfig(buildCtx, os.Args[1:]...)
+	}
 
 	log.SetVerbose(config.IsVerbose())
 	build.SetupOutDir(buildCtx, config)
@@ -99,5 +109,129 @@
 	defer f.Shutdown()
 	build.FindSources(buildCtx, config, f)
 
-	build.Build(buildCtx, config, build.BuildAll)
+	if os.Args[1] == "--dumpvar-mode" {
+		dumpVar(buildCtx, config, os.Args[2:])
+	} else if os.Args[1] == "--dumpvars-mode" {
+		dumpVars(buildCtx, config, os.Args[2:])
+	} else {
+		build.Build(buildCtx, config, build.BuildAll)
+	}
+}
+
+func dumpVar(ctx build.Context, config build.Config, args []string) {
+	flags := flag.NewFlagSet("dumpvar", flag.ExitOnError)
+	flags.Usage = func() {
+		fmt.Fprintf(os.Stderr, "usage: %s --dumpvar-mode [--abs] <VAR>\n\n", os.Args[0])
+		fmt.Fprintln(os.Stderr, "In dumpvar mode, print the value of the legacy make variable VAR to stdout")
+		fmt.Fprintln(os.Stderr, "")
+
+		fmt.Fprintln(os.Stderr, "'report_config' is a special case that prints the human-readable config banner")
+		fmt.Fprintln(os.Stderr, "from the beginning of the build.")
+		fmt.Fprintln(os.Stderr, "")
+		flags.PrintDefaults()
+	}
+	abs := flags.Bool("abs", false, "Print the absolute path of the value")
+	flags.Parse(args)
+
+	if flags.NArg() != 1 {
+		flags.Usage()
+		os.Exit(1)
+	}
+
+	varName := flags.Arg(0)
+	if varName == "report_config" {
+		varData, err := build.DumpMakeVars(ctx, config, nil, build.BannerVars)
+		if err != nil {
+			ctx.Fatal(err)
+		}
+
+		fmt.Println(build.Banner(varData))
+	} else {
+		varData, err := build.DumpMakeVars(ctx, config, nil, []string{varName})
+		if err != nil {
+			ctx.Fatal(err)
+		}
+
+		if *abs {
+			var res []string
+			for _, path := range strings.Fields(varData[varName]) {
+				if abs, err := filepath.Abs(path); err == nil {
+					res = append(res, abs)
+				} else {
+					ctx.Fatalln("Failed to get absolute path of", path, err)
+				}
+			}
+			fmt.Println(strings.Join(res, " "))
+		} else {
+			fmt.Println(varData[varName])
+		}
+	}
+}
+
+func dumpVars(ctx build.Context, config build.Config, args []string) {
+	flags := flag.NewFlagSet("dumpvars", flag.ExitOnError)
+	flags.Usage = func() {
+		fmt.Fprintf(os.Stderr, "usage: %s --dumpvars-mode [--vars=\"VAR VAR ...\"]\n\n", os.Args[0])
+		fmt.Fprintln(os.Stderr, "In dumpvars mode, dump the values of one or more legacy make variables, in")
+		fmt.Fprintln(os.Stderr, "shell syntax. The resulting output may be sourced directly into a shell to")
+		fmt.Fprintln(os.Stderr, "set corresponding shell variables.")
+		fmt.Fprintln(os.Stderr, "")
+
+		fmt.Fprintln(os.Stderr, "'report_config' is a special case that dumps a variable containing the")
+		fmt.Fprintln(os.Stderr, "human-readable config banner from the beginning of the build.")
+		fmt.Fprintln(os.Stderr, "")
+		flags.PrintDefaults()
+	}
+
+	varsStr := flags.String("vars", "", "Space-separated list of variables to dump")
+	absVarsStr := flags.String("abs-vars", "", "Space-separated list of variables to dump (using absolute paths)")
+
+	varPrefix := flags.String("var-prefix", "", "String to prepend to all variable names when dumping")
+	absVarPrefix := flags.String("abs-var-prefix", "", "String to prepent to all absolute path variable names when dumping")
+
+	flags.Parse(args)
+
+	if flags.NArg() != 0 {
+		flags.Usage()
+		os.Exit(1)
+	}
+
+	vars := strings.Fields(*varsStr)
+	absVars := strings.Fields(*absVarsStr)
+
+	allVars := append([]string{}, vars...)
+	allVars = append(allVars, absVars...)
+
+	if i := indexList("report_config", allVars); i != -1 {
+		allVars = append(allVars[:i], allVars[i+1:]...)
+		allVars = append(allVars, build.BannerVars...)
+	}
+
+	if len(allVars) == 0 {
+		return
+	}
+
+	varData, err := build.DumpMakeVars(ctx, config, nil, allVars)
+	if err != nil {
+		ctx.Fatal(err)
+	}
+
+	for _, name := range vars {
+		if name == "report_config" {
+			fmt.Printf("%sreport_config='%s'\n", *varPrefix, build.Banner(varData))
+		} else {
+			fmt.Printf("%s%s='%s'\n", *varPrefix, name, varData[name])
+		}
+	}
+	for _, name := range absVars {
+		var res []string
+		for _, path := range strings.Fields(varData[name]) {
+			abs, err := filepath.Abs(path)
+			if err != nil {
+				ctx.Fatalln("Failed to get absolute path of", path, err)
+			}
+			res = append(res, abs)
+		}
+		fmt.Printf("%s%s='%s'\n", *absVarPrefix, name, strings.Join(res, " "))
+	}
 }
diff --git a/cmd/soong_zip/soong_zip.go b/cmd/soong_zip/soong_zip.go
index cb9df9a..2bcc9a5 100644
--- a/cmd/soong_zip/soong_zip.go
+++ b/cmd/soong_zip/soong_zip.go
@@ -62,12 +62,6 @@
 	io.Closer
 }
 
-type fileArg struct {
-	pathPrefixInZip, sourcePrefixToStrip string
-	sourceFiles                          []string
-	globDir                              string
-}
-
 type pathMapping struct {
 	dest, src string
 	zipMethod uint16
@@ -89,8 +83,6 @@
 	return nil
 }
 
-type fileArgs []fileArg
-
 type file struct{}
 
 type listFiles struct{}
@@ -106,10 +98,10 @@
 		return fmt.Errorf("must pass -C before -f")
 	}
 
-	fArgs = append(fArgs, fileArg{
-		pathPrefixInZip:     filepath.Clean(*rootPrefix),
-		sourcePrefixToStrip: filepath.Clean(*relativeRoot),
-		sourceFiles:         []string{s},
+	fArgs = append(fArgs, FileArg{
+		PathPrefixInZip:     filepath.Clean(*rootPrefix),
+		SourcePrefixToStrip: filepath.Clean(*relativeRoot),
+		SourceFiles:         []string{s},
 	})
 
 	return nil
@@ -129,10 +121,10 @@
 		return err
 	}
 
-	fArgs = append(fArgs, fileArg{
-		pathPrefixInZip:     filepath.Clean(*rootPrefix),
-		sourcePrefixToStrip: filepath.Clean(*relativeRoot),
-		sourceFiles:         strings.Split(string(list), "\n"),
+	fArgs = append(fArgs, FileArg{
+		PathPrefixInZip:     filepath.Clean(*rootPrefix),
+		SourcePrefixToStrip: filepath.Clean(*relativeRoot),
+		SourceFiles:         strings.Split(string(list), "\n"),
 	})
 
 	return nil
@@ -147,10 +139,10 @@
 		return fmt.Errorf("must pass -C before -D")
 	}
 
-	fArgs = append(fArgs, fileArg{
-		pathPrefixInZip:     filepath.Clean(*rootPrefix),
-		sourcePrefixToStrip: filepath.Clean(*relativeRoot),
-		globDir:             filepath.Clean(s),
+	fArgs = append(fArgs, FileArg{
+		PathPrefixInZip:     filepath.Clean(*rootPrefix),
+		SourcePrefixToStrip: filepath.Clean(*relativeRoot),
+		GlobDir:             filepath.Clean(s),
 	})
 
 	return nil
@@ -166,7 +158,7 @@
 	compLevel    = flag.Int("L", 5, "deflate compression level (0-9)")
 	emulateJar   = flag.Bool("jar", false, "modify the resultant .zip to emulate the output of 'jar'")
 
-	fArgs            fileArgs
+	fArgs            FileArgs
 	nonDeflatedFiles = make(uniqueSet)
 
 	cpuProfile = flag.String("cpuprofile", "", "write cpu profile to file")
@@ -186,7 +178,36 @@
 	os.Exit(2)
 }
 
-type zipWriter struct {
+func main() {
+	flag.Parse()
+
+	err := Run(ZipArgs{
+		FileArgs:                 fArgs,
+		OutputFilePath:           *out,
+		CpuProfileFilePath:       *cpuProfile,
+		TraceFilePath:            *traceFile,
+		EmulateJar:               *emulateJar,
+		AddDirectoryEntriesToZip: *directories,
+		CompressionLevel:         *compLevel,
+		ManifestSourcePath:       *manifest,
+		NumParallelJobs:          *parallelJobs,
+		NonDeflatedFiles:         nonDeflatedFiles,
+	})
+	if err != nil {
+		fmt.Fprintln(os.Stderr, err.Error())
+		os.Exit(1)
+	}
+}
+
+type FileArg struct {
+	PathPrefixInZip, SourcePrefixToStrip string
+	SourceFiles                          []string
+	GlobDir                              string
+}
+
+type FileArgs []FileArg
+
+type ZipWriter struct {
 	time         time.Time
 	createdFiles map[string]string
 	createdDirs  map[string]string
@@ -213,11 +234,22 @@
 	allocatedSize int64
 }
 
-func main() {
-	flag.Parse()
+type ZipArgs struct {
+	FileArgs                 FileArgs
+	OutputFilePath           string
+	CpuProfileFilePath       string
+	TraceFilePath            string
+	EmulateJar               bool
+	AddDirectoryEntriesToZip bool
+	CompressionLevel         int
+	ManifestSourcePath       string
+	NumParallelJobs          int
+	NonDeflatedFiles         map[string]bool
+}
 
-	if *cpuProfile != "" {
-		f, err := os.Create(*cpuProfile)
+func Run(args ZipArgs) (err error) {
+	if args.CpuProfileFilePath != "" {
+		f, err := os.Create(args.CpuProfileFilePath)
 		if err != nil {
 			fmt.Fprintln(os.Stderr, err.Error())
 			os.Exit(1)
@@ -227,8 +259,8 @@
 		defer pprof.StopCPUProfile()
 	}
 
-	if *traceFile != "" {
-		f, err := os.Create(*traceFile)
+	if args.TraceFilePath != "" {
+		f, err := os.Create(args.TraceFilePath)
 		if err != nil {
 			fmt.Fprintln(os.Stderr, err.Error())
 			os.Exit(1)
@@ -242,46 +274,41 @@
 		defer trace.Stop()
 	}
 
-	if *out == "" {
-		fmt.Fprintf(os.Stderr, "error: -o is required\n")
-		usage()
+	if args.OutputFilePath == "" {
+		return fmt.Errorf("output file path must be nonempty")
 	}
 
-	if *emulateJar {
-		*directories = true
+	if args.EmulateJar {
+		args.AddDirectoryEntriesToZip = true
 	}
 
-	w := &zipWriter{
+	w := &ZipWriter{
 		time:         jar.DefaultTime,
 		createdDirs:  make(map[string]string),
 		createdFiles: make(map[string]string),
-		directories:  *directories,
-		compLevel:    *compLevel,
+		directories:  args.AddDirectoryEntriesToZip,
+		compLevel:    args.CompressionLevel,
 	}
-
 	pathMappings := []pathMapping{}
 
-	for _, fa := range fArgs {
-		srcs := fa.sourceFiles
-		if fa.globDir != "" {
-			srcs = append(srcs, recursiveGlobFiles(fa.globDir)...)
+	for _, fa := range args.FileArgs {
+		srcs := fa.SourceFiles
+		if fa.GlobDir != "" {
+			srcs = append(srcs, recursiveGlobFiles(fa.GlobDir)...)
 		}
 		for _, src := range srcs {
-			if err := fillPathPairs(fa.pathPrefixInZip,
-				fa.sourcePrefixToStrip, src, &pathMappings); err != nil {
+			if err := fillPathPairs(fa.PathPrefixInZip,
+				fa.SourcePrefixToStrip, src, &pathMappings, args.NonDeflatedFiles); err != nil {
 				log.Fatal(err)
 			}
 		}
 	}
 
-	err := w.write(*out, pathMappings, *manifest)
-	if err != nil {
-		fmt.Fprintln(os.Stderr, err.Error())
-		os.Exit(1)
-	}
+	return w.write(args.OutputFilePath, pathMappings, args.ManifestSourcePath, args.EmulateJar, args.NumParallelJobs)
+
 }
 
-func fillPathPairs(prefix, rel, src string, pathMappings *[]pathMapping) error {
+func fillPathPairs(prefix, rel, src string, pathMappings *[]pathMapping, nonDeflatedFiles map[string]bool) error {
 	src = strings.TrimSpace(src)
 	if src == "" {
 		return nil
@@ -317,7 +344,7 @@
 	io.Seeker
 }
 
-func (z *zipWriter) write(out string, pathMappings []pathMapping, manifest string) error {
+func (z *ZipWriter) write(out string, pathMappings []pathMapping, manifest string, emulateJar bool, parallelJobs int) error {
 	f, err := os.Create(out)
 	if err != nil {
 		return err
@@ -346,18 +373,18 @@
 	// The RateLimit object will put the upper bounds on the number of
 	// parallel compressions and outstanding buffers.
 	z.writeOps = make(chan chan *zipEntry, 1000)
-	z.cpuRateLimiter = NewCPURateLimiter(int64(*parallelJobs))
+	z.cpuRateLimiter = NewCPURateLimiter(int64(parallelJobs))
 	z.memoryRateLimiter = NewMemoryRateLimiter(0)
 	defer func() {
 		z.cpuRateLimiter.Stop()
 		z.memoryRateLimiter.Stop()
 	}()
 
-	if manifest != "" && !*emulateJar {
+	if manifest != "" && !emulateJar {
 		return errors.New("must specify --jar when specifying a manifest via -m")
 	}
 
-	if *emulateJar {
+	if emulateJar {
 		// manifest may be empty, in which case addManifest will fill in a default
 		pathMappings = append(pathMappings, pathMapping{jar.ManifestFile, manifest, zip.Deflate})
 
@@ -369,10 +396,10 @@
 		defer close(z.writeOps)
 
 		for _, ele := range pathMappings {
-			if *emulateJar && ele.dest == jar.ManifestFile {
+			if emulateJar && ele.dest == jar.ManifestFile {
 				err = z.addManifest(ele.dest, ele.src, ele.zipMethod)
 			} else {
-				err = z.addFile(ele.dest, ele.src, ele.zipMethod)
+				err = z.addFile(ele.dest, ele.src, ele.zipMethod, emulateJar)
 			}
 			if err != nil {
 				z.errors <- err
@@ -470,7 +497,7 @@
 }
 
 // imports (possibly with compression) <src> into the zip at sub-path <dest>
-func (z *zipWriter) addFile(dest, src string, method uint16) error {
+func (z *ZipWriter) addFile(dest, src string, method uint16, emulateJar bool) error {
 	var fileSize int64
 	var executable bool
 
@@ -478,11 +505,11 @@
 		return err
 	} else if s.IsDir() {
 		if z.directories {
-			return z.writeDirectory(dest, src)
+			return z.writeDirectory(dest, src, emulateJar)
 		}
 		return nil
 	} else {
-		if err := z.writeDirectory(filepath.Dir(dest), src); err != nil {
+		if err := z.writeDirectory(filepath.Dir(dest), src, emulateJar); err != nil {
 			return err
 		}
 
@@ -523,7 +550,7 @@
 	return z.writeFileContents(header, r)
 }
 
-func (z *zipWriter) addManifest(dest string, src string, method uint16) error {
+func (z *ZipWriter) addManifest(dest string, src string, method uint16) error {
 	if prev, exists := z.createdDirs[dest]; exists {
 		return fmt.Errorf("destination %q is both a directory %q and a file %q", dest, prev, src)
 	}
@@ -531,7 +558,7 @@
 		return fmt.Errorf("destination %q has two files %q and %q", dest, prev, src)
 	}
 
-	if err := z.writeDirectory(filepath.Dir(dest), src); err != nil {
+	if err := z.writeDirectory(filepath.Dir(dest), src, true); err != nil {
 		return err
 	}
 
@@ -545,7 +572,7 @@
 	return z.writeFileContents(fh, reader)
 }
 
-func (z *zipWriter) writeFileContents(header *zip.FileHeader, r readerSeekerCloser) (err error) {
+func (z *ZipWriter) writeFileContents(header *zip.FileHeader, r readerSeekerCloser) (err error) {
 
 	header.SetModTime(z.time)
 
@@ -621,7 +648,7 @@
 	return nil
 }
 
-func (z *zipWriter) crcFile(r io.Reader, ze *zipEntry, resultChan chan *zipEntry, wg *sync.WaitGroup) {
+func (z *ZipWriter) crcFile(r io.Reader, ze *zipEntry, resultChan chan *zipEntry, wg *sync.WaitGroup) {
 	defer wg.Done()
 	defer z.cpuRateLimiter.Finish()
 
@@ -637,7 +664,7 @@
 	close(resultChan)
 }
 
-func (z *zipWriter) compressPartialFile(r io.Reader, dict []byte, last bool, resultChan chan io.Reader, wg *sync.WaitGroup) {
+func (z *ZipWriter) compressPartialFile(r io.Reader, dict []byte, last bool, resultChan chan io.Reader, wg *sync.WaitGroup) {
 	defer wg.Done()
 
 	result, err := z.compressBlock(r, dict, last)
@@ -651,7 +678,7 @@
 	resultChan <- result
 }
 
-func (z *zipWriter) compressBlock(r io.Reader, dict []byte, last bool) (*bytes.Buffer, error) {
+func (z *ZipWriter) compressBlock(r io.Reader, dict []byte, last bool) (*bytes.Buffer, error) {
 	buf := new(bytes.Buffer)
 	var fw *flate.Writer
 	var err error
@@ -685,7 +712,7 @@
 	return buf, nil
 }
 
-func (z *zipWriter) compressWholeFile(ze *zipEntry, r io.ReadSeeker, compressChan chan *zipEntry) {
+func (z *ZipWriter) compressWholeFile(ze *zipEntry, r io.ReadSeeker, compressChan chan *zipEntry) {
 
 	crc := crc32.NewIEEE()
 	_, err := io.Copy(crc, r)
@@ -758,7 +785,7 @@
 
 // writeDirectory annotates that dir is a directory created for the src file or directory, and adds
 // the directory entry to the zip file if directories are enabled.
-func (z *zipWriter) writeDirectory(dir, src string) error {
+func (z *ZipWriter) writeDirectory(dir string, src string, emulateJar bool) error {
 	// clean the input
 	dir = filepath.Clean(dir)
 
@@ -785,7 +812,7 @@
 		for _, cleanDir := range zipDirs {
 			var dirHeader *zip.FileHeader
 
-			if *emulateJar && cleanDir+"/" == jar.MetaDir {
+			if emulateJar && cleanDir+"/" == jar.MetaDir {
 				dirHeader = jar.MetaDirFileHeader()
 			} else {
 				dirHeader = &zip.FileHeader{
@@ -808,7 +835,7 @@
 	return nil
 }
 
-func (z *zipWriter) writeSymlink(rel, file string) error {
+func (z *ZipWriter) writeSymlink(rel, file string) error {
 	fileHeader := &zip.FileHeader{
 		Name: rel,
 	}
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 479e67a..4a734da 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -28,8 +28,8 @@
 )
 
 func init() {
-	android.RegisterModuleType("gensrcs", genSrcsFactory)
-	android.RegisterModuleType("genrule", genRuleFactory)
+	android.RegisterModuleType("gensrcs", GenSrcsFactory)
+	android.RegisterModuleType("genrule", GenRuleFactory)
 }
 
 var (
@@ -61,13 +61,13 @@
 	//
 	// Available variables for substitution:
 	//
-	// $(location): the path to the first entry in tools or tool_files
-	// $(location <label>): the path to the tool or tool_file with name <label>
-	// $(in): one or more input files
-	// $(out): a single output file
-	// $(depfile): a file to which dependencies will be written, if the depfile property is set to true
-	// $(genDir): the sandbox directory for this tool; contains $(out)
-	// $$: a literal $
+	//  $(location): the path to the first entry in tools or tool_files
+	//  $(location <label>): the path to the tool or tool_file with name <label>
+	//  $(in): one or more input files
+	//  $(out): a single output file
+	//  $(depfile): a file to which dependencies will be written, if the depfile property is set to true
+	//  $(genDir): the sandbox directory for this tool; contains $(out)
+	//  $$: a literal $
 	//
 	// All files used must be declared as inputs (to ensure proper up-to-date checks).
 	// Use "$(in)" directly in Cmd to ensure that all inputs used are declared.
@@ -331,7 +331,7 @@
 	return generatorFactory(tasks, properties)
 }
 
-func genSrcsFactory() android.Module {
+func GenSrcsFactory() android.Module {
 	m := NewGenSrcs()
 	android.InitAndroidModule(m)
 	return m
@@ -361,7 +361,7 @@
 	return generatorFactory(tasks, properties)
 }
 
-func genRuleFactory() android.Module {
+func GenRuleFactory() android.Module {
 	m := NewGenRule()
 	android.InitAndroidModule(m)
 	return m
diff --git a/java/androidmk.go b/java/androidmk.go
index 89d7d51..e349de4 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -19,6 +19,8 @@
 	"io"
 	"strings"
 
+	"github.com/google/blueprint/proptools"
+
 	"android/soong/android"
 )
 
@@ -38,6 +40,25 @@
 				fmt.Fprintln(w, "LOCAL_SDK_VERSION :=", library.deviceProperties.Sdk_version)
 			},
 		},
+		Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
+			android.WriteAndroidMkData(w, data)
+
+			if proptools.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")
+				fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", library.classpathFile.String())
+				if library.properties.Installable != nil && *library.properties.Installable == false {
+					fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := true")
+				}
+				if library.dexJarFile != nil {
+					fmt.Fprintln(w, "LOCAL_SOONG_DEX_JAR :=", library.dexJarFile.String())
+				}
+				fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES := "+strings.Join(data.Required, " "))
+				fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_java_prebuilt.mk")
+			}
+		},
 	}
 }
 
diff --git a/java/app.go b/java/app.go
index e40478a..42ae236 100644
--- a/java/app.go
+++ b/java/app.go
@@ -89,7 +89,7 @@
 		publicResourcesFile, proguardOptionsFile, aaptJavaFileList :=
 			CreateResourceJavaFiles(ctx, aaptRJavaFlags, aaptDeps)
 		a.aaptJavaFileList = aaptJavaFileList
-		a.ExtraSrcLists = append(a.ExtraSrcLists, aaptJavaFileList)
+		// TODO(ccross):  export aapt generated java files as a src jar
 
 		if a.appProperties.Export_package_resources {
 			aaptPackageFlags := append([]string(nil), aaptFlags...)
@@ -274,8 +274,6 @@
 func AndroidAppFactory() android.Module {
 	module := &AndroidApp{}
 
-	module.deviceProperties.Dex = true
-
 	module.AddProperties(
 		&module.Module.properties,
 		&module.Module.deviceProperties,
diff --git a/java/builder.go b/java/builder.go
index ca0d2c5..9086d51 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -39,22 +39,39 @@
 	javac = pctx.AndroidGomaStaticRule("javac",
 		blueprint.RuleParams{
 			Command: `rm -rf "$outDir" "$annoDir" && mkdir -p "$outDir" "$annoDir" && ` +
-				`${config.JavacWrapper}${config.JavacCmd} ${config.JavacHeapFlags} ${config.CommonJdkFlags} ` +
-				`$javacFlags $bootClasspath $classpath ` +
+				`${config.SoongJavacWrapper} ${config.JavacWrapper}${config.JavacCmd} ${config.JavacHeapFlags} ${config.CommonJdkFlags} ` +
+				`$javacFlags $sourcepath $bootClasspath $classpath ` +
 				`-source $javaVersion -target $javaVersion ` +
 				`-d $outDir -s $annoDir @$out.rsp && ` +
 				`${config.SoongZipCmd} -jar -o $out -C $outDir -D $outDir`,
-			CommandDeps:    []string{"${config.JavacCmd}", "${config.SoongZipCmd}"},
-			Rspfile:        "$out.rsp",
-			RspfileContent: "$in",
+			CommandDeps:      []string{"${config.JavacCmd}", "${config.SoongZipCmd}"},
+			CommandOrderOnly: []string{"${config.SoongJavacWrapper}"},
+			Rspfile:          "$out.rsp",
+			RspfileContent:   "$in",
 		},
-		"javacFlags", "bootClasspath", "classpath", "outDir", "annoDir", "javaVersion")
+		"javacFlags", "sourcepath", "bootClasspath", "classpath", "outDir", "annoDir", "javaVersion")
+
+	kotlinc = pctx.AndroidGomaStaticRule("kotlinc",
+		blueprint.RuleParams{
+			// TODO(ccross): kotlinc doesn't support @ file for arguments, which will limit the
+			// maximum number of input files, especially on darwin.
+			Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
+				`${config.KotlincCmd} $classpath $kotlincFlags ` +
+				`-jvm-target $javaVersion -d $outDir $in && ` +
+				`${config.SoongZipCmd} -jar -o $out -C $outDir -D $outDir`,
+			CommandDeps: []string{
+				"${config.KotlincCmd}",
+				"${config.KotlinCompilerJar}",
+				"${config.SoongZipCmd}",
+			},
+		},
+		"kotlincFlags", "classpath", "outDir", "javaVersion")
 
 	errorprone = pctx.AndroidStaticRule("errorprone",
 		blueprint.RuleParams{
 			Command: `rm -rf "$outDir" "$annoDir" && mkdir -p "$outDir" "$annoDir" && ` +
-				`${config.ErrorProneCmd} ` +
-				`$javacFlags $bootClasspath $classpath ` +
+				`${config.SoongJavacWrapper} ${config.ErrorProneCmd} ` +
+				`$javacFlags $sourcepath $bootClasspath $classpath ` +
 				`-source $javaVersion -target $javaVersion ` +
 				`-d $outDir -s $annoDir @$out.rsp && ` +
 				`${config.SoongZipCmd} -jar -o $out -C $outDir -D $outDir`,
@@ -64,10 +81,11 @@
 				"${config.ErrorProneJar}",
 				"${config.SoongZipCmd}",
 			},
-			Rspfile:        "$out.rsp",
-			RspfileContent: "$in",
+			CommandOrderOnly: []string{"${config.SoongJavacWrapper}"},
+			Rspfile:          "$out.rsp",
+			RspfileContent:   "$in",
 		},
-		"javacFlags", "bootClasspath", "classpath", "outDir", "annoDir", "javaVersion")
+		"javacFlags", "sourcepath", "bootClasspath", "classpath", "outDir", "annoDir", "javaVersion")
 
 	jar = pctx.AndroidStaticRule("jar",
 		blueprint.RuleParams{
@@ -126,95 +144,108 @@
 	dxFlags       string
 	bootClasspath classpath
 	classpath     classpath
+	systemModules classpath
 	desugarFlags  string
 	aidlFlags     string
 	javaVersion   string
 
+	kotlincFlags     string
+	kotlincClasspath classpath
+
 	protoFlags   string
 	protoOutFlag string
 }
 
-func TransformJavaToClasses(ctx android.ModuleContext, srcFiles, srcFileLists android.Paths,
-	flags javaBuilderFlags, deps android.Paths) android.ModuleOutPath {
+func TransformKotlinToClasses(ctx android.ModuleContext, outputFile android.WritablePath,
+	srcFiles android.Paths, srcJars classpath,
+	flags javaBuilderFlags) {
 
-	classDir := android.PathForModuleOut(ctx, "classes")
-	annoDir := android.PathForModuleOut(ctx, "anno")
-	classJar := android.PathForModuleOut(ctx, "classes-compiled.jar")
+	classDir := android.PathForModuleOut(ctx, "classes-kt")
 
-	javacFlags := flags.javacFlags
-	if len(srcFileLists) > 0 {
-		javacFlags += " " + android.JoinWithPrefix(srcFileLists.Strings(), "@")
-	}
-
-	deps = append(deps, srcFileLists...)
-	deps = append(deps, flags.bootClasspath...)
-	deps = append(deps, flags.classpath...)
+	inputs := append(android.Paths(nil), srcFiles...)
+	inputs = append(inputs, srcJars...)
 
 	ctx.ModuleBuild(pctx, android.ModuleBuildParams{
-		Rule:        javac,
-		Description: "javac",
-		Output:      classJar,
-		Inputs:      srcFiles,
-		Implicits:   deps,
+		Rule:        kotlinc,
+		Description: "kotlinc",
+		Output:      outputFile,
+		Inputs:      inputs,
 		Args: map[string]string{
-			"javacFlags":    javacFlags,
-			"bootClasspath": flags.bootClasspath.JavaBootClasspath(ctx.Device()),
-			"classpath":     flags.classpath.JavaClasspath(),
-			"outDir":        classDir.String(),
-			"annoDir":       annoDir.String(),
-			"javaVersion":   flags.javaVersion,
+			"classpath":    flags.kotlincClasspath.JavaClasspath(),
+			"kotlincFlags": flags.kotlincFlags,
+			"outDir":       classDir.String(),
+			"javaVersion":  flags.javaVersion,
 		},
 	})
-
-	return classJar
 }
 
-func RunErrorProne(ctx android.ModuleContext, srcFiles, srcFileLists android.Paths,
-	flags javaBuilderFlags) android.Path {
+func TransformJavaToClasses(ctx android.ModuleContext, outputFile android.WritablePath,
+	srcFiles android.Paths, srcJars classpath,
+	flags javaBuilderFlags, deps android.Paths) {
+
+	transformJavaToClasses(ctx, outputFile, srcFiles, srcJars, flags, deps,
+		"", "javac", javac)
+}
+
+func RunErrorProne(ctx android.ModuleContext, outputFile android.WritablePath,
+	srcFiles android.Paths, srcJars classpath,
+	flags javaBuilderFlags) {
 
 	if config.ErrorProneJar == "" {
 		ctx.ModuleErrorf("cannot build with Error Prone, missing external/error_prone?")
-		return nil
 	}
 
-	classDir := android.PathForModuleOut(ctx, "classes-errorprone")
-	annoDir := android.PathForModuleOut(ctx, "anno-errorprone")
-	classFileList := android.PathForModuleOut(ctx, "classes-errorprone.list")
+	transformJavaToClasses(ctx, outputFile, srcFiles, srcJars, flags, nil,
+		"-errorprone", "errorprone", errorprone)
+}
 
-	javacFlags := flags.javacFlags
-	if len(srcFileLists) > 0 {
-		javacFlags += " " + android.JoinWithPrefix(srcFileLists.Strings(), "@")
+// transformJavaToClasses takes source files and converts them to a jar containing .class files.
+// srcFiles is a list of paths to sources, srcJars is a list of paths to jar files that contain
+// sources.  flags contains various command line flags to be passed to the compiler.
+//
+// This method may be used for different compilers, including javac and Error Prone.  The rule
+// argument specifies which command line to use and desc sets the description of the rule that will
+// be printed at build time.  The stem argument provides the file name of the output jar, and
+// suffix will be appended to various intermediate files and directories to avoid collisions when
+// this function is called twice in the same module directory.
+func transformJavaToClasses(ctx android.ModuleContext, outputFile android.WritablePath,
+	srcFiles android.Paths, srcJars classpath,
+	flags javaBuilderFlags, deps android.Paths,
+	intermediatesSuffix, desc string, rule blueprint.Rule) {
+
+	deps = append(deps, srcJars...)
+
+	var bootClasspath string
+	if flags.javaVersion == "1.9" {
+		deps = append(deps, flags.systemModules...)
+		bootClasspath = flags.systemModules.JavaSystemModules(ctx.Device())
+	} else {
+		deps = append(deps, flags.bootClasspath...)
+		bootClasspath = flags.bootClasspath.JavaBootClasspath(ctx.Device())
 	}
 
-	var deps android.Paths
-
-	deps = append(deps, srcFileLists...)
-	deps = append(deps, flags.bootClasspath...)
 	deps = append(deps, flags.classpath...)
 
 	ctx.ModuleBuild(pctx, android.ModuleBuildParams{
-		Rule:        errorprone,
-		Description: "errorprone",
-		Output:      classFileList,
+		Rule:        rule,
+		Description: desc,
+		Output:      outputFile,
 		Inputs:      srcFiles,
 		Implicits:   deps,
 		Args: map[string]string{
-			"javacFlags":    javacFlags,
-			"bootClasspath": flags.bootClasspath.JavaBootClasspath(ctx.Device()),
+			"javacFlags":    flags.javacFlags,
+			"bootClasspath": bootClasspath,
+			"sourcepath":    srcJars.JavaSourcepath(),
 			"classpath":     flags.classpath.JavaClasspath(),
-			"outDir":        classDir.String(),
-			"annoDir":       annoDir.String(),
+			"outDir":        android.PathForModuleOut(ctx, "classes"+intermediatesSuffix).String(),
+			"annoDir":       android.PathForModuleOut(ctx, "anno"+intermediatesSuffix).String(),
 			"javaVersion":   flags.javaVersion,
 		},
 	})
-
-	return classFileList
 }
 
-func TransformResourcesToJar(ctx android.ModuleContext, jarArgs []string,
-	deps android.Paths) android.Path {
-
-	outputFile := android.PathForModuleOut(ctx, "res.jar")
+func TransformResourcesToJar(ctx android.ModuleContext, outputFile android.WritablePath,
+	jarArgs []string, deps android.Paths) {
 
 	ctx.ModuleBuild(pctx, android.ModuleBuildParams{
 		Rule:        jar,
@@ -225,18 +256,10 @@
 			"jarArgs": strings.Join(jarArgs, " "),
 		},
 	})
-
-	return outputFile
 }
 
-func TransformJarsToJar(ctx android.ModuleContext, stem string, jars android.Paths,
-	manifest android.OptionalPath, stripDirs bool) android.Path {
-
-	outputFile := android.PathForModuleOut(ctx, stem)
-
-	if len(jars) == 1 && !manifest.Valid() {
-		return jars[0]
-	}
+func TransformJarsToJar(ctx android.ModuleContext, outputFile android.WritablePath,
+	jars android.Paths, manifest android.OptionalPath, stripDirs bool) {
 
 	var deps android.Paths
 
@@ -260,18 +283,15 @@
 			"jarArgs": strings.Join(jarArgs, " "),
 		},
 	})
-
-	return outputFile
 }
 
-func TransformDesugar(ctx android.ModuleContext, classesJar android.Path,
-	flags javaBuilderFlags) android.Path {
+func TransformDesugar(ctx android.ModuleContext, outputFile android.WritablePath,
+	classesJar android.Path, flags javaBuilderFlags) {
 
-	outputFile := android.PathForModuleOut(ctx, "classes-desugar.jar")
 	dumpDir := android.PathForModuleOut(ctx, "desugar_dumped_classes")
 
 	javaFlags := ""
-	if ctx.AConfig().Getenv("EXPERIMENTAL_USE_OPENJDK9") != "" {
+	if ctx.AConfig().UseOpenJDK9() {
 		javaFlags = "--add-opens java.base/java.lang.invoke=ALL-UNNAMED"
 	}
 
@@ -296,17 +316,14 @@
 			"desugarFlags":   flags.desugarFlags,
 		},
 	})
-
-	return outputFile
 }
 
 // Converts a classes.jar file to classes*.dex, then combines the dex files with any resources
 // in the classes.jar file into a dex jar.
-func TransformClassesJarToDexJar(ctx android.ModuleContext, stem string, classesJar android.Path,
-	flags javaBuilderFlags) android.Path {
+func TransformClassesJarToDexJar(ctx android.ModuleContext, outputFile android.WritablePath,
+	classesJar android.Path, flags javaBuilderFlags) {
 
 	outDir := android.PathForModuleOut(ctx, "dex")
-	outputFile := android.PathForModuleOut(ctx, stem)
 
 	ctx.ModuleBuild(pctx, android.ModuleBuildParams{
 		Rule:        dx,
@@ -318,12 +335,10 @@
 			"outDir":  outDir.String(),
 		},
 	})
-
-	return outputFile
 }
 
-func TransformJarJar(ctx android.ModuleContext, classesJar android.Path, rulesFile android.Path) android.ModuleOutPath {
-	outputFile := android.PathForModuleOut(ctx, "classes-jarjar.jar")
+func TransformJarJar(ctx android.ModuleContext, outputFile android.WritablePath,
+	classesJar android.Path, rulesFile android.Path) {
 	ctx.ModuleBuild(pctx, android.ModuleBuildParams{
 		Rule:        jarjar,
 		Description: "jarjar",
@@ -334,12 +349,20 @@
 			"rulesFile": rulesFile.String(),
 		},
 	})
-
-	return outputFile
 }
 
 type classpath []android.Path
 
+// Returns a -sourcepath argument in the form javac expects.  If the list is empty returns
+// -sourcepath "" to ensure javac does not fall back to searching the classpath for sources.
+func (x *classpath) JavaSourcepath() string {
+	if len(*x) > 0 {
+		return "-sourcepath " + strings.Join(x.Strings(), ":")
+	} else {
+		return `-sourcepath ""`
+	}
+}
+
 // Returns a -classpath argument in the form java or javac expects
 func (x *classpath) JavaClasspath() string {
 	if len(*x) > 0 {
@@ -371,6 +394,21 @@
 	}
 }
 
+// Returns a --system argument in the form javac expects with -source 1.9.  If forceEmpty is true,
+// returns --system=none if the list is empty to ensure javac does not fall back to the default
+// system modules.
+func (x *classpath) JavaSystemModules(forceEmpty bool) string {
+	if len(*x) > 1 {
+		panic("more than one system module")
+	} else if len(*x) == 1 {
+		return "--system=" + strings.TrimSuffix((*x)[0].String(), "lib/modules")
+	} else if forceEmpty {
+		return "--system=none"
+	} else {
+		return ""
+	}
+}
+
 func (x *classpath) DesugarBootClasspath() []string {
 	if x == nil || *x == nil {
 		return nil
diff --git a/java/config/config.go b/java/config/config.go
index 4f74ef2..eb71ddb 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -15,7 +15,6 @@
 package config
 
 import (
-	"path/filepath"
 	"strings"
 
 	_ "github.com/google/blueprint/bootstrap"
@@ -27,6 +26,7 @@
 	pctx = android.NewPackageContext("android/soong/java/config")
 
 	DefaultBootclasspathLibraries = []string{"core-oj", "core-libart"}
+	DefaultSystemModules          = "core-system-modules"
 	DefaultLibraries              = []string{"ext", "framework", "okhttp"}
 )
 
@@ -47,14 +47,23 @@
 		// If a different javac is used the flag will be ignored and extra bridges will be inserted.
 		// The flag is implemented by https://android-review.googlesource.com/c/486427
 		`-XDskipDuplicateBridges=true`,
-	}, " "))
 
-	pctx.StaticVariable("DefaultJavaVersion", "1.8")
+		// b/65004097: prevent using java.lang.invoke.StringConcatFactory when using -target 1.9
+		`-XDstringConcat=inline`,
+	}, " "))
 
 	pctx.VariableConfigMethod("hostPrebuiltTag", android.Config.PrebuiltOS)
 
-	pctx.SourcePathVariableWithEnvOverride("JavaHome",
-		"prebuilts/jdk/jdk8/${hostPrebuiltTag}", "OVERRIDE_ANDROID_JAVA_HOME")
+	pctx.VariableFunc("JavaHome", func(config interface{}) (string, error) {
+		if override := config.(android.Config).Getenv("OVERRIDE_ANDROID_JAVA_HOME"); override != "" {
+			return override, nil
+		}
+		if config.(android.Config).UseOpenJDK9() {
+			return "prebuilts/jdk/jdk9/${hostPrebuiltTag}", nil
+		}
+		return "prebuilts/jdk/jdk8/${hostPrebuiltTag}", nil
+	})
+
 	pctx.SourcePathVariable("JavaToolchain", "${JavaHome}/bin")
 	pctx.SourcePathVariableWithEnvOverride("JavacCmd",
 		"${JavaToolchain}/javac", "ALTERNATE_JAVAC")
@@ -63,14 +72,17 @@
 	pctx.SourcePathVariable("JavadocCmd", "${JavaToolchain}/javadoc")
 	pctx.SourcePathVariable("JlinkCmd", "${JavaToolchain}/jlink")
 	pctx.SourcePathVariable("JmodCmd", "${JavaToolchain}/jmod")
+	pctx.SourcePathVariable("JrtFsJar", "${JavaHome}/lib/jrt-fs.jar")
 
 	pctx.SourcePathVariable("JarArgsCmd", "build/soong/scripts/jar-args.sh")
-	pctx.StaticVariable("SoongZipCmd", filepath.Join("${bootstrap.ToolDir}", "soong_zip"))
-	pctx.StaticVariable("MergeZipsCmd", filepath.Join("${bootstrap.ToolDir}", "merge_zips"))
+	pctx.HostBinToolVariable("SoongZipCmd", "soong_zip")
+	pctx.HostBinToolVariable("MergeZipsCmd", "merge_zips")
 	pctx.HostBinToolVariable("DxCmd", "dx")
 	pctx.HostJavaToolVariable("JarjarCmd", "jarjar.jar")
 	pctx.HostJavaToolVariable("DesugarJar", "desugar.jar")
 
+	pctx.HostBinToolVariable("SoongJavacWrapper", "soong_javac_wrapper")
+
 	pctx.VariableFunc("JavacWrapper", func(config interface{}) (string, error) {
 		if override := config.(android.Config).Getenv("JAVAC_WRAPPER"); override != "" {
 			return override + " ", nil
@@ -78,17 +90,3 @@
 		return "", nil
 	})
 }
-
-func StripJavac9Flags(flags []string) []string {
-	var ret []string
-	for _, f := range flags {
-		switch {
-		case strings.HasPrefix(f, "-J--add-modules="):
-			// drop
-		default:
-			ret = append(ret, f)
-		}
-	}
-
-	return ret
-}
diff --git a/java/config/kotlin.go b/java/config/kotlin.go
new file mode 100644
index 0000000..35f9e9d
--- /dev/null
+++ b/java/config/kotlin.go
@@ -0,0 +1,25 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+var (
+	KotlinStdlibJar = "external/kotlinc/lib/kotlin-stdlib.jar"
+)
+
+func init() {
+	pctx.SourcePathVariable("KotlincCmd", "external/kotlinc/bin/kotlinc")
+	pctx.SourcePathVariable("KotlinCompilerJar", "external/kotlinc/lib/kotlin-compiler.jar")
+	pctx.SourcePathVariable("KotlinStdlibJar", KotlinStdlibJar)
+}
diff --git a/java/config/makevars.go b/java/config/makevars.go
index 937d597..6b49592 100644
--- a/java/config/makevars.go
+++ b/java/config/makevars.go
@@ -27,8 +27,13 @@
 func makeVarsProvider(ctx android.MakeVarsContext) {
 	ctx.Strict("TARGET_DEFAULT_JAVA_LIBRARIES", strings.Join(DefaultLibraries, " "))
 	ctx.Strict("TARGET_DEFAULT_BOOTCLASSPATH_LIBRARIES", strings.Join(DefaultBootclasspathLibraries, " "))
+	ctx.Strict("DEFAULT_SYSTEM_MODULES", DefaultSystemModules)
 
-	ctx.Strict("DEFAULT_JAVA_LANGUAGE_VERSION", "${DefaultJavaVersion}")
+	if ctx.Config().TargetOpenJDK9() {
+		ctx.Strict("DEFAULT_JAVA_LANGUAGE_VERSION", "1.9")
+	} else {
+		ctx.Strict("DEFAULT_JAVA_LANGUAGE_VERSION", "1.8")
+	}
 
 	ctx.Strict("ANDROID_JAVA_HOME", "${JavaHome}")
 	ctx.Strict("ANDROID_JAVA_TOOLCHAIN", "${JavaToolchain}")
@@ -47,8 +52,10 @@
 		ctx.Strict("HOST_JAVAC", "${JavacCmd} ${CommonJdkFlags}")
 	}
 
-	if ctx.Config().IsEnvTrue("EXPERIMENTAL_USE_OPENJDK9") {
+	if ctx.Config().UseOpenJDK9() {
 		ctx.Strict("JLINK", "${JlinkCmd}")
 		ctx.Strict("JMOD", "${JmodCmd}")
 	}
+
+	ctx.Strict("SOONG_JAVAC_WRAPPER", "${SoongJavacWrapper}")
 }
diff --git a/java/gen.go b/java/gen.go
index e55be91..e12a71c 100644
--- a/java/gen.go
+++ b/java/gen.go
@@ -85,7 +85,7 @@
 }
 
 func (j *Module) genSources(ctx android.ModuleContext, srcFiles android.Paths,
-	flags javaBuilderFlags) (android.Paths, android.Paths) {
+	flags javaBuilderFlags) (android.Paths, classpath) {
 
 	var protoFiles android.Paths
 	outSrcFiles := make(android.Paths, 0, len(srcFiles))
@@ -106,16 +106,17 @@
 		}
 	}
 
-	var outSrcFileLists android.Paths
+	var outSrcJars classpath
 
 	if len(protoFiles) > 0 {
-		protoFileList := genProto(ctx, protoFiles,
+		protoSrcJar := android.PathForModuleGen(ctx, "proto.src.jar")
+		genProto(ctx, protoSrcJar, protoFiles,
 			flags.protoFlags, flags.protoOutFlag, "")
 
-		outSrcFileLists = append(outSrcFileLists, protoFileList)
+		outSrcJars = append(outSrcJars, protoSrcJar)
 	}
 
-	return outSrcFiles, outSrcFileLists
+	return outSrcFiles, outSrcJars
 }
 
 func LogtagsSingleton() blueprint.Singleton {
diff --git a/java/java.go b/java/java.go
index 3726435..76e574e 100644
--- a/java/java.go
+++ b/java/java.go
@@ -28,7 +28,6 @@
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
-	"android/soong/genrule"
 	"android/soong/java/config"
 )
 
@@ -101,7 +100,7 @@
 	Manifest *string
 
 	// if not blank, run jarjar using the specified rules file
-	Jarjar_rules *string
+	Jarjar_rules *string `android:"arch_variant"`
 
 	// If not blank, set the java version passed to javac as -source and -target
 	Java_version *string
@@ -117,6 +116,14 @@
 
 	// List of classes to pass to javac to use as annotation processors
 	Annotation_processor_classes []string
+
+	Openjdk9 struct {
+		// List of source files that should only be used when passing -source 1.9
+		Srcs []string
+
+		// List of javac flags that should only be used when passing -source 1.9
+		Javacflags []string
+	}
 }
 
 type CompilerDeviceProperties struct {
@@ -126,16 +133,18 @@
 	// if not blank, set to the version of the sdk to compile against
 	Sdk_version string
 
-	// Set for device java libraries, and for host versions of device java libraries
-	// built for testing
-	Dex bool `blueprint:"mutated"`
-
 	// directories to pass to aidl tool
 	Aidl_includes []string
 
 	// directories that should be added as include directories
 	// for any aidl sources of modules that depend on this module
 	Export_aidl_include_dirs []string
+
+	// If true, export a copy of the module as a -hostdex module for host testing.
+	Hostdex *bool
+
+	// When targeting 1.9, override the modules to use with --system
+	System_modules *string
 }
 
 // Module contains the properties and members used by all java module types
@@ -160,9 +169,9 @@
 
 	logtagsSrcs android.Paths
 
-	// filelists of extra source files that should be included in the javac command line,
+	// jars containing source files that should be included in the javac command line,
 	// for example R.java generated by aapt for android apps
-	ExtraSrcLists android.Paths
+	ExtraSrcJars android.Paths
 
 	// installed file for binary dependency
 	installFile android.Path
@@ -187,26 +196,40 @@
 	staticLibTag     = dependencyTag{name: "staticlib"}
 	libTag           = dependencyTag{name: "javalib"}
 	bootClasspathTag = dependencyTag{name: "bootclasspath"}
+	systemModulesTag = dependencyTag{name: "system modules"}
 	frameworkResTag  = dependencyTag{name: "framework-res"}
+	kotlinStdlibTag  = dependencyTag{name: "kotlin-stdlib"}
 )
 
 type sdkDep struct {
 	useModule, useFiles, useDefaultLibs, invalidVersion bool
 
-	module string
-	jar    android.Path
-	aidl   android.Path
+	module        string
+	systemModules string
+
+	jar  android.Path
+	aidl android.Path
+}
+
+func sdkStringToNumber(ctx android.BaseContext, v string) int {
+	switch v {
+	case "", "current", "system_current", "test_current":
+		return 10000
+	default:
+		if i, err := strconv.Atoi(v); err != nil {
+			ctx.PropertyErrorf("sdk_version", "invalid sdk version")
+			return -1
+		} else {
+			return i
+		}
+	}
 }
 
 func decodeSdkDep(ctx android.BaseContext, v string) sdkDep {
-	switch v {
-	case "", "current", "system_current", "test_current":
-		// OK
-	default:
-		if _, err := strconv.Atoi(v); err != nil {
-			ctx.PropertyErrorf("sdk_version", "invalid sdk version")
-			return sdkDep{}
-		}
+	i := sdkStringToNumber(ctx, v)
+	if i == -1 {
+		// Invalid sdk version, error handled by sdkStringToNumber.
+		return sdkDep{}
 	}
 
 	toFile := func(v string) sdkDep {
@@ -242,8 +265,9 @@
 
 	toModule := func(m string) sdkDep {
 		return sdkDep{
-			useModule: true,
-			module:    m,
+			useModule:     true,
+			module:        m,
+			systemModules: m + "_system_modules",
 		}
 	}
 
@@ -268,22 +292,31 @@
 }
 
 func (j *Module) deps(ctx android.BottomUpMutatorContext) {
-	if !proptools.Bool(j.properties.No_standard_libs) {
-		if ctx.Device() {
+	if ctx.Device() {
+		if !proptools.Bool(j.properties.No_standard_libs) {
 			sdkDep := decodeSdkDep(ctx, j.deviceProperties.Sdk_version)
 			if sdkDep.useDefaultLibs {
 				ctx.AddDependency(ctx.Module(), bootClasspathTag, config.DefaultBootclasspathLibraries...)
+				if ctx.AConfig().TargetOpenJDK9() {
+					ctx.AddDependency(ctx.Module(), systemModulesTag, config.DefaultSystemModules)
+				}
 				if !proptools.Bool(j.properties.No_framework_libs) {
 					ctx.AddDependency(ctx.Module(), libTag, config.DefaultLibraries...)
 				}
-			}
-			if sdkDep.useModule {
+			} else if sdkDep.useModule {
+				if ctx.AConfig().TargetOpenJDK9() {
+					ctx.AddDependency(ctx.Module(), systemModulesTag, sdkDep.systemModules)
+				}
 				ctx.AddDependency(ctx.Module(), bootClasspathTag, sdkDep.module)
 			}
-		} else {
-			// TODO(ccross): add hostdex support
+		} else if j.deviceProperties.System_modules == nil {
+			ctx.PropertyErrorf("no_standard_libs",
+				"system_modules is required to be set when no_standard_libs is true, did you mean no_framework_libs?")
+		} else if *j.deviceProperties.System_modules != "none" && ctx.AConfig().TargetOpenJDK9() {
+			ctx.AddDependency(ctx.Module(), systemModulesTag, *j.deviceProperties.System_modules)
 		}
 	}
+
 	ctx.AddDependency(ctx.Module(), libTag, j.properties.Libs...)
 	ctx.AddDependency(ctx.Module(), staticLibTag, j.properties.Static_libs...)
 	ctx.AddDependency(ctx.Module(), libTag, j.properties.Annotation_processors...)
@@ -294,6 +327,12 @@
 	if j.hasSrcExt(".proto") {
 		protoDeps(ctx, &j.protoProperties)
 	}
+
+	if j.hasSrcExt(".kt") {
+		// TODO(ccross): move this to a mutator pass that can tell if generated sources contain
+		// Kotlin files
+		ctx.AddDependency(ctx.Module(), kotlinStdlibTag, "kotlin-stdlib")
+	}
 }
 
 func hasSrcExt(srcs []string, ext string) bool {
@@ -338,8 +377,10 @@
 	staticJars         android.Paths
 	staticJarResources android.Paths
 	aidlIncludeDirs    android.Paths
-	srcFileLists       android.Paths
+	srcJars            android.Paths
+	systemModules      android.Path
 	aidlPreprocess     android.OptionalPath
+	kotlinStdlib       android.Paths
 }
 
 func (j *Module) collectDeps(ctx android.ModuleContext) deps {
@@ -362,6 +403,15 @@
 			switch tag {
 			case android.DefaultsDepTag, android.SourceDepTag:
 				// Nothing to do
+			case systemModulesTag:
+				if deps.systemModules != nil {
+					panic("Found two system module dependencies")
+				}
+				sm := module.(*SystemModules)
+				if sm.outputFile == nil {
+					panic("Missing directory for system module dependency")
+				}
+				deps.systemModules = sm.outputFile
 			default:
 				ctx.ModuleErrorf("depends on non-java module %q", otherName)
 			}
@@ -380,8 +430,10 @@
 			if ctx.ModuleName() == "framework" {
 				// framework.jar has a one-off dependency on the R.java and Manifest.java files
 				// generated by framework-res.apk
-				deps.srcFileLists = append(deps.srcFileLists, module.(*AndroidApp).aaptJavaFileList)
+				// TODO(ccross): aapt java files should go in a src jar
 			}
+		case kotlinStdlibTag:
+			deps.kotlinStdlib = dep.ClasspathFiles()
 		default:
 			panic(fmt.Errorf("unknown dependency %q for %q", otherName, ctx.ModuleName()))
 		}
@@ -401,19 +453,29 @@
 	var flags javaBuilderFlags
 
 	javacFlags := j.properties.Javacflags
-	if ctx.AConfig().Getenv("EXPERIMENTAL_USE_OPENJDK9") == "" {
-		javacFlags = config.StripJavac9Flags(javacFlags)
+	if ctx.AConfig().TargetOpenJDK9() {
+		javacFlags = append(javacFlags, j.properties.Openjdk9.Javacflags...)
+		j.properties.Srcs = append(j.properties.Srcs, j.properties.Openjdk9.Srcs...)
 	}
 
+	sdk := sdkStringToNumber(ctx, j.deviceProperties.Sdk_version)
 	if j.properties.Java_version != nil {
 		flags.javaVersion = *j.properties.Java_version
+	} else if ctx.Device() && sdk <= 23 {
+		flags.javaVersion = "1.7"
+	} else if ctx.Device() && sdk <= 26 || !ctx.AConfig().TargetOpenJDK9() {
+		flags.javaVersion = "1.8"
 	} else {
-		flags.javaVersion = "${config.DefaultJavaVersion}"
+		flags.javaVersion = "1.9"
 	}
 
 	flags.bootClasspath.AddPaths(deps.bootClasspath)
 	flags.classpath.AddPaths(deps.classpath)
 
+	if deps.systemModules != nil {
+		flags.systemModules = append(flags.systemModules, deps.systemModules)
+	}
+
 	if len(javacFlags) > 0 {
 		ctx.Variable(pctx, "javacFlags", strings.Join(javacFlags, " "))
 		flags.javacFlags = "$javacFlags"
@@ -431,23 +493,42 @@
 		flags = protoFlags(ctx, &j.protoProperties, flags)
 	}
 
-	var srcFileLists android.Paths
+	var srcJars classpath
+	srcFiles, srcJars = j.genSources(ctx, srcFiles, flags)
 
-	srcFiles, srcFileLists = j.genSources(ctx, srcFiles, flags)
+	srcJars = append(srcJars, deps.srcJars...)
 
-	srcFileLists = append(srcFileLists, deps.srcFileLists...)
-
-	ctx.VisitDirectDeps(func(module blueprint.Module) {
-		if gen, ok := module.(genrule.SourceFileGenerator); ok {
-			srcFiles = append(srcFiles, gen.GeneratedSourceFiles()...)
-		}
-	})
-
-	srcFileLists = append(srcFileLists, j.ExtraSrcLists...)
+	srcJars = append(srcJars, j.ExtraSrcJars...)
 
 	var jars android.Paths
 
-	if len(srcFiles) > 0 {
+	if srcFiles.HasExt(".kt") {
+		// If there are kotlin files, compile them first but pass all the kotlin and java files
+		// kotlinc will use the java files to resolve types referenced by the kotlin files, but
+		// won't emit any classes for them.
+
+		flags.kotlincFlags = "-no-stdlib"
+		if ctx.Device() {
+			flags.kotlincFlags += " -no-jdk"
+		}
+
+		flags.kotlincClasspath = append(flags.kotlincClasspath, deps.kotlinStdlib...)
+		flags.kotlincClasspath = append(flags.kotlincClasspath, deps.classpath...)
+
+		kotlinJar := android.PathForModuleOut(ctx, "classes-kt.jar")
+		TransformKotlinToClasses(ctx, kotlinJar, srcFiles, srcJars, flags)
+		if ctx.Failed() {
+			return
+		}
+
+		// Make javac rule depend on the kotlinc rule
+		flags.classpath = append(flags.classpath, kotlinJar)
+		// Jar kotlin classes into the final jar after javac
+		jars = append(jars, kotlinJar)
+		jars = append(jars, deps.kotlinStdlib...)
+	}
+
+	if javaSrcFiles := srcFiles.FilterByExt(".java"); len(javaSrcFiles) > 0 {
 		var extraJarDeps android.Paths
 		if ctx.AConfig().IsEnvTrue("RUN_ERROR_PRONE") {
 			// If error-prone is enabled, add an additional rule to compile the java files into
@@ -455,12 +536,14 @@
 			// a rebuild when error-prone is turned off).
 			// TODO(ccross): Once we always compile with javac9 we may be able to conditionally
 			//    enable error-prone without affecting the output class files.
-			errorprone := RunErrorProne(ctx, srcFiles, srcFileLists, flags)
+			errorprone := android.PathForModuleOut(ctx, "classes-errorprone.list")
+			RunErrorProne(ctx, errorprone, javaSrcFiles, srcJars, flags)
 			extraJarDeps = append(extraJarDeps, errorprone)
 		}
 
 		// Compile java sources into .class files
-		classes := TransformJavaToClasses(ctx, srcFiles, srcFileLists, flags, extraJarDeps)
+		classes := android.PathForModuleOut(ctx, "classes-compiled.jar")
+		TransformJavaToClasses(ctx, classes, javaSrcFiles, srcJars, flags, extraJarDeps)
 		if ctx.Failed() {
 			return
 		}
@@ -481,13 +564,14 @@
 	resDeps = append(resDeps, fileDeps...)
 
 	if proptools.Bool(j.properties.Include_srcs) {
-		srcArgs, srcDeps := ResourceFilesToJarArgs(ctx, j.properties.Srcs, j.properties.Exclude_srcs)
+		srcArgs, srcDeps := SourceFilesToJarArgs(ctx, j.properties.Srcs, j.properties.Exclude_srcs)
 		resArgs = append(resArgs, srcArgs...)
 		resDeps = append(resDeps, srcDeps...)
 	}
 
 	if len(resArgs) > 0 {
-		resourceJar := TransformResourcesToJar(ctx, resArgs, resDeps)
+		resourceJar := android.PathForModuleOut(ctx, "res.jar")
+		TransformResourcesToJar(ctx, resourceJar, resArgs, resDeps)
 		if ctx.Failed() {
 			return
 		}
@@ -502,12 +586,23 @@
 
 	// Combine the classes built from sources, any manifests, and any static libraries into
 	// classes.jar.  If there is only one input jar this step will be skipped.
-	outputFile := TransformJarsToJar(ctx, "classes.jar", jars, manifest, false)
+	var outputFile android.Path
+
+	if len(jars) == 1 && !manifest.Valid() {
+		// Optimization: skip the combine step if there is nothing to do
+		outputFile = jars[0]
+	} else {
+		combinedJar := android.PathForModuleOut(ctx, "classes.jar")
+		TransformJarsToJar(ctx, combinedJar, jars, manifest, false)
+		outputFile = combinedJar
+	}
 
 	if j.properties.Jarjar_rules != nil {
 		jarjar_rules := android.PathForModuleSrc(ctx, *j.properties.Jarjar_rules)
 		// Transform classes.jar into classes-jarjar.jar
-		outputFile = TransformJarJar(ctx, outputFile, jarjar_rules)
+		jarjarFile := android.PathForModuleOut(ctx, "classes-jarjar.jar")
+		TransformJarJar(ctx, jarjarFile, outputFile, jarjar_rules)
+		outputFile = jarjarFile
 		if ctx.Failed() {
 			return
 		}
@@ -515,7 +610,6 @@
 
 	j.classpathFile = outputFile
 
-	// TODO(ccross): handle hostdex
 	if ctx.Device() && j.installable() {
 		dxFlags := j.deviceProperties.Dxflags
 		if false /* emma enabled */ {
@@ -564,13 +658,17 @@
 
 		flags.desugarFlags = strings.Join(desugarFlags, " ")
 
-		desugarJar := TransformDesugar(ctx, outputFile, flags)
+		desugarJar := android.PathForModuleOut(ctx, "classes-desugar.jar")
+		TransformDesugar(ctx, desugarJar, outputFile, flags)
+		outputFile = desugarJar
 		if ctx.Failed() {
 			return
 		}
 
 		// Compile classes.jar into classes.dex and then javalib.jar
-		outputFile = TransformClassesJarToDexJar(ctx, "javalib.jar", desugarJar, flags)
+		javalibJar := android.PathForModuleOut(ctx, "javalib.jar")
+		TransformClassesJarToDexJar(ctx, javalibJar, desugarJar, flags)
+		outputFile = javalibJar
 		if ctx.Failed() {
 			return
 		}
@@ -629,7 +727,6 @@
 		if !installable {
 			module.properties.Installable = proptools.BoolPtr(false)
 		}
-		module.deviceProperties.Dex = true
 
 		module.AddProperties(
 			&module.Module.properties,
@@ -687,8 +784,6 @@
 func BinaryFactory() android.Module {
 	module := &Binary{}
 
-	module.deviceProperties.Dex = true
-
 	module.AddProperties(
 		&module.Module.properties,
 		&module.Module.deviceProperties,
@@ -748,7 +843,9 @@
 func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	j.classpathFiles = android.PathsForModuleSrc(ctx, j.properties.Jars)
 
-	j.combinedClasspathFile = TransformJarsToJar(ctx, "classes.jar", j.classpathFiles, android.OptionalPath{}, false)
+	outputFile := android.PathForModuleOut(ctx, "classes.jar")
+	TransformJarsToJar(ctx, outputFile, j.classpathFiles, android.OptionalPath{}, false)
+	j.combinedClasspathFile = outputFile
 }
 
 var _ Dependency = (*Import)(nil)
diff --git a/java/java_test.go b/java/java_test.go
index a4a3f52..d64688f 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -50,9 +50,12 @@
 
 	os.Exit(run())
 }
-
 func testJava(t *testing.T, bp string) *android.TestContext {
-	config := android.TestArchConfig(buildDir)
+	return testJavaWithEnv(t, bp, nil)
+}
+
+func testJavaWithEnv(t *testing.T, bp string, env map[string]string) *android.TestContext {
+	config := android.TestArchConfig(buildDir, env)
 
 	ctx := android.NewTestArchContext()
 	ctx.RegisterModuleType("android_app", android.ModuleFactoryAdaptor(AndroidAppFactory))
@@ -60,7 +63,9 @@
 	ctx.RegisterModuleType("java_library_host", android.ModuleFactoryAdaptor(LibraryHostFactory))
 	ctx.RegisterModuleType("java_import", android.ModuleFactoryAdaptor(ImportFactory))
 	ctx.RegisterModuleType("java_defaults", android.ModuleFactoryAdaptor(defaultsFactory))
+	ctx.RegisterModuleType("java_system_modules", android.ModuleFactoryAdaptor(SystemModulesFactory))
 	ctx.RegisterModuleType("filegroup", android.ModuleFactoryAdaptor(genrule.FileGroupFactory))
+	ctx.RegisterModuleType("genrule", android.ModuleFactoryAdaptor(genrule.GenRuleFactory))
 	ctx.PreArchMutators(android.RegisterPrebuiltsPreArchMutators)
 	ctx.PreArchMutators(android.RegisterPrebuiltsPostDepsMutators)
 	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
@@ -75,6 +80,7 @@
 		"android_stubs_current",
 		"android_system_stubs_current",
 		"android_test_stubs_current",
+		"kotlin-stdlib",
 	}
 
 	for _, extra := range extraModules {
@@ -83,15 +89,34 @@
 				name: "%s",
 				srcs: ["a.java"],
 				no_standard_libs: true,
+				system_modules: "core-system-modules",
 			}
 		`, extra)
 	}
 
+	if config.TargetOpenJDK9() {
+		systemModules := []string{
+			"core-system-modules",
+			"android_stubs_current_system_modules",
+			"android_system_stubs_current_system_modules",
+			"android_test_stubs_current_system_modules",
+		}
+
+		for _, extra := range systemModules {
+			bp += fmt.Sprintf(`
+			java_system_modules {
+				name: "%s",
+			}
+		`, extra)
+		}
+	}
+
 	ctx.MockFileSystem(map[string][]byte{
 		"Android.bp": []byte(bp),
 		"a.java":     nil,
 		"b.java":     nil,
 		"c.java":     nil,
+		"b.kt":       nil,
 		"a.jar":      nil,
 		"b.jar":      nil,
 		"res/a":      nil,
@@ -189,17 +214,20 @@
 	host          android.OsClass
 	properties    string
 	bootclasspath []string
+	system        string
 	classpath     []string
 }{
 	{
 		name:          "default",
 		bootclasspath: []string{"core-oj", "core-libart"},
+		system:        "core-system-modules",
 		classpath:     []string{"ext", "framework", "okhttp"},
 	},
 	{
 		name:          "blank sdk version",
 		properties:    `sdk_version: "",`,
 		bootclasspath: []string{"core-oj", "core-libart"},
+		system:        "core-system-modules",
 		classpath:     []string{"ext", "framework", "okhttp"},
 	},
 	{
@@ -207,6 +235,7 @@
 		name:          "sdk v14",
 		properties:    `sdk_version: "14",`,
 		bootclasspath: []string{`""`},
+		system:        "bootclasspath", // special value to tell 1.9 test to expect bootclasspath
 		classpath:     []string{"prebuilts/sdk/14/android.jar"},
 	},
 	{
@@ -214,6 +243,7 @@
 		name:          "current",
 		properties:    `sdk_version: "current",`,
 		bootclasspath: []string{"android_stubs_current"},
+		system:        "android_stubs_current_system_modules",
 		classpath:     []string{},
 	},
 	{
@@ -221,6 +251,7 @@
 		name:          "system_current",
 		properties:    `sdk_version: "system_current",`,
 		bootclasspath: []string{"android_system_stubs_current"},
+		system:        "android_system_stubs_current_system_modules",
 		classpath:     []string{},
 	},
 	{
@@ -228,12 +259,22 @@
 		name:          "test_current",
 		properties:    `sdk_version: "test_current",`,
 		bootclasspath: []string{"android_test_stubs_current"},
+		system:        "android_test_stubs_current_system_modules",
 		classpath:     []string{},
 	},
 	{
 
 		name:          "nostdlib",
-		properties:    `no_standard_libs: true`,
+		properties:    `no_standard_libs: true, system_modules: "none"`,
+		system:        "none",
+		bootclasspath: []string{`""`},
+		classpath:     []string{},
+	},
+	{
+
+		name:          "nostdlib system_modules",
+		properties:    `no_standard_libs: true, system_modules: "core-system-modules"`,
+		system:        "core-system-modules",
 		bootclasspath: []string{`""`},
 		classpath:     []string{},
 	},
@@ -262,7 +303,7 @@
 	{
 		name:       "host supported nostdlib",
 		host:       android.Host,
-		properties: `host_supported: true, no_standard_libs: true`,
+		properties: `host_supported: true, no_standard_libs: true, system_modules: "none"`,
 		classpath:  []string{},
 	},
 }
@@ -274,12 +315,17 @@
 			if testcase.moduleType != "" {
 				moduleType = testcase.moduleType
 			}
-			ctx := testJava(t, moduleType+` {
+
+			bp := moduleType + ` {
 				name: "foo",
 				srcs: ["a.java"],
-				`+testcase.properties+`
+				` + testcase.properties + `
+			}`
+
+			variant := "android_common"
+			if testcase.host == android.Host {
+				variant = android.BuildOs.String() + "_common"
 			}
-			`)
 
 			convertModulesToPaths := func(cp []string) []string {
 				ret := make([]string, len(cp))
@@ -292,33 +338,63 @@
 			bootclasspath := convertModulesToPaths(testcase.bootclasspath)
 			classpath := convertModulesToPaths(testcase.classpath)
 
-			variant := "android_common"
-			if testcase.host == android.Host {
-				variant = android.BuildOs.String() + "_common"
-			}
-			javac := ctx.ModuleForTests("foo", variant).Rule("javac")
-
-			got := strings.TrimPrefix(javac.Args["bootClasspath"], "-bootclasspath ")
 			bc := strings.Join(bootclasspath, ":")
-			if got != bc {
-				t.Errorf("bootclasspath expected %q != got %q", bc, got)
+			if bc != "" {
+				bc = "-bootclasspath " + bc
 			}
 
-			got = strings.TrimPrefix(javac.Args["classpath"], "-classpath ")
 			c := strings.Join(classpath, ":")
-			if got != c {
-				t.Errorf("classpath expected %q != got %q", c, got)
+			if c != "" {
+				c = "-classpath " + c
+			}
+			system := ""
+			if testcase.system == "none" {
+				system = "--system=none"
+			} else if testcase.system != "" {
+				system = "--system=" + filepath.Join(buildDir, ".intermediates", testcase.system, "android_common", "system") + "/"
 			}
 
-			var deps []string
-			if len(bootclasspath) > 0 && bootclasspath[0] != `""` {
-				deps = append(deps, bootclasspath...)
-			}
-			deps = append(deps, classpath...)
+			t.Run("1.8", func(t *testing.T) {
+				// Test default javac 1.8
+				ctx := testJava(t, bp)
 
-			if !reflect.DeepEqual(javac.Implicits.Strings(), deps) {
-				t.Errorf("implicits expected %q != got %q", deps, javac.Implicits.Strings())
-			}
+				javac := ctx.ModuleForTests("foo", variant).Rule("javac")
+
+				got := javac.Args["bootClasspath"]
+				if got != bc {
+					t.Errorf("bootclasspath expected %q != got %q", bc, got)
+				}
+
+				got = javac.Args["classpath"]
+				if got != c {
+					t.Errorf("classpath expected %q != got %q", c, got)
+				}
+
+				var deps []string
+				if len(bootclasspath) > 0 && bootclasspath[0] != `""` {
+					deps = append(deps, bootclasspath...)
+				}
+				deps = append(deps, classpath...)
+
+				if !reflect.DeepEqual(javac.Implicits.Strings(), deps) {
+					t.Errorf("implicits expected %q != got %q", deps, javac.Implicits.Strings())
+				}
+			})
+
+			// Test again with javac 1.9
+			t.Run("1.9", func(t *testing.T) {
+				ctx := testJavaWithEnv(t, bp, map[string]string{"EXPERIMENTAL_USE_OPENJDK9": "true"})
+
+				javac := ctx.ModuleForTests("foo", variant).Rule("javac")
+				got := javac.Args["bootClasspath"]
+				expected := system
+				if testcase.system == "bootclasspath" {
+					expected = bc
+				}
+				if got != expected {
+					t.Errorf("bootclasspath expected %q != got %q", expected, got)
+				}
+			})
 		})
 	}
 
@@ -408,16 +484,16 @@
 		args  string
 	}{
 		{
-			// Test that a module with java_resource_dirs includes a file list file
+			// Test that a module with java_resource_dirs includes the files
 			name: "resource dirs",
 			prop: `java_resource_dirs: ["res"]`,
-			args: "-C res -l ",
+			args: "-C res -f res/a -f res/b",
 		},
 		{
 			// Test that a module with java_resources includes the files
 			name: "resource files",
 			prop: `java_resources: ["res/a", "res/b"]`,
-			args: "-C . -f res/a -C . -f res/b",
+			args: "-C . -f res/a -f res/b",
 		},
 		{
 			// Test that a module with a filegroup in java_resources includes the files with the
@@ -430,13 +506,13 @@
 					path: "res",
 					srcs: ["res/a", "res/b"],
 				}`,
-			args: "-C res -f res/a -C res -f res/b",
+			args: "-C res -f res/a -f res/b",
 		},
 		{
 			// Test that a module with "include_srcs: true" includes its source files in the resources jar
 			name: "include sources",
 			prop: `include_srcs: true`,
-			args: "-C . -f a.java -C . -f b.java -C . -f c.java",
+			args: "-C . -f a.java -f b.java -f c.java",
 		},
 	}
 
@@ -462,8 +538,8 @@
 					foo.Inputs.Strings(), fooRes.Output.String())
 			}
 
-			if !strings.Contains(fooRes.Args["jarArgs"], test.args) {
-				t.Errorf("foo resource jar args %q does not contain %q",
+			if fooRes.Args["jarArgs"] != test.args {
+				t.Errorf("foo resource jar args %q is not %q",
 					fooRes.Args["jarArgs"], test.args)
 			}
 		})
@@ -489,7 +565,7 @@
 
 	fooRes := ctx.ModuleForTests("foo", "android_common").Output("res.jar")
 
-	expected := "-C res -l " + fooRes.Implicits[0].String()
+	expected := "-C res -f res/a -f res/b"
 	if fooRes.Args["jarArgs"] != expected {
 		t.Errorf("foo resource jar args %q is not %q",
 			fooRes.Args["jarArgs"], expected)
@@ -506,6 +582,71 @@
 	}
 }
 
+func TestGeneratedSources(t *testing.T) {
+	ctx := testJava(t, `
+		java_library {
+			name: "foo",
+			srcs: [
+				"a*.java",
+				":gen",
+				"b*.java",
+			],
+		}
+
+		genrule {
+			name: "gen",
+			tool_files: ["res/a"],
+			out: ["gen.java"],
+		}
+	`)
+
+	javac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
+	genrule := ctx.ModuleForTests("gen", "").Rule("generator")
+
+	if len(genrule.Outputs) != 1 || filepath.Base(genrule.Outputs[0].String()) != "gen.java" {
+		t.Fatalf(`gen output file %v is not [".../gen.java"]`, genrule.Outputs.Strings())
+	}
+
+	if len(javac.Inputs) != 3 ||
+		javac.Inputs[0].String() != "a.java" ||
+		javac.Inputs[1].String() != genrule.Outputs[0].String() ||
+		javac.Inputs[2].String() != "b.java" {
+		t.Errorf(`foo inputs %v != ["a.java", ".../gen.java", "b.java"]`, javac.Inputs)
+	}
+}
+
+func TestKotlin(t *testing.T) {
+	ctx := testJava(t, `
+		java_library {
+			name: "foo",
+                        srcs: ["a.java", "b.kt"],
+		}
+		`)
+
+	kotlinc := ctx.ModuleForTests("foo", "android_common").Rule("kotlinc")
+	javac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
+	jar := ctx.ModuleForTests("foo", "android_common").Output("classes.jar")
+
+	if len(kotlinc.Inputs) != 2 || kotlinc.Inputs[0].String() != "a.java" ||
+		kotlinc.Inputs[1].String() != "b.kt" {
+		t.Errorf(`foo kotlinc inputs %v != ["a.java", "b.kt"]`, kotlinc.Inputs)
+	}
+
+	if len(javac.Inputs) != 1 || javac.Inputs[0].String() != "a.java" {
+		t.Errorf(`foo inputs %v != ["a.java"]`, javac.Inputs)
+	}
+
+	if !strings.Contains(javac.Args["classpath"], kotlinc.Output.String()) {
+		t.Errorf("foo classpath %v does not contain %q",
+			javac.Args["classpath"], kotlinc.Output.String())
+	}
+
+	if !inList(kotlinc.Output.String(), jar.Inputs.Strings()) {
+		t.Errorf("foo jar inputs %v does not contain %q",
+			jar.Inputs.Strings(), kotlinc.Output.String())
+	}
+}
+
 func fail(t *testing.T, errs []error) {
 	if len(errs) > 0 {
 		for _, err := range errs {
diff --git a/java/proto.go b/java/proto.go
index dd8cabd..fc259a5 100644
--- a/java/proto.go
+++ b/java/proto.go
@@ -30,20 +30,21 @@
 		blueprint.RuleParams{
 			Command: `rm -rf $outDir && mkdir -p $outDir && ` +
 				`$protocCmd $protoOut=$protoOutFlags:$outDir $protoFlags $in && ` +
-				`find $outDir -name "*.java" > $out`,
-			CommandDeps: []string{"$protocCmd"},
+				`${config.SoongZipCmd} -jar -o $out -C $outDir -D $outDir`,
+			CommandDeps: []string{
+				"$protocCmd",
+				"${config.SoongZipCmd}",
+			},
 		}, "protoFlags", "protoOut", "protoOutFlags", "outDir")
 )
 
-func genProto(ctx android.ModuleContext, protoFiles android.Paths,
-	protoFlags string, protoOut, protoOutFlags string) android.WritablePath {
-
-	protoFileList := android.PathForModuleGen(ctx, "proto.filelist")
+func genProto(ctx android.ModuleContext, outputSrcJar android.WritablePath,
+	protoFiles android.Paths, protoFlags string, protoOut, protoOutFlags string) {
 
 	ctx.ModuleBuild(pctx, android.ModuleBuildParams{
 		Rule:        proto,
 		Description: "protoc " + protoFiles[0].Rel(),
-		Output:      protoFileList,
+		Output:      outputSrcJar,
 		Inputs:      protoFiles,
 		Args: map[string]string{
 			"outDir":        android.ProtoDir(ctx).String(),
@@ -52,8 +53,6 @@
 			"protoFlags":    protoFlags,
 		},
 	})
-
-	return protoFileList
 }
 
 func protoDeps(ctx android.BottomUpMutatorContext, p *android.ProtoProperties) {
diff --git a/java/resources.go b/java/resources.go
index 85ebd52..a596fd7 100644
--- a/java/resources.go
+++ b/java/resources.go
@@ -19,8 +19,6 @@
 	"path/filepath"
 	"strings"
 
-	"github.com/google/blueprint/bootstrap"
-
 	"android/soong/android"
 )
 
@@ -33,15 +31,6 @@
 	"**/*~",
 }
 
-func isStringInSlice(str string, slice []string) bool {
-	for _, s := range slice {
-		if s == str {
-			return true
-		}
-	}
-	return false
-}
-
 func ResourceDirsToJarArgs(ctx android.ModuleContext,
 	resourceDirs, excludeDirs []string) (args []string, deps android.Paths) {
 	var excludes []string
@@ -53,40 +42,64 @@
 
 	excludes = append(excludes, resourceExcludes...)
 
-	for _, resourceDir := range resourceDirs {
-		if isStringInSlice(resourceDir, excludeDirs) {
-			continue
-		}
-		resourceDir := android.PathForModuleSrc(ctx, resourceDir)
-		dirs := ctx.Glob(resourceDir.String(), nil)
-		for _, dir := range dirs {
-			fileListFile := android.ResPathWithName(ctx, dir, "resources.list")
-			depFile := fileListFile.String() + ".d"
+	for _, dir := range resourceDirs {
+		dir := android.PathForModuleSrc(ctx, dir).String()
+		files := ctx.Glob(filepath.Join(dir, "**/*"), excludes)
 
-			pattern := filepath.Join(dir.String(), "**/*")
-			bootstrap.GlobFile(ctx, pattern, excludes, fileListFile.String(), depFile)
-			args = append(args,
-				"-C", dir.String(),
-				"-l", fileListFile.String())
-			deps = append(deps, fileListFile)
+		deps = append(deps, files...)
+
+		if len(files) > 0 {
+			args = append(args, "-C", dir)
+
+			for _, f := range files {
+				path := f.String()
+				if !strings.HasPrefix(path, dir) {
+					panic(fmt.Errorf("path %q does not start with %q", path, dir))
+				}
+				args = append(args, "-f", path)
+			}
 		}
 	}
 
 	return args, deps
 }
 
+// Convert java_resources properties to arguments to soong_zip -jar, ignoring common patterns
+// that should not be treated as resources (including *.java).
 func ResourceFilesToJarArgs(ctx android.ModuleContext,
 	res, exclude []string) (args []string, deps android.Paths) {
+
+	exclude = append([]string(nil), exclude...)
+	exclude = append(exclude, resourceExcludes...)
+	return resourceFilesToJarArgs(ctx, res, exclude)
+}
+
+// Convert java_resources properties to arguments to soong_zip -jar, keeping files that should
+// normally not used as resources like *.java
+func SourceFilesToJarArgs(ctx android.ModuleContext,
+	res, exclude []string) (args []string, deps android.Paths) {
+
+	return resourceFilesToJarArgs(ctx, res, exclude)
+}
+
+func resourceFilesToJarArgs(ctx android.ModuleContext,
+	res, exclude []string) (args []string, deps android.Paths) {
+
 	files := ctx.ExpandSources(res, exclude)
 
-	for _, f := range files {
+	lastDir := ""
+	for i, f := range files {
 		rel := f.Rel()
 		path := f.String()
 		if !strings.HasSuffix(path, rel) {
 			panic(fmt.Errorf("path %q does not end with %q", path, rel))
 		}
-		path = filepath.Clean(strings.TrimSuffix(path, rel))
-		args = append(args, "-C", filepath.Clean(path), "-f", f.String())
+		dir := filepath.Clean(strings.TrimSuffix(path, rel))
+		if i == 0 || dir != lastDir {
+			args = append(args, "-C", dir)
+		}
+		args = append(args, "-f", path)
+		lastDir = dir
 	}
 
 	return args, files
diff --git a/java/system_modules.go b/java/system_modules.go
new file mode 100644
index 0000000..ddfc5cf
--- /dev/null
+++ b/java/system_modules.go
@@ -0,0 +1,144 @@
+// Copyright 2017 Google Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+	"fmt"
+	"io"
+	"strings"
+
+	"github.com/google/blueprint"
+
+	"android/soong/android"
+)
+
+// OpenJDK 9 introduces the concept of "system modules", which replace the bootclasspath.  This
+// file will produce the rules necessary to convert each unique set of bootclasspath jars into
+// system modules in a runtime image using the jmod and jlink tools.
+
+func init() {
+	android.RegisterModuleType("java_system_modules", SystemModulesFactory)
+
+	pctx.SourcePathVariable("moduleInfoJavaPath", "build/soong/scripts/jars-to-module-info-java.sh")
+}
+
+var (
+	jarsTosystemModules = pctx.AndroidStaticRule("jarsTosystemModules", blueprint.RuleParams{
+		Command: `rm -rf ${outDir} ${workDir} && mkdir -p ${workDir}/jmod && ` +
+			`${moduleInfoJavaPath} ${moduleName} $in > ${workDir}/module-info.java && ` +
+			`${config.JavacCmd} --system=none --patch-module=java.base=${classpath} ${workDir}/module-info.java && ` +
+			`${config.SoongZipCmd} -jar -o ${workDir}/classes.jar -C ${workDir} -f ${workDir}/module-info.class && ` +
+			`${config.MergeZipsCmd} -j ${workDir}/module.jar ${workDir}/classes.jar $in && ` +
+			`${config.JmodCmd} create --module-version 9 --target-platform android ` +
+			`  --class-path ${workDir}/module.jar ${workDir}/jmod/${moduleName}.jmod && ` +
+			`${config.JlinkCmd} --module-path ${workDir}/jmod --add-modules ${moduleName} --output ${outDir} && ` +
+			`cp ${config.JrtFsJar} ${outDir}/lib/`,
+		CommandDeps: []string{
+			"${moduleInfoJavaPath}",
+			"${config.JavacCmd}",
+			"${config.SoongZipCmd}",
+			"${config.MergeZipsCmd}",
+			"${config.JmodCmd}",
+			"${config.JlinkCmd}",
+			"${config.JrtFsJar}",
+		},
+	},
+		"moduleName", "classpath", "outDir", "workDir")
+)
+
+func TransformJarsToSystemModules(ctx android.ModuleContext, moduleName string, jars android.Paths) android.WritablePath {
+	outDir := android.PathForModuleOut(ctx, "system")
+	workDir := android.PathForModuleOut(ctx, "modules")
+	outputFile := android.PathForModuleOut(ctx, "system/lib/modules")
+	outputs := android.WritablePaths{
+		outputFile,
+		android.PathForModuleOut(ctx, "system/lib/jrt-fs.jar"),
+		android.PathForModuleOut(ctx, "system/release"),
+	}
+
+	ctx.ModuleBuild(pctx, android.ModuleBuildParams{
+		Rule:        jarsTosystemModules,
+		Description: "system modules",
+		Outputs:     outputs,
+		Inputs:      jars,
+		Args: map[string]string{
+			"moduleName": moduleName,
+			"classpath":  strings.Join(jars.Strings(), ":"),
+			"workDir":    workDir.String(),
+			"outDir":     outDir.String(),
+		},
+	})
+
+	return outputFile
+}
+
+func SystemModulesFactory() android.Module {
+	module := &SystemModules{}
+	module.AddProperties(&module.properties)
+	android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon)
+	return module
+}
+
+type SystemModules struct {
+	android.ModuleBase
+
+	properties SystemModulesProperties
+
+	outputFile android.Path
+}
+
+type SystemModulesProperties struct {
+	// List of java library modules that should be included in the system modules
+	Libs []string
+
+	// List of prebuilt jars that should be included in the system modules
+	Jars []string
+
+	// Sdk version that should be included in the system modules
+	Sdk_version *string
+}
+
+func (system *SystemModules) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	var jars android.Paths
+
+	ctx.VisitDirectDeps(func(module blueprint.Module) {
+		if ctx.OtherModuleDependencyTag(module) == libTag {
+			dep, _ := module.(Dependency)
+			jars = append(jars, dep.ClasspathFiles()...)
+		}
+	})
+
+	jars = append(jars, android.PathsForModuleSrc(ctx, system.properties.Jars)...)
+
+	if ctx.AConfig().TargetOpenJDK9() {
+		system.outputFile = TransformJarsToSystemModules(ctx, "java.base", jars)
+	}
+}
+
+func (system *SystemModules) DepsMutator(ctx android.BottomUpMutatorContext) {
+	ctx.AddDependency(ctx.Module(), libTag, system.properties.Libs...)
+}
+
+func (system *SystemModules) AndroidMk() android.AndroidMkData {
+	return android.AndroidMkData{
+		Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
+			if system.outputFile != nil {
+				makevar := "SOONG_SYSTEM_MODULES_" + name
+				fmt.Fprintln(w)
+				fmt.Fprintln(w, makevar, ":=", system.outputFile.String())
+				fmt.Fprintln(w, ".KATI_READONLY", ":=", makevar)
+			}
+		},
+	}
+}
diff --git a/python/python_test.go b/python/python_test.go
index 8737302..176c6ec 100644
--- a/python/python_test.go
+++ b/python/python_test.go
@@ -450,7 +450,7 @@
 		t.Fatal(err)
 	}
 
-	config = android.TestConfig(buildDir)
+	config = android.TestConfig(buildDir, nil)
 
 	return
 }
diff --git a/scripts/jars-to-module-info-java.sh b/scripts/jars-to-module-info-java.sh
new file mode 100755
index 0000000..44be549
--- /dev/null
+++ b/scripts/jars-to-module-info-java.sh
@@ -0,0 +1,20 @@
+#!/bin/bash -e
+
+# Extracts the Java package names of all classes in the .jar files and writes a module-info.java
+# file to stdout that exports all of those packages.
+
+if [ -z "$1" ]; then
+  echo "usage: $0 <module name> <jar1> [<jar2> ...]" >&2
+  exit 1
+fi
+
+module_name=$1
+shift
+
+echo "module ${module_name} {"
+for j in "$@"; do zipinfo -1 $j ; done \
+  | grep -E '/[^/]*\.class$' \
+  | sed 's|\(.*\)/[^/]*\.class$|    exports \1;|g' \
+  | sed 's|/|.|g' \
+  | sort -u
+echo "}"
diff --git a/ui/build/Android.bp b/ui/build/Android.bp
index 34c21f7..d1b4943 100644
--- a/ui/build/Android.bp
+++ b/ui/build/Android.bp
@@ -27,11 +27,11 @@
         "cleanbuild.go",
         "config.go",
         "context.go",
+        "dumpvars.go",
         "environment.go",
         "exec.go",
         "finder.go",
         "kati.go",
-        "make.go",
         "ninja.go",
         "proc_sync.go",
         "signal.go",
diff --git a/ui/build/build.go b/ui/build/build.go
index 45d18e0..0df22b3 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -104,7 +104,7 @@
 
 func help(ctx Context, config Config, what int) {
 	cmd := Command(ctx, config, "help.sh", "build/make/help.sh")
-	cmd.Sandbox = makeSandbox
+	cmd.Sandbox = dumpvarsSandbox
 	cmd.Stdout = ctx.Stdout()
 	cmd.Stderr = ctx.Stderr()
 	cmd.RunOrFatal()
diff --git a/ui/build/config.go b/ui/build/config.go
index 2b0da4d..191a102 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -154,7 +154,7 @@
 			c.verbose = true
 		} else if arg == "--skip-make" {
 			c.skipMake = true
-		} else if arg[0] == '-' {
+		} else if len(arg) > 0 && arg[0] == '-' {
 			parseArgNum := func(def int) int {
 				if len(arg) > 2 {
 					p, err := strconv.ParseUint(arg[2:], 10, 31)
@@ -172,9 +172,9 @@
 				return def
 			}
 
-			if arg[1] == 'j' {
+			if len(arg) > 1 && arg[1] == 'j' {
 				c.parallel = parseArgNum(c.parallel)
-			} else if arg[1] == 'k' {
+			} else if len(arg) > 1 && arg[1] == 'k' {
 				c.keepGoing = parseArgNum(0)
 			} else {
 				ctx.Fatalln("Unknown option:", arg)
@@ -437,21 +437,13 @@
 	}
 }
 
-func (c *configImpl) HostAsan() bool {
+func (c *configImpl) PrebuiltBuildTool(name string) string {
 	if v, ok := c.environ.Get("SANITIZE_HOST"); ok {
 		if sanitize := strings.Fields(v); inList("address", sanitize) {
-			return true
-		}
-	}
-	return false
-}
-
-func (c *configImpl) PrebuiltBuildTool(name string) string {
-	// (b/36182021) We're seeing rare ckati crashes, so always enable asan kati on the build servers.
-	if c.HostAsan() || (c.Dist() && name == "ckati") {
-		asan := filepath.Join("prebuilts/build-tools", c.HostPrebuiltTag(), "asan/bin", name)
-		if _, err := os.Stat(asan); err == nil {
-			return asan
+			asan := filepath.Join("prebuilts/build-tools", c.HostPrebuiltTag(), "asan/bin", name)
+			if _, err := os.Stat(asan); err == nil {
+				return asan
+			}
 		}
 	}
 	return filepath.Join("prebuilts/build-tools", c.HostPrebuiltTag(), "bin", name)
diff --git a/ui/build/make.go b/ui/build/dumpvars.go
similarity index 62%
rename from ui/build/make.go
rename to ui/build/dumpvars.go
index edf6d96..fb20d63 100644
--- a/ui/build/make.go
+++ b/ui/build/dumpvars.go
@@ -15,8 +15,8 @@
 package build
 
 import (
+	"bytes"
 	"fmt"
-	"path/filepath"
 	"strings"
 )
 
@@ -28,27 +28,29 @@
 // Make without actually building them. So all the variables based on
 // MAKECMDGOALS can be read.
 //
-// extra_targets adds real arguments to the make command, in case other targets
-// actually need to be run (like the Soong config generator).
-//
 // vars is the list of variables to read. The values will be put in the
 // returned map.
-func DumpMakeVars(ctx Context, config Config, goals, extra_targets, vars []string) (map[string]string, error) {
+func DumpMakeVars(ctx Context, config Config, goals, vars []string) (map[string]string, error) {
+	return dumpMakeVars(ctx, config, goals, vars, false)
+}
+
+func dumpMakeVars(ctx Context, config Config, goals, vars []string, write_soong_vars bool) (map[string]string, error) {
 	ctx.BeginTrace("dumpvars")
 	defer ctx.EndTrace()
 
-	cmd := Command(ctx, config, "make",
-		"make",
-		"--no-print-directory",
-		"-f", "build/core/config.mk",
+	cmd := Command(ctx, config, "dumpvars",
+		config.PrebuiltBuildTool("ckati"),
+		"-f", "build/make/core/config.mk",
+		"--color_warnings",
 		"dump-many-vars",
-		"CALLED_FROM_SETUP=true",
-		"BUILD_SYSTEM=build/core",
-		"MAKECMDGOALS="+strings.Join(goals, " "),
-		"DUMP_MANY_VARS="+strings.Join(vars, " "),
-		"OUT_DIR="+config.OutDir())
-	cmd.Args = append(cmd.Args, extra_targets...)
-	cmd.Sandbox = makeSandbox
+		"MAKECMDGOALS="+strings.Join(goals, " "))
+	cmd.Environment.Set("CALLED_FROM_SETUP", "true")
+	cmd.Environment.Set("BUILD_SYSTEM", "build/make/core")
+	if write_soong_vars {
+		cmd.Environment.Set("WRITE_SOONG_VARIABLES", "true")
+	}
+	cmd.Environment.Set("DUMP_MANY_VARS", strings.Join(vars, " "))
+	cmd.Sandbox = dumpvarsSandbox
 	// TODO: error out when Stderr contains any content
 	cmd.Stderr = ctx.Stderr()
 	output, err := cmd.Output()
@@ -77,6 +79,49 @@
 	return ret, nil
 }
 
+// Variables to print out in the top banner
+var BannerVars = []string{
+	"PLATFORM_VERSION_CODENAME",
+	"PLATFORM_VERSION",
+	"TARGET_PRODUCT",
+	"TARGET_BUILD_VARIANT",
+	"TARGET_BUILD_TYPE",
+	"TARGET_BUILD_APPS",
+	"TARGET_ARCH",
+	"TARGET_ARCH_VARIANT",
+	"TARGET_CPU_VARIANT",
+	"TARGET_2ND_ARCH",
+	"TARGET_2ND_ARCH_VARIANT",
+	"TARGET_2ND_CPU_VARIANT",
+	"HOST_ARCH",
+	"HOST_2ND_ARCH",
+	"HOST_OS",
+	"HOST_OS_EXTRA",
+	"HOST_CROSS_OS",
+	"HOST_CROSS_ARCH",
+	"HOST_CROSS_2ND_ARCH",
+	"HOST_BUILD_TYPE",
+	"BUILD_ID",
+	"OUT_DIR",
+	"AUX_OS_VARIANT_LIST",
+	"TARGET_BUILD_PDK",
+	"PDK_FUSION_PLATFORM_ZIP",
+}
+
+func Banner(make_vars map[string]string) string {
+	b := &bytes.Buffer{}
+
+	fmt.Fprintln(b, "============================================")
+	for _, name := range BannerVars {
+		if make_vars[name] != "" {
+			fmt.Fprintf(b, "%s=%s\n", name, make_vars[name])
+		}
+	}
+	fmt.Fprint(b, "============================================")
+
+	return b.String()
+}
+
 func runMakeProductConfig(ctx Context, config Config) {
 	// Variables to export into the environment of Kati/Ninja
 	exportEnvVars := []string{
@@ -98,35 +143,6 @@
 		"CCACHE_CPP2",
 	}
 
-	// Variables to print out in the top banner
-	bannerVars := []string{
-		"PLATFORM_VERSION_CODENAME",
-		"PLATFORM_VERSION",
-		"TARGET_PRODUCT",
-		"TARGET_BUILD_VARIANT",
-		"TARGET_BUILD_TYPE",
-		"TARGET_BUILD_APPS",
-		"TARGET_ARCH",
-		"TARGET_ARCH_VARIANT",
-		"TARGET_CPU_VARIANT",
-		"TARGET_2ND_ARCH",
-		"TARGET_2ND_ARCH_VARIANT",
-		"TARGET_2ND_CPU_VARIANT",
-		"HOST_ARCH",
-		"HOST_2ND_ARCH",
-		"HOST_OS",
-		"HOST_OS_EXTRA",
-		"HOST_CROSS_OS",
-		"HOST_CROSS_ARCH",
-		"HOST_CROSS_2ND_ARCH",
-		"HOST_BUILD_TYPE",
-		"BUILD_ID",
-		"OUT_DIR",
-		"AUX_OS_VARIANT_LIST",
-		"TARGET_BUILD_PDK",
-		"PDK_FUSION_PLATFORM_ZIP",
-	}
-
 	allVars := append(append([]string{
 		// Used to execute Kati and Ninja
 		"NINJA_GOALS",
@@ -134,23 +150,15 @@
 
 		// To find target/product/<DEVICE>
 		"TARGET_DEVICE",
-	}, exportEnvVars...), bannerVars...)
+	}, exportEnvVars...), BannerVars...)
 
-	make_vars, err := DumpMakeVars(ctx, config, config.Arguments(), []string{
-		filepath.Join(config.SoongOutDir(), "soong.variables"),
-	}, allVars)
+	make_vars, err := dumpMakeVars(ctx, config, config.Arguments(), allVars, true)
 	if err != nil {
 		ctx.Fatalln("Error dumping make vars:", err)
 	}
 
 	// Print the banner like make does
-	fmt.Fprintln(ctx.Stdout(), "============================================")
-	for _, name := range bannerVars {
-		if make_vars[name] != "" {
-			fmt.Fprintf(ctx.Stdout(), "%s=%s\n", name, make_vars[name])
-		}
-	}
-	fmt.Fprintln(ctx.Stdout(), "============================================")
+	fmt.Fprintln(ctx.Stdout(), Banner(make_vars))
 
 	// Populate the environment
 	env := config.Environment()
diff --git a/ui/build/kati.go b/ui/build/kati.go
index cc02c76..7bb721d 100644
--- a/ui/build/kati.go
+++ b/ui/build/kati.go
@@ -77,7 +77,8 @@
 		"--color_warnings",
 		"--gen_all_targets",
 		"--werror_find_emulator",
-		"-f", "build/core/main.mk",
+		"--kati_stats",
+		"-f", "build/make/core/main.mk",
 	}
 
 	if !config.Environment().IsFalse("KATI_EMULATE_FIND") {
@@ -109,6 +110,7 @@
 }
 
 var katiIncludeRe = regexp.MustCompile(`^(\[\d+/\d+] )?including [^ ]+ ...$`)
+var katiLogRe = regexp.MustCompile(`^\*kati\*: `)
 
 func katiRewriteOutput(ctx Context, pipe io.ReadCloser) {
 	haveBlankLine := true
@@ -119,6 +121,12 @@
 		line := scanner.Text()
 		verbose := katiIncludeRe.MatchString(line)
 
+		// Only put kati debug/stat lines in our verbose log
+		if katiLogRe.MatchString(line) {
+			ctx.Verbose(line)
+			continue
+		}
+
 		// For verbose lines, write them on the current line without a newline,
 		// then overwrite them if the next thing we're printing is another
 		// verbose line.
diff --git a/ui/build/sandbox_darwin.go b/ui/build/sandbox_darwin.go
index 60407d4..2cf6e20 100644
--- a/ui/build/sandbox_darwin.go
+++ b/ui/build/sandbox_darwin.go
@@ -24,7 +24,7 @@
 const (
 	noSandbox            = ""
 	globalSandbox        = "build/soong/ui/build/sandbox/darwin/global.sb"
-	makeSandbox          = globalSandbox
+	dumpvarsSandbox      = globalSandbox
 	soongSandbox         = globalSandbox
 	katiSandbox          = globalSandbox
 	katiCleanSpecSandbox = globalSandbox
diff --git a/ui/build/sandbox_linux.go b/ui/build/sandbox_linux.go
index 6615d37..f2bfac2 100644
--- a/ui/build/sandbox_linux.go
+++ b/ui/build/sandbox_linux.go
@@ -19,7 +19,7 @@
 const (
 	noSandbox            = false
 	globalSandbox        = false
-	makeSandbox          = false
+	dumpvarsSandbox      = false
 	soongSandbox         = false
 	katiSandbox          = false
 	katiCleanSpecSandbox = false