Merge changes from topic "host_bionic"

* changes:
  Extract the linker and embed it into host bionic binaries
  Add prefix_symbols support to cc_object
  Fix ToolchainClangCflags for host bionic
  Don't add a rpath to the linker
diff --git a/android/config.go b/android/config.go
index 4f35114..f30a708 100644
--- a/android/config.go
+++ b/android/config.go
@@ -185,6 +185,25 @@
 	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)
+	config := testConfig.config
+
+	config.Targets = map[OsClass][]Target{
+		Device: []Target{
+			{Android, Arch{ArchType: Arm64, Native: true}},
+			{Android, Arch{ArchType: Arm, Native: true}},
+		},
+		Host: []Target{
+			{BuildOs, Arch{ArchType: X86_64}},
+			{BuildOs, Arch{ArchType: X86}},
+		},
+	}
+
+	return testConfig
+}
+
 // New creates a new Config object.  The srcDir argument specifies the path to
 // the root source directory. It also loads the config file, if found.
 func NewConfig(srcDir, buildDir string) (Config, error) {
diff --git a/android/mutator.go b/android/mutator.go
index e20bc2c..04407eb 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -78,11 +78,13 @@
 	RegisterDefaultsPreArchMutators,
 }
 
+func registerArchMutator(ctx RegisterMutatorsContext) {
+	ctx.BottomUp("arch", archMutator).Parallel()
+	ctx.TopDown("arch_hooks", archHookMutator).Parallel()
+}
+
 var preDeps = []RegisterMutatorFunc{
-	func(ctx RegisterMutatorsContext) {
-		ctx.BottomUp("arch", archMutator).Parallel()
-		ctx.TopDown("arch_hooks", archHookMutator).Parallel()
-	},
+	registerArchMutator,
 }
 
 var postDeps = []RegisterMutatorFunc{
diff --git a/android/testing.go b/android/testing.go
index 4144775..519e279 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -27,6 +27,12 @@
 	}
 }
 
+func NewTestArchContext() *TestContext {
+	ctx := NewTestContext()
+	ctx.preDeps = append(ctx.preDeps, registerArchMutator)
+	return ctx
+}
+
 type TestContext struct {
 	*blueprint.Context
 	preArch, preDeps, postDeps []RegisterMutatorFunc
diff --git a/cmd/merge_zips/merge_zips.go b/cmd/merge_zips/merge_zips.go
index 9fd5ddd..a94392b 100644
--- a/cmd/merge_zips/merge_zips.go
+++ b/cmd/merge_zips/merge_zips.go
@@ -15,8 +15,10 @@
 package main
 
 import (
+	"errors"
 	"flag"
 	"fmt"
+	"hash/crc32"
 	"log"
 	"os"
 	"path/filepath"
@@ -52,10 +54,12 @@
 }
 
 var (
-	sortEntries    = flag.Bool("s", false, "sort entries (defaults to the order from the input zip files)")
-	emulateJar     = flag.Bool("j", false, "sort zip entries using jar ordering (META-INF first)")
-	stripDirs      []string
-	zipsToNotStrip = make(map[string]bool)
+	sortEntries     = flag.Bool("s", false, "sort entries (defaults to the order from the input zip files)")
+	emulateJar      = flag.Bool("j", false, "sort zip entries using jar ordering (META-INF first)")
+	stripDirs       []string
+	zipsToNotStrip  = make(map[string]bool)
+	stripDirEntries = flag.Bool("D", false, "strip directory entries from the output zip file")
+	manifest        = flag.String("m", "", "manifest file to insert in jar")
 )
 
 func init() {
@@ -65,7 +69,7 @@
 
 func main() {
 	flag.Usage = func() {
-		fmt.Fprintln(os.Stderr, "usage: merge_zips [-j] output [inputs...]")
+		fmt.Fprintln(os.Stderr, "usage: merge_zips [-jsD] [-m manifest] output [inputs...]")
 		flag.PrintDefaults()
 	}
 
@@ -107,8 +111,13 @@
 		readers = append(readers, namedReader)
 	}
 
+	if *manifest != "" && !*emulateJar {
+		log.Fatal(errors.New("must specify -j when specifying a manifest via -m"))
+	}
+
 	// do merge
-	if err := mergeZips(readers, writer, *sortEntries, *emulateJar); err != nil {
+	err = mergeZips(readers, writer, *manifest, *sortEntries, *emulateJar, *stripDirEntries)
+	if err != nil {
 		log.Fatal(err)
 	}
 }
@@ -129,23 +138,109 @@
 	return p.zipName + "/" + p.entryName
 }
 
-// a zipEntry knows the location and content of a file within a zip
+// a zipEntry is a zipSource that pulls its content from another zip
 type zipEntry struct {
 	path    zipEntryPath
 	content *zip.File
 }
 
-// a fileMapping specifies to copy a zip entry from one place to another
-type fileMapping struct {
-	source zipEntry
-	dest   string
+func (ze zipEntry) String() string {
+	return ze.path.String()
 }
 
-func mergeZips(readers []namedZipReader, writer *zip.Writer, sortEntries bool, emulateJar bool) error {
+func (ze zipEntry) IsDir() bool {
+	return ze.content.FileInfo().IsDir()
+}
 
-	mappingsByDest := make(map[string]fileMapping, 0)
+func (ze zipEntry) CRC32() uint32 {
+	return ze.content.FileHeader.CRC32
+}
+
+func (ze zipEntry) WriteToZip(dest string, zw *zip.Writer) error {
+	return zw.CopyFrom(ze.content, dest)
+}
+
+// a bufferEntry is a zipSource that pulls its content from a []byte
+type bufferEntry struct {
+	fh      *zip.FileHeader
+	content []byte
+}
+
+func (be bufferEntry) String() string {
+	return "internal buffer"
+}
+
+func (be bufferEntry) IsDir() bool {
+	return be.fh.FileInfo().IsDir()
+}
+
+func (be bufferEntry) CRC32() uint32 {
+	return crc32.ChecksumIEEE(be.content)
+}
+
+func (be bufferEntry) WriteToZip(dest string, zw *zip.Writer) error {
+	w, err := zw.CreateHeader(be.fh)
+	if err != nil {
+		return err
+	}
+
+	if !be.IsDir() {
+		_, err = w.Write(be.content)
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+type zipSource interface {
+	String() string
+	IsDir() bool
+	CRC32() uint32
+	WriteToZip(dest string, zw *zip.Writer) error
+}
+
+// a fileMapping specifies to copy a zip entry from one place to another
+type fileMapping struct {
+	dest   string
+	source zipSource
+}
+
+func mergeZips(readers []namedZipReader, writer *zip.Writer, manifest string,
+	sortEntries, emulateJar, stripDirEntries bool) error {
+
+	sourceByDest := make(map[string]zipSource, 0)
 	orderedMappings := []fileMapping{}
 
+	// if dest already exists returns a non-null zipSource for the existing source
+	addMapping := func(dest string, source zipSource) zipSource {
+		mapKey := filepath.Clean(dest)
+		if existingSource, exists := sourceByDest[mapKey]; exists {
+			return existingSource
+		}
+
+		sourceByDest[mapKey] = source
+		orderedMappings = append(orderedMappings, fileMapping{source: source, dest: dest})
+		return nil
+	}
+
+	if manifest != "" {
+		if !stripDirEntries {
+			dirHeader := jar.MetaDirFileHeader()
+			dirSource := bufferEntry{dirHeader, nil}
+			addMapping(jar.MetaDir, dirSource)
+		}
+
+		fh, buf, err := jar.ManifestFileContents(manifest)
+		if err != nil {
+			return err
+		}
+
+		fileSource := bufferEntry{fh, buf}
+		addMapping(jar.ManifestFile, fileSource)
+	}
+
 	for _, namedReader := range readers {
 		_, skipStripThisZip := zipsToNotStrip[namedReader.path]
 	FileLoop:
@@ -163,46 +258,39 @@
 					}
 				}
 			}
+
+			if stripDirEntries && file.FileInfo().IsDir() {
+				continue
+			}
+
 			// check for other files or directories destined for the same path
 			dest := file.Name
-			mapKey := dest
-			if strings.HasSuffix(mapKey, "/") {
-				mapKey = mapKey[:len(mapKey)-1]
-			}
-			existingMapping, exists := mappingsByDest[mapKey]
 
 			// make a new entry to add
 			source := zipEntry{path: zipEntryPath{zipName: namedReader.path, entryName: file.Name}, content: file}
-			newMapping := fileMapping{source: source, dest: dest}
 
-			if exists {
+			if existingSource := addMapping(dest, source); existingSource != nil {
 				// handle duplicates
-				wasDir := existingMapping.source.content.FileHeader.FileInfo().IsDir()
-				isDir := newMapping.source.content.FileHeader.FileInfo().IsDir()
-				if wasDir != isDir {
+				if existingSource.IsDir() != source.IsDir() {
 					return fmt.Errorf("Directory/file mismatch at %v from %v and %v\n",
-						dest, existingMapping.source.path, newMapping.source.path)
+						dest, existingSource, source)
 				}
 				if emulateJar &&
 					file.Name == jar.ManifestFile || file.Name == jar.ModuleInfoClass {
 					// Skip manifest and module info files that are not from the first input file
 					continue
 				}
-				if !isDir {
+				if !source.IsDir() {
 					if emulateJar {
-						if existingMapping.source.content.CRC32 != newMapping.source.content.CRC32 {
+						if existingSource.CRC32() != source.CRC32() {
 							fmt.Fprintf(os.Stdout, "WARNING: Duplicate path %v found in %v and %v\n",
-								dest, existingMapping.source.path, newMapping.source.path)
+								dest, existingSource, source)
 						}
 					} else {
 						return fmt.Errorf("Duplicate path %v found in %v and %v\n",
-							dest, existingMapping.source.path, newMapping.source.path)
+							dest, existingSource, source)
 					}
 				}
-			} else {
-				// save entry
-				mappingsByDest[mapKey] = newMapping
-				orderedMappings = append(orderedMappings, newMapping)
 			}
 		}
 	}
@@ -214,7 +302,7 @@
 	}
 
 	for _, entry := range orderedMappings {
-		if err := writer.CopyFrom(entry.source.content, entry.dest); err != nil {
+		if err := entry.source.WriteToZip(entry.dest, writer); err != nil {
 			return err
 		}
 	}
diff --git a/cmd/soong_zip/soong_zip.go b/cmd/soong_zip/soong_zip.go
index 4cf0764..cb9df9a 100644
--- a/cmd/soong_zip/soong_zip.go
+++ b/cmd/soong_zip/soong_zip.go
@@ -58,7 +58,7 @@
 }
 
 type byteReaderCloser struct {
-	bytes.Reader
+	*bytes.Reader
 	io.Closer
 }
 
@@ -252,7 +252,7 @@
 	}
 
 	w := &zipWriter{
-		time:         time.Date(2009, 1, 1, 0, 0, 0, 0, time.UTC),
+		time:         jar.DefaultTime,
 		createdDirs:  make(map[string]string),
 		createdFiles: make(map[string]string),
 		directories:  *directories,
@@ -353,14 +353,14 @@
 		z.memoryRateLimiter.Stop()
 	}()
 
-	if manifest != "" {
-		if !*emulateJar {
-			return errors.New("must specify --jar when specifying a manifest via -m")
-		}
-		pathMappings = append(pathMappings, pathMapping{jar.ManifestFile, manifest, zip.Deflate})
+	if manifest != "" && !*emulateJar {
+		return errors.New("must specify --jar when specifying a manifest via -m")
 	}
 
 	if *emulateJar {
+		// manifest may be empty, in which case addManifest will fill in a default
+		pathMappings = append(pathMappings, pathMapping{jar.ManifestFile, manifest, zip.Deflate})
+
 		jarSort(pathMappings)
 	}
 
@@ -524,11 +524,6 @@
 }
 
 func (z *zipWriter) addManifest(dest string, src string, method uint16) error {
-	givenBytes, err := ioutil.ReadFile(src)
-	if err != nil {
-		return err
-	}
-
 	if prev, exists := z.createdDirs[dest]; exists {
 		return fmt.Errorf("destination %q is both a directory %q and a file %q", dest, prev, src)
 	}
@@ -536,27 +531,18 @@
 		return fmt.Errorf("destination %q has two files %q and %q", dest, prev, src)
 	}
 
-	manifestMarker := []byte("Manifest-Version:")
-	header := append(manifestMarker, []byte(" 1.0\nCreated-By: soong_zip\n")...)
-
-	var finalBytes []byte
-	if !bytes.Contains(givenBytes, manifestMarker) {
-		finalBytes = append(append(header, givenBytes...), byte('\n'))
-	} else {
-		finalBytes = givenBytes
+	if err := z.writeDirectory(filepath.Dir(dest), src); err != nil {
+		return err
 	}
 
-	byteReader := bytes.NewReader(finalBytes)
-
-	reader := &byteReaderCloser{*byteReader, ioutil.NopCloser(nil)}
-
-	fileHeader := &zip.FileHeader{
-		Name:               dest,
-		Method:             zip.Store,
-		UncompressedSize64: uint64(byteReader.Len()),
+	fh, buf, err := jar.ManifestFileContents(src)
+	if err != nil {
+		return err
 	}
 
-	return z.writeFileContents(fileHeader, reader)
+	reader := &byteReaderCloser{bytes.NewReader(buf), ioutil.NopCloser(nil)}
+
+	return z.writeFileContents(fh, reader)
 }
 
 func (z *zipWriter) writeFileContents(header *zip.FileHeader, r readerSeekerCloser) (err error) {
@@ -770,19 +756,6 @@
 	close(compressChan)
 }
 
-func (z *zipWriter) addExtraField(zipHeader *zip.FileHeader, fieldHeader [2]byte, data []byte) {
-	// add the field header in little-endian order
-	zipHeader.Extra = append(zipHeader.Extra, fieldHeader[1], fieldHeader[0])
-
-	// specify the length of the data (in little-endian order)
-	dataLength := len(data)
-	lengthBytes := []byte{byte(dataLength % 256), byte(dataLength / 256)}
-	zipHeader.Extra = append(zipHeader.Extra, lengthBytes...)
-
-	// add the contents of the extra field
-	zipHeader.Extra = append(zipHeader.Extra, data...)
-}
-
 // 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 {
@@ -810,17 +783,19 @@
 	if z.directories {
 		// make a directory entry for each uncreated directory
 		for _, cleanDir := range zipDirs {
-			dirHeader := &zip.FileHeader{
-				Name: cleanDir + "/",
-			}
-			dirHeader.SetMode(0700 | os.ModeDir)
-			dirHeader.SetModTime(z.time)
+			var dirHeader *zip.FileHeader
 
-			if *emulateJar && dir == "META-INF/" {
-				// Jar files have a 0-length extra field with header "CAFE"
-				z.addExtraField(dirHeader, [2]byte{0xca, 0xfe}, []byte{})
+			if *emulateJar && cleanDir+"/" == jar.MetaDir {
+				dirHeader = jar.MetaDirFileHeader()
+			} else {
+				dirHeader = &zip.FileHeader{
+					Name: cleanDir + "/",
+				}
+				dirHeader.SetMode(0700 | os.ModeDir)
 			}
 
+			dirHeader.SetModTime(z.time)
+
 			ze := make(chan *zipEntry, 1)
 			ze <- &zipEntry{
 				fh: dirHeader,
diff --git a/jar/Android.bp b/jar/Android.bp
index 23ad536..6c2e60e 100644
--- a/jar/Android.bp
+++ b/jar/Android.bp
@@ -18,5 +18,8 @@
     srcs: [
         "jar.go",
     ],
+    deps: [
+        "android-archive-zip",
+    ],
 }
 
diff --git a/jar/jar.go b/jar/jar.go
index 5960bf0..f17bc98 100644
--- a/jar/jar.go
+++ b/jar/jar.go
@@ -15,8 +15,14 @@
 package jar
 
 import (
+	"bytes"
 	"fmt"
+	"io/ioutil"
+	"os"
 	"strings"
+	"time"
+
+	"android/soong/third_party/zip"
 )
 
 const (
@@ -25,6 +31,10 @@
 	ModuleInfoClass = "module-info.class"
 )
 
+var DefaultTime = time.Date(2009, 1, 1, 0, 0, 0, 0, time.UTC)
+
+var MetaDirExtra = [2]byte{0xca, 0xfe}
+
 // EntryNamesLess tells whether <filepathA> should precede <filepathB> in
 // the order of files with a .jar
 func EntryNamesLess(filepathA string, filepathB string) (less bool) {
@@ -59,3 +69,56 @@
 	}
 	panic(fmt.Errorf("file %q did not match any pattern", name))
 }
+
+func MetaDirFileHeader() *zip.FileHeader {
+	dirHeader := &zip.FileHeader{
+		Name:  MetaDir,
+		Extra: []byte{MetaDirExtra[1], MetaDirExtra[0], 0, 0},
+	}
+	dirHeader.SetMode(0700 | os.ModeDir)
+	dirHeader.SetModTime(DefaultTime)
+
+	return dirHeader
+}
+
+// Convert manifest source path to zip header and contents.  If path is empty uses a default
+// manifest.
+func ManifestFileContents(src string) (*zip.FileHeader, []byte, error) {
+	b, err := manifestContents(src)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	fh := &zip.FileHeader{
+		Name:               ManifestFile,
+		Method:             zip.Store,
+		UncompressedSize64: uint64(len(b)),
+	}
+
+	return fh, b, nil
+}
+
+// Convert manifest source path to contents.  If path is empty uses a default manifest.
+func manifestContents(src string) ([]byte, error) {
+	var givenBytes []byte
+	var err error
+
+	if src != "" {
+		givenBytes, err = ioutil.ReadFile(src)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	manifestMarker := []byte("Manifest-Version:")
+	header := append(manifestMarker, []byte(" 1.0\nCreated-By: soong_zip\n")...)
+
+	var finalBytes []byte
+	if !bytes.Contains(givenBytes, manifestMarker) {
+		finalBytes = append(append(header, givenBytes...), byte('\n'))
+	} else {
+		finalBytes = givenBytes
+	}
+
+	return finalBytes, nil
+}
diff --git a/java/androidmk.go b/java/androidmk.go
index 680d864..89d7d51 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -25,13 +25,17 @@
 func (library *Library) AndroidMk() android.AndroidMkData {
 	return android.AndroidMkData{
 		Class:      "JAVA_LIBRARIES",
-		OutputFile: android.OptionalPathForPath(library.outputFile),
+		OutputFile: android.OptionalPathForPath(library.classpathFile),
 		Include:    "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
 		Extra: []android.AndroidMkExtraFunc{
 			func(w io.Writer, outputFile android.Path) {
 				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_SDK_VERSION :=", library.deviceProperties.Sdk_version)
 			},
 		},
 	}
@@ -53,7 +57,7 @@
 func (binary *Binary) AndroidMk() android.AndroidMkData {
 	return android.AndroidMkData{
 		Class:      "JAVA_LIBRARIES",
-		OutputFile: android.OptionalPathForPath(binary.outputFile),
+		OutputFile: android.OptionalPathForPath(binary.classpathFile),
 		Include:    "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
 		Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
 			android.WriteAndroidMkData(w, data)
diff --git a/java/builder.go b/java/builder.go
index d6f8c5b..b8332ad 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -78,17 +78,32 @@
 
 	combineJar = pctx.AndroidStaticRule("combineJar",
 		blueprint.RuleParams{
-			Command:     `${config.MergeZipsCmd} -j $out $in`,
+			Command:     `${config.MergeZipsCmd} -j $jarArgs $out $in`,
 			CommandDeps: []string{"${config.MergeZipsCmd}"},
 		},
-		"outDir")
+		"jarArgs")
+
+	desugar = pctx.AndroidStaticRule("desugar",
+		blueprint.RuleParams{
+			Command: `rm -rf $dumpDir && mkdir -p $dumpDir && ` +
+				`${config.JavaCmd} ` +
+				`-Djdk.internal.lambda.dumpProxyClasses=$$(cd $dumpDir && pwd) ` +
+				`$javaFlags ` +
+				`-jar ${config.DesugarJar} $classpathFlags $desugarFlags ` +
+				`-i $in -o $out`,
+			CommandDeps: []string{"${config.DesugarJar}"},
+		},
+		"javaFlags", "classpathFlags", "desugarFlags", "dumpDir")
 
 	dx = pctx.AndroidStaticRule("dx",
 		blueprint.RuleParams{
 			Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
-				`${config.DxCmd} --dex --output=$outDir $dxFlags $in || ( rm -rf "$outDir"; exit 41 ) && ` +
-				`find "$outDir" -name "classes*.dex" | sort > $out`,
-			CommandDeps: []string{"${config.DxCmd}"},
+				`${config.DxCmd} --dex --output=$outDir $dxFlags $in && ` +
+				`${config.SoongZipCmd} -jar -o $out -C $outDir -D $outDir`,
+			CommandDeps: []string{
+				"${config.DxCmd}",
+				"${config.SoongZipCmd}",
+			},
 		},
 		"outDir", "dxFlags")
 
@@ -107,8 +122,9 @@
 type javaBuilderFlags struct {
 	javacFlags    string
 	dxFlags       string
-	bootClasspath string
-	classpath     string
+	bootClasspath classpath
+	classpath     classpath
+	desugarFlags  string
 	aidlFlags     string
 	javaVersion   string
 }
@@ -126,11 +142,13 @@
 
 	classDir := android.PathForModuleOut(ctx, "classes")
 	annoDir := android.PathForModuleOut(ctx, "anno")
-	classJar := android.PathForModuleOut(ctx, "classes.jar")
+	classJar := android.PathForModuleOut(ctx, "classes-compiled.jar")
 
 	javacFlags := flags.javacFlags + android.JoinWithPrefix(srcFileLists.Strings(), "@")
 
 	deps = append(deps, srcFileLists...)
+	deps = append(deps, flags.bootClasspath...)
+	deps = append(deps, flags.classpath...)
 
 	ctx.ModuleBuild(pctx, android.ModuleBuildParams{
 		Rule:        javac,
@@ -140,8 +158,8 @@
 		Implicits:   deps,
 		Args: map[string]string{
 			"javacFlags":    javacFlags,
-			"bootClasspath": flags.bootClasspath,
-			"classpath":     flags.classpath,
+			"bootClasspath": flags.bootClasspath.JavaBootClasspath(ctx.Device()),
+			"classpath":     flags.classpath.JavaClasspath(),
 			"outDir":        classDir.String(),
 			"annoDir":       annoDir.String(),
 			"javaVersion":   flags.javaVersion,
@@ -166,6 +184,8 @@
 	javacFlags := flags.javacFlags + android.JoinWithPrefix(srcFileLists.Strings(), "@")
 
 	deps = append(deps, srcFileLists...)
+	deps = append(deps, flags.bootClasspath...)
+	deps = append(deps, flags.classpath...)
 
 	ctx.ModuleBuild(pctx, android.ModuleBuildParams{
 		Rule:        errorprone,
@@ -175,8 +195,8 @@
 		Implicits:   deps,
 		Args: map[string]string{
 			"javacFlags":    javacFlags,
-			"bootClasspath": flags.bootClasspath,
-			"classpath":     flags.classpath,
+			"bootClasspath": flags.bootClasspath.JavaBootClasspath(ctx.Device()),
+			"classpath":     flags.classpath.JavaClasspath(),
 			"outDir":        classDir.String(),
 			"annoDir":       annoDir.String(),
 			"javaVersion":   flags.javaVersion,
@@ -187,7 +207,7 @@
 }
 
 func TransformResourcesToJar(ctx android.ModuleContext, resources []jarSpec,
-	manifest android.OptionalPath, deps android.Paths) android.Path {
+	deps android.Paths) android.Path {
 
 	outputFile := android.PathForModuleOut(ctx, "res.jar")
 
@@ -198,11 +218,6 @@
 		jarArgs = append(jarArgs, j.soongJarArgs())
 	}
 
-	if manifest.Valid() {
-		deps = append(deps, manifest.Path())
-		jarArgs = append(jarArgs, "-m "+manifest.String())
-	}
-
 	ctx.ModuleBuild(pctx, android.ModuleBuildParams{
 		Rule:        jar,
 		Description: "jar",
@@ -216,29 +231,82 @@
 	return outputFile
 }
 
-func TransformJarsToJar(ctx android.ModuleContext, stem string, jars android.Paths) android.Path {
+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 {
+	if len(jars) == 1 && !manifest.Valid() {
 		return jars[0]
 	}
 
+	var deps android.Paths
+
+	var jarArgs []string
+	if manifest.Valid() {
+		jarArgs = append(jarArgs, "-m "+manifest.String())
+		deps = append(deps, manifest.Path())
+	}
+
+	if stripDirs {
+		jarArgs = append(jarArgs, "-D")
+	}
+
 	ctx.ModuleBuild(pctx, android.ModuleBuildParams{
 		Rule:        combineJar,
 		Description: "combine jars",
 		Output:      outputFile,
 		Inputs:      jars,
+		Implicits:   deps,
+		Args: map[string]string{
+			"jarArgs": strings.Join(jarArgs, " "),
+		},
 	})
 
 	return outputFile
 }
 
-func TransformClassesJarToDex(ctx android.ModuleContext, classesJar android.Path,
-	flags javaBuilderFlags) jarSpec {
+func TransformDesugar(ctx android.ModuleContext, classesJar android.Path,
+	flags javaBuilderFlags) android.Path {
+
+	outputFile := android.PathForModuleOut(ctx, "classes-desugar.jar")
+	dumpDir := android.PathForModuleOut(ctx, "desugar_dumped_classes")
+
+	javaFlags := ""
+	if ctx.AConfig().Getenv("EXPERIMENTAL_USE_OPENJDK9") != "" {
+		javaFlags = "--add-opens java.base/java.lang.invoke=ALL-UNNAMED"
+	}
+
+	var desugarFlags []string
+	desugarFlags = append(desugarFlags, flags.bootClasspath.DesugarBootClasspath()...)
+	desugarFlags = append(desugarFlags, flags.classpath.DesugarClasspath()...)
+
+	var deps android.Paths
+	deps = append(deps, flags.bootClasspath...)
+	deps = append(deps, flags.classpath...)
+
+	ctx.ModuleBuild(pctx, android.ModuleBuildParams{
+		Rule:        desugar,
+		Description: "desugar",
+		Output:      outputFile,
+		Input:       classesJar,
+		Implicits:   deps,
+		Args: map[string]string{
+			"dumpDir":        dumpDir.String(),
+			"javaFlags":      javaFlags,
+			"classpathFlags": strings.Join(desugarFlags, " "),
+			"desugarFlags":   flags.desugarFlags,
+		},
+	})
+
+	return outputFile
+}
+
+func TransformClassesJarToDexJar(ctx android.ModuleContext, classesJar android.Path,
+	flags javaBuilderFlags) android.Path {
 
 	outDir := android.PathForModuleOut(ctx, "dex")
-	outputFile := android.PathForModuleOut(ctx, "dex.filelist")
+	outputFile := android.PathForModuleOut(ctx, "classes.dex.jar")
 
 	ctx.ModuleBuild(pctx, android.ModuleBuildParams{
 		Rule:        dx,
@@ -251,34 +319,6 @@
 		},
 	})
 
-	return jarSpec{outputFile, outDir}
-}
-
-func TransformDexToJavaLib(ctx android.ModuleContext, resources []jarSpec,
-	dexJarSpec jarSpec) android.Path {
-
-	outputFile := android.PathForModuleOut(ctx, "javalib.jar")
-	var deps android.Paths
-	var jarArgs []string
-
-	for _, j := range resources {
-		deps = append(deps, j.fileList)
-		jarArgs = append(jarArgs, j.soongJarArgs())
-	}
-
-	deps = append(deps, dexJarSpec.fileList)
-	jarArgs = append(jarArgs, dexJarSpec.soongJarArgs())
-
-	ctx.ModuleBuild(pctx, android.ModuleBuildParams{
-		Rule:        jar,
-		Description: "jar",
-		Output:      outputFile,
-		Implicits:   deps,
-		Args: map[string]string{
-			"jarArgs": strings.Join(jarArgs, " "),
-		},
-	})
-
 	return outputFile
 }
 
@@ -297,3 +337,83 @@
 
 	return outputFile
 }
+
+type classpath []android.Path
+
+// Returns a -classpath argument in the form java or javac expects
+func (x *classpath) JavaClasspath() string {
+	if len(*x) > 0 {
+		return "-classpath " + strings.Join(x.Strings(), ":")
+	} else {
+		return ""
+	}
+}
+
+// Returns a -processorpath argument in the form java or javac expects
+func (x *classpath) JavaProcessorpath() string {
+	if len(*x) > 0 {
+		return "-processorpath " + strings.Join(x.Strings(), ":")
+	} else {
+		return ""
+	}
+}
+
+// Returns a -bootclasspath argument in the form java or javac expects.  If forceEmpty is true,
+// returns -bootclasspath "" if the bootclasspath is empty to ensure javac does not fall back to the
+// default bootclasspath.
+func (x *classpath) JavaBootClasspath(forceEmpty bool) string {
+	if len(*x) > 0 {
+		return "-bootclasspath " + strings.Join(x.Strings(), ":")
+	} else if forceEmpty {
+		return `-bootclasspath ""`
+	} else {
+		return ""
+	}
+}
+
+func (x *classpath) DesugarBootClasspath() []string {
+	if x == nil || *x == nil {
+		return nil
+	}
+	flags := make([]string, len(*x))
+	for i, v := range *x {
+		flags[i] = "--bootclasspath_entry " + v.String()
+	}
+
+	return flags
+}
+
+func (x *classpath) DesugarClasspath() []string {
+	if x == nil || *x == nil {
+		return nil
+	}
+	flags := make([]string, len(*x))
+	for i, v := range *x {
+		flags[i] = "--classpath_entry " + v.String()
+	}
+
+	return flags
+}
+
+// Append an android.Paths to the end of the classpath list
+func (x *classpath) AddPaths(paths android.Paths) {
+	for _, path := range paths {
+		*x = append(*x, path)
+	}
+}
+
+// Convert a classpath to an android.Paths
+func (x *classpath) Paths() android.Paths {
+	return append(android.Paths(nil), (*x)...)
+}
+
+func (x *classpath) Strings() []string {
+	if x == nil {
+		return nil
+	}
+	ret := make([]string, len(*x))
+	for i, path := range *x {
+		ret[i] = path.String()
+	}
+	return ret
+}
diff --git a/java/config/config.go b/java/config/config.go
index 69d6fc3..3029a5a 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -68,6 +68,7 @@
 	pctx.StaticVariable("MergeZipsCmd", filepath.Join("${bootstrap.ToolDir}", "merge_zips"))
 	pctx.HostBinToolVariable("DxCmd", "dx")
 	pctx.HostJavaToolVariable("JarjarCmd", "jarjar.jar")
+	pctx.HostJavaToolVariable("DesugarJar", "desugar.jar")
 
 	pctx.VariableFunc("JavacWrapper", func(config interface{}) (string, error) {
 		if override := config.(android.Config).Getenv("JAVAC_WRAPPER"); override != "" {
diff --git a/java/java.go b/java/java.go
index b4ce35e..2ded80b 100644
--- a/java/java.go
+++ b/java/java.go
@@ -137,6 +137,12 @@
 	// output file suitable for inserting into the classpath of another compile
 	classpathFile android.Path
 
+	// output file containing classes.dex
+	dexJarFile android.Path
+
+	// output files containing resources
+	resourceJarFiles android.Paths
+
 	// output file suitable for installing or running
 	outputFile android.Path
 
@@ -154,6 +160,7 @@
 
 type Dependency interface {
 	ClasspathFiles() android.Paths
+	ResourceJarFiles() android.Paths
 	AidlIncludeDirs() android.Paths
 }
 
@@ -168,12 +175,11 @@
 }
 
 var (
-	staticLibTag           = dependencyTag{name: "staticlib"}
-	libTag                 = dependencyTag{name: "javalib"}
-	bootClasspathTag       = dependencyTag{name: "bootclasspath"}
-	frameworkResTag        = dependencyTag{name: "framework-res"}
-	sdkDependencyTag       = dependencyTag{name: "sdk"}
-	annotationProcessorTag = dependencyTag{name: "annotation processor"}
+	staticLibTag     = dependencyTag{name: "staticlib"}
+	libTag           = dependencyTag{name: "javalib"}
+	bootClasspathTag = dependencyTag{name: "bootclasspath"}
+	frameworkResTag  = dependencyTag{name: "framework-res"}
+	sdkDependencyTag = dependencyTag{name: "sdk"}
 )
 
 func (j *Module) deps(ctx android.BottomUpMutatorContext) {
@@ -202,7 +208,7 @@
 	}
 	ctx.AddDependency(ctx.Module(), libTag, j.properties.Libs...)
 	ctx.AddDependency(ctx.Module(), staticLibTag, j.properties.Static_libs...)
-	ctx.AddDependency(ctx.Module(), annotationProcessorTag, j.properties.Annotation_processors...)
+	ctx.AddDependency(ctx.Module(), libTag, j.properties.Annotation_processors...)
 
 	android.ExtractSourcesDeps(ctx, j.properties.Srcs)
 }
@@ -230,13 +236,13 @@
 }
 
 type deps struct {
-	classpath            android.Paths
-	bootClasspath        android.Paths
-	staticJars           android.Paths
-	aidlIncludeDirs      android.Paths
-	srcFileLists         android.Paths
-	annotationProcessors android.Paths
-	aidlPreprocess       android.OptionalPath
+	classpath          android.Paths
+	bootClasspath      android.Paths
+	staticJars         android.Paths
+	staticJarResources android.Paths
+	aidlIncludeDirs    android.Paths
+	srcFileLists       android.Paths
+	aidlPreprocess     android.OptionalPath
 }
 
 func (j *Module) collectDeps(ctx android.ModuleContext) deps {
@@ -264,8 +270,7 @@
 		case staticLibTag:
 			deps.classpath = append(deps.classpath, dep.ClasspathFiles()...)
 			deps.staticJars = append(deps.staticJars, dep.ClasspathFiles()...)
-		case annotationProcessorTag:
-			deps.annotationProcessors = append(deps.annotationProcessors, dep.ClasspathFiles()...)
+			deps.staticJarResources = append(deps.staticJarResources, dep.ResourceJarFiles()...)
 		case frameworkResTag:
 			if ctx.ModuleName() == "framework" {
 				// framework.jar has a one-off dependency on the R.java and Manifest.java files
@@ -306,21 +311,17 @@
 		javacFlags = config.StripJavac9Flags(javacFlags)
 	}
 
-	if len(deps.annotationProcessors) > 0 {
-		javacFlags = append(javacFlags,
-			"-processorpath "+strings.Join(deps.annotationProcessors.Strings(), ":"))
-	}
-
-	for _, c := range j.properties.Annotation_processor_classes {
-		javacFlags = append(javacFlags, "-processor "+c)
-	}
-
 	if j.properties.Java_version != nil {
 		flags.javaVersion = *j.properties.Java_version
 	} else {
 		flags.javaVersion = "${config.DefaultJavaVersion}"
 	}
 
+	var extraDeps android.Paths
+
+	flags.bootClasspath.AddPaths(deps.bootClasspath)
+	flags.classpath.AddPaths(deps.classpath)
+
 	if len(javacFlags) > 0 {
 		ctx.Variable(pctx, "javacFlags", strings.Join(javacFlags, " "))
 		flags.javacFlags = "$javacFlags"
@@ -332,21 +333,6 @@
 		flags.aidlFlags = "$aidlFlags"
 	}
 
-	var extraDeps android.Paths
-
-	if len(deps.bootClasspath) > 0 {
-		flags.bootClasspath = "-bootclasspath " + strings.Join(deps.bootClasspath.Strings(), ":")
-		extraDeps = append(extraDeps, deps.bootClasspath...)
-	} else if ctx.Device() {
-		// Explicitly clear the bootclasspath for device builds
-		flags.bootClasspath = `-bootclasspath ""`
-	}
-
-	if len(deps.classpath) > 0 {
-		flags.classpath = "-classpath " + strings.Join(deps.classpath.Strings(), ":")
-		extraDeps = append(extraDeps, deps.classpath...)
-	}
-
 	srcFiles := ctx.ExpandSources(j.properties.Srcs, j.properties.Exclude_srcs)
 
 	srcFiles = j.genSources(ctx, srcFiles, flags)
@@ -385,23 +371,29 @@
 	}
 
 	resourceJarSpecs := ResourceDirsToJarSpecs(ctx, j.properties.Resource_dirs, j.properties.Exclude_resource_dirs)
-	manifest := android.OptionalPathForModuleSrc(ctx, j.properties.Manifest)
-
-	if len(resourceJarSpecs) > 0 || manifest.Valid() {
+	if len(resourceJarSpecs) > 0 {
 		// Combine classes + resources into classes-full-debug.jar
-		resourceJar := TransformResourcesToJar(ctx, resourceJarSpecs, manifest, extraJarDeps)
+		resourceJar := TransformResourcesToJar(ctx, resourceJarSpecs, extraJarDeps)
 		if ctx.Failed() {
 			return
 		}
 
+		j.resourceJarFiles = append(j.resourceJarFiles, resourceJar)
 		jars = append(jars, resourceJar)
 	}
 
+	// Propagate the resources from the transitive closure of static dependencies for copying
+	// into dex jars
+	j.resourceJarFiles = append(j.resourceJarFiles, deps.staticJarResources...)
+
+	// static classpath jars have the resources in them, so the resource jars aren't necessary here
 	jars = append(jars, deps.staticJars...)
 
+	manifest := android.OptionalPathForModuleSrc(ctx, j.properties.Manifest)
+
 	// Combine the classes built from sources, any manifests, and any static libraries into
 	// classes-combined.jar.  If there is only one input jar this step will be skipped.
-	outputFile := TransformJarsToJar(ctx, "classes-combined.jar", jars)
+	outputFile := TransformJarsToJar(ctx, "classes.jar", jars, manifest, false)
 
 	if j.properties.Jarjar_rules != nil {
 		jarjar_rules := android.PathForModuleSrc(ctx, *j.properties.Jarjar_rules)
@@ -450,14 +442,39 @@
 
 		flags.dxFlags = strings.Join(dxFlags, " ")
 
-		// Compile classes.jar into classes.dex
-		dexJarSpec := TransformClassesJarToDex(ctx, outputFile, flags)
+		desugarFlags := []string{
+			"--min_sdk_version " + minSdkVersion,
+			"--desugar_try_with_resources_if_needed=false",
+			"--allow_empty_bootclasspath",
+		}
+
+		if inList("--core-library", dxFlags) {
+			desugarFlags = append(desugarFlags, "--core_library")
+		}
+
+		flags.desugarFlags = strings.Join(desugarFlags, " ")
+
+		desugarJar := TransformDesugar(ctx, outputFile, flags)
 		if ctx.Failed() {
 			return
 		}
 
-		// Combine classes.dex + resources into javalib.jar
-		outputFile = TransformDexToJavaLib(ctx, resourceJarSpecs, dexJarSpec)
+		// TODO(ccross): For now, use the desugared jar as the classpath file.  Eventually this
+		// might cause problems because desugar wants non-desugared jars in its class path.
+		j.classpathFile = desugarJar
+
+		// Compile classes.jar into classes.dex
+		dexJarFile := TransformClassesJarToDexJar(ctx, desugarJar, flags)
+		if ctx.Failed() {
+			return
+		}
+
+		jars := android.Paths{dexJarFile}
+		jars = append(jars, j.resourceJarFiles...)
+
+		outputFile = TransformJarsToJar(ctx, "javalib.jar", jars, android.OptionalPath{}, true)
+
+		j.dexJarFile = outputFile
 	}
 	ctx.CheckbuildFile(outputFile)
 	j.outputFile = outputFile
@@ -469,6 +486,10 @@
 	return android.Paths{j.classpathFile}
 }
 
+func (j *Module) ResourceJarFiles() android.Paths {
+	return j.resourceJarFiles
+}
+
 func (j *Module) AidlIncludeDirs() android.Paths {
 	return j.exportAidlIncludeDirs
 }
@@ -616,7 +637,7 @@
 func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	j.classpathFiles = android.PathsForModuleSrc(ctx, j.properties.Jars)
 
-	j.combinedClasspathFile = TransformJarsToJar(ctx, "classes.jar", j.classpathFiles)
+	j.combinedClasspathFile = TransformJarsToJar(ctx, "classes.jar", j.classpathFiles, android.OptionalPath{}, false)
 }
 
 var _ Dependency = (*Import)(nil)
@@ -625,6 +646,11 @@
 	return j.classpathFiles
 }
 
+func (j *Import) ResourceJarFiles() android.Paths {
+	// resources are in the ClasspathFiles
+	return nil
+}
+
 func (j *Import) AidlIncludeDirs() android.Paths {
 	return nil
 }
@@ -687,9 +713,11 @@
 func SdkPrebuiltFactory() android.Module {
 	module := &sdkPrebuilt{}
 
-	module.AddProperties(&module.sdkProperties)
+	module.AddProperties(
+		&module.sdkProperties,
+		&module.Import.properties)
 
-	android.InitPrebuiltModule(module, &module.properties.Jars)
+	android.InitPrebuiltModule(module, &module.Import.properties.Jars)
 	android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon)
 	return module
 }
diff --git a/java/java_test.go b/java/java_test.go
index 4f5c0ec..040adb4 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -51,19 +51,30 @@
 }
 
 func testJava(t *testing.T, bp string) *android.TestContext {
-	config := android.TestConfig(buildDir)
+	config := android.TestArchConfig(buildDir)
 
-	ctx := android.NewTestContext()
+	ctx := android.NewTestArchContext()
 	ctx.RegisterModuleType("android_app", android.ModuleFactoryAdaptor(AndroidAppFactory))
 	ctx.RegisterModuleType("java_library", android.ModuleFactoryAdaptor(LibraryFactory))
+	ctx.RegisterModuleType("java_library_host", android.ModuleFactoryAdaptor(LibraryHostFactory))
 	ctx.RegisterModuleType("java_import", android.ModuleFactoryAdaptor(ImportFactory))
 	ctx.RegisterModuleType("java_defaults", android.ModuleFactoryAdaptor(defaultsFactory))
+	ctx.RegisterModuleType("android_prebuilt_sdk", android.ModuleFactoryAdaptor(SdkPrebuiltFactory))
 	ctx.PreArchMutators(android.RegisterPrebuiltsPreArchMutators)
 	ctx.PreArchMutators(android.RegisterPrebuiltsPostDepsMutators)
 	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
 	ctx.Register()
 
-	extraModules := []string{"core-oj", "core-libart", "frameworks", "sdk_v14"}
+	extraModules := []string{
+		"core-oj",
+		"core-libart",
+		"framework",
+		"ext",
+		"okhttp",
+		"android_stubs_current",
+		"android_system_stubs_current",
+		"android_test_stubs_current",
+	}
 
 	for _, extra := range extraModules {
 		bp += fmt.Sprintf(`
@@ -75,13 +86,21 @@
 		`, extra)
 	}
 
+	bp += `
+		android_prebuilt_sdk {
+			name: "sdk_v14",
+			jars: ["sdk_v14.jar"],
+		}
+	`
+
 	ctx.MockFileSystem(map[string][]byte{
-		"Android.bp": []byte(bp),
-		"a.java":     nil,
-		"b.java":     nil,
-		"c.java":     nil,
-		"a.jar":      nil,
-		"b.jar":      nil,
+		"Android.bp":  []byte(bp),
+		"a.java":      nil,
+		"b.java":      nil,
+		"c.java":      nil,
+		"a.jar":       nil,
+		"b.jar":       nil,
+		"sdk_v14.jar": nil,
 	})
 
 	_, errs := ctx.ParseBlueprintsFiles("Android.bp")
@@ -92,6 +111,17 @@
 	return ctx
 }
 
+func moduleToPath(name string) string {
+	switch {
+	case name == `""`:
+		return name
+	case strings.HasPrefix(name, "sdk_v"):
+		return name + ".jar"
+	default:
+		return filepath.Join(buildDir, ".intermediates", name, "android_common", "classes-desugar.jar")
+	}
+}
+
 func TestSimple(t *testing.T) {
 	ctx := testJava(t, `
 		java_library {
@@ -112,15 +142,15 @@
 		}
 		`)
 
-	javac := ctx.ModuleForTests("foo", "").Rule("javac")
-	combineJar := ctx.ModuleForTests("foo", "").Rule("combineJar")
+	javac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
+	combineJar := ctx.ModuleForTests("foo", "android_common").Rule("combineJar")
 
 	if len(javac.Inputs) != 1 || javac.Inputs[0].String() != "a.java" {
 		t.Errorf(`foo inputs %v != ["a.java"]`, javac.Inputs)
 	}
 
-	bar := filepath.Join(buildDir, ".intermediates", "bar", "classes.jar")
-	baz := filepath.Join(buildDir, ".intermediates", "baz", "classes.jar")
+	bar := filepath.Join(buildDir, ".intermediates", "bar", "android_common", "classes-desugar.jar")
+	baz := filepath.Join(buildDir, ".intermediates", "baz", "android_common", "classes-desugar.jar")
 
 	if !strings.Contains(javac.Args["classpath"], bar) {
 		t.Errorf("foo classpath %v does not contain %q", javac.Args["classpath"], bar)
@@ -135,83 +165,130 @@
 	}
 }
 
-func TestSdk(t *testing.T) {
-	ctx := testJava(t, `
-		java_library {
-			name: "foo1",
-			srcs: ["a.java"],
-		}
+var classpathTestcases = []struct {
+	name          string
+	host          android.OsClass
+	properties    string
+	bootclasspath []string
+	classpath     []string
+}{
+	{
+		name:          "default",
+		bootclasspath: []string{"core-oj", "core-libart"},
+		classpath:     []string{"ext", "framework", "okhttp"},
+	},
+	{
+		name:          "blank sdk version",
+		properties:    `sdk_version: "",`,
+		bootclasspath: []string{"core-oj", "core-libart"},
+		classpath:     []string{"ext", "framework", "okhttp"},
+	},
+	{
 
-		java_library {
-			name: "foo2",
-			srcs: ["a.java"],
-			sdk_version: "",
-		}
+		name:          "sdk v14",
+		properties:    `sdk_version: "14",`,
+		bootclasspath: []string{"sdk_v14"},
+		classpath:     []string{},
+	},
+	{
 
-		java_library {
-			name: "foo3",
-			srcs: ["a.java"],
-			sdk_version: "14",
-		}
+		name:          "current",
+		properties:    `sdk_version: "current",`,
+		bootclasspath: []string{"android_stubs_current"},
+		classpath:     []string{},
+	},
+	{
 
-		java_library {
-			name: "foo4",
-			srcs: ["a.java"],
-			sdk_version: "current",
-		}
+		name:          "system_current",
+		properties:    `sdk_version: "system_current",`,
+		bootclasspath: []string{"android_system_stubs_current"},
+		classpath:     []string{},
+	},
+	{
 
-		java_library {
-			name: "foo5",
-			srcs: ["a.java"],
-			sdk_version: "system_current",
-		}
+		name:          "test_current",
+		properties:    `sdk_version: "test_current",`,
+		bootclasspath: []string{"android_test_stubs_current"},
+		classpath:     []string{},
+	},
+	{
 
-		java_library {
-			name: "foo6",
-			srcs: ["a.java"],
-			sdk_version: "test_current",
-		}
-		`)
+		name:          "nostdlib",
+		properties:    `no_standard_libs: true`,
+		bootclasspath: []string{`""`},
+		classpath:     []string{},
+	},
+	{
 
-	type depType int
-	const (
-		staticLib = iota
-		classpathLib
-		bootclasspathLib
-	)
+		name:       "host default",
+		host:       android.Host,
+		properties: ``,
+		classpath:  []string{},
+	},
+	{
+		name:       "host nostdlib",
+		host:       android.Host,
+		properties: `no_standard_libs: true`,
+		classpath:  []string{},
+	},
+}
 
-	check := func(module string, depType depType, deps ...string) {
-		for i := range deps {
-			deps[i] = filepath.Join(buildDir, ".intermediates", deps[i], "classes.jar")
-		}
-		dep := strings.Join(deps, ":")
+func TestClasspath(t *testing.T) {
+	for _, testcase := range classpathTestcases {
+		t.Run(testcase.name, func(t *testing.T) {
+			hostExtra := ""
+			if testcase.host == android.Host {
+				hostExtra = "_host"
+			}
+			ctx := testJava(t, `
+			java_library`+hostExtra+` {
+				name: "foo",
+				srcs: ["a.java"],
+				`+testcase.properties+`
+			}
+			`)
 
-		javac := ctx.ModuleForTests(module, "").Rule("javac")
+			convertModulesToPaths := func(cp []string) []string {
+				ret := make([]string, len(cp))
+				for i, e := range cp {
+					ret[i] = moduleToPath(e)
+				}
+				return ret
+			}
 
-		if depType == bootclasspathLib {
+			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 ")
-			if got != dep {
-				t.Errorf("module %q bootclasspath %q != %q", module, got, dep)
+			bc := strings.Join(bootclasspath, ":")
+			if got != bc {
+				t.Errorf("bootclasspath expected %q != got %q", bc, got)
 			}
-		} else if depType == classpathLib {
-			got := strings.TrimPrefix(javac.Args["classpath"], "-classpath ")
-			if got != dep {
-				t.Errorf("module %q classpath %q != %q", module, got, dep)
-			}
-		}
 
-		if !reflect.DeepEqual(javac.Implicits.Strings(), deps) {
-			t.Errorf("module %q implicits %q != %q", module, javac.Implicits.Strings(), deps)
-		}
+			got = strings.TrimPrefix(javac.Args["classpath"], "-classpath ")
+			c := strings.Join(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())
+			}
+		})
 	}
 
-	check("foo1", bootclasspathLib, "core-oj", "core-libart")
-	check("foo2", bootclasspathLib, "core-oj", "core-libart")
-	// TODO(ccross): these need the arch mutator to run to work correctly
-	//check("foo3", bootclasspathLib, "sdk_v14")
-	//check("foo4", bootclasspathLib, "android_stubs_current")
-	//check("foo5", bootclasspathLib, "android_system_stubs_current")
-	//check("foo6", bootclasspathLib, "android_test_stubs_current")
 }
 
 func TestPrebuilts(t *testing.T) {
@@ -234,8 +311,8 @@
 		}
 		`)
 
-	javac := ctx.ModuleForTests("foo", "").Rule("javac")
-	combineJar := ctx.ModuleForTests("foo", "").Rule("combineJar")
+	javac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
+	combineJar := ctx.ModuleForTests("foo", "android_common").Rule("combineJar")
 
 	bar := "a.jar"
 	if !strings.Contains(javac.Args["classpath"], bar) {
@@ -272,19 +349,19 @@
 		}
 		`)
 
-	javac := ctx.ModuleForTests("foo", "").Rule("javac")
-	combineJar := ctx.ModuleForTests("foo", "").Rule("combineJar")
+	javac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
+	combineJar := ctx.ModuleForTests("foo", "android_common").Rule("combineJar")
 
 	if len(javac.Inputs) != 1 || javac.Inputs[0].String() != "a.java" {
 		t.Errorf(`foo inputs %v != ["a.java"]`, javac.Inputs)
 	}
 
-	bar := filepath.Join(buildDir, ".intermediates", "bar", "classes.jar")
+	bar := filepath.Join(buildDir, ".intermediates", "bar", "android_common", "classes-desugar.jar")
 	if !strings.Contains(javac.Args["classpath"], bar) {
 		t.Errorf("foo classpath %v does not contain %q", javac.Args["classpath"], bar)
 	}
 
-	baz := filepath.Join(buildDir, ".intermediates", "baz", "classes.jar")
+	baz := filepath.Join(buildDir, ".intermediates", "baz", "android_common", "classes-desugar.jar")
 	if len(combineJar.Inputs) != 2 || combineJar.Inputs[1].String() != baz {
 		t.Errorf("foo combineJar inputs %v does not contain %q", combineJar.Inputs, baz)
 	}