Merge "Update jacoco command line flags for latest version"
diff --git a/Android.bp b/Android.bp
index ebf1038..ba71789 100644
--- a/Android.bp
+++ b/Android.bp
@@ -51,6 +51,7 @@
         "android/module.go",
         "android/mutator.go",
         "android/namespace.go",
+        "android/neverallow.go",
         "android/onceper.go",
         "android/package_ctx.go",
         "android/paths.go",
diff --git a/android/arch.go b/android/arch.go
index 3a22569..e696a0d 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -119,12 +119,12 @@
 
 func RegisterArchVariantFeatures(arch ArchType, variant string, features ...string) {
 	checkCalledFromInit()
-	if variant != "" && !inList(variant, archVariants[arch]) {
+	if variant != "" && !InList(variant, archVariants[arch]) {
 		panic(fmt.Errorf("Invalid variant %q for arch %q", variant, arch))
 	}
 
 	for _, feature := range features {
-		if !inList(feature, archFeatures[arch]) {
+		if !InList(feature, archFeatures[arch]) {
 			panic(fmt.Errorf("Invalid feature %q for arch %q variant %q", feature, arch, variant))
 		}
 	}
@@ -481,13 +481,13 @@
 
 			if os.Linux() {
 				target := "Linux_" + archType.Name
-				if !inList(target, targets) {
+				if !InList(target, targets) {
 					targets = append(targets, target)
 				}
 			}
 			if os.Bionic() {
 				target := "Bionic_" + archType.Name
-				if !inList(target, targets) {
+				if !InList(target, targets) {
 					targets = append(targets, target)
 				}
 			}
diff --git a/android/config.go b/android/config.go
index 887291d..07e25f3 100644
--- a/android/config.go
+++ b/android/config.go
@@ -690,12 +690,12 @@
 func (c *deviceConfig) CoverageEnabledForPath(path string) bool {
 	coverage := false
 	if c.config.ProductVariables.CoveragePaths != nil {
-		if prefixInList(path, *c.config.ProductVariables.CoveragePaths) {
+		if PrefixInList(path, *c.config.ProductVariables.CoveragePaths) {
 			coverage = true
 		}
 	}
 	if coverage && c.config.ProductVariables.CoverageExcludePaths != nil {
-		if prefixInList(path, *c.config.ProductVariables.CoverageExcludePaths) {
+		if PrefixInList(path, *c.config.ProductVariables.CoverageExcludePaths) {
 			coverage = false
 		}
 	}
@@ -706,21 +706,21 @@
 	if c.ProductVariables.IntegerOverflowExcludePaths == nil {
 		return false
 	}
-	return prefixInList(path, *c.ProductVariables.IntegerOverflowExcludePaths)
+	return PrefixInList(path, *c.ProductVariables.IntegerOverflowExcludePaths)
 }
 
 func (c *config) CFIDisabledForPath(path string) bool {
 	if c.ProductVariables.CFIExcludePaths == nil {
 		return false
 	}
-	return prefixInList(path, *c.ProductVariables.CFIExcludePaths)
+	return PrefixInList(path, *c.ProductVariables.CFIExcludePaths)
 }
 
 func (c *config) CFIEnabledForPath(path string) bool {
 	if c.ProductVariables.CFIIncludePaths == nil {
 		return false
 	}
-	return prefixInList(path, *c.ProductVariables.CFIIncludePaths)
+	return PrefixInList(path, *c.ProductVariables.CFIIncludePaths)
 }
 
 func stringSlice(s *[]string) []string {
diff --git a/android/module.go b/android/module.go
index cb068ab..0fb9479 100644
--- a/android/module.go
+++ b/android/module.go
@@ -139,6 +139,7 @@
 
 	VisitDirectDepsBlueprint(visit func(blueprint.Module))
 	VisitDirectDeps(visit func(Module))
+	VisitDirectDepsWithTag(tag blueprint.DependencyTag, visit func(Module))
 	VisitDirectDepsIf(pred func(Module) bool, visit func(Module))
 	VisitDepsDepthFirst(visit func(Module))
 	VisitDepsDepthFirstIf(pred func(Module) bool, visit func(Module))
@@ -831,6 +832,16 @@
 	})
 }
 
+func (a *androidModuleContext) VisitDirectDepsWithTag(tag blueprint.DependencyTag, visit func(Module)) {
+	a.ModuleContext.VisitDirectDeps(func(module blueprint.Module) {
+		if aModule := a.validateAndroidModule(module); aModule != nil {
+			if a.ModuleContext.OtherModuleDependencyTag(aModule) == tag {
+				visit(aModule)
+			}
+		}
+	})
+}
+
 func (a *androidModuleContext) VisitDirectDepsIf(pred func(Module) bool, visit func(Module)) {
 	a.ModuleContext.VisitDirectDepsIf(
 		// pred
diff --git a/android/mutator.go b/android/mutator.go
index 876d161..2f13f6c 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -92,6 +92,7 @@
 
 var postDeps = []RegisterMutatorFunc{
 	RegisterPrebuiltsPostDepsMutators,
+	registerNeverallowMutator,
 }
 
 func PreArchMutators(f RegisterMutatorFunc) {
@@ -126,6 +127,7 @@
 	GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag)
 
 	VisitDirectDeps(visit func(Module))
+	VisitDirectDepsWithTag(tag blueprint.DependencyTag, visit func(Module))
 	VisitDirectDepsIf(pred func(Module) bool, visit func(Module))
 	VisitDepsDepthFirst(visit func(Module))
 	VisitDepsDepthFirstIf(pred func(Module) bool, visit func(Module))
@@ -229,6 +231,16 @@
 	})
 }
 
+func (a *androidTopDownMutatorContext) VisitDirectDepsWithTag(tag blueprint.DependencyTag, visit func(Module)) {
+	a.TopDownMutatorContext.VisitDirectDeps(func(module blueprint.Module) {
+		if aModule, _ := module.(Module); aModule != nil {
+			if a.TopDownMutatorContext.OtherModuleDependencyTag(aModule) == tag {
+				visit(aModule)
+			}
+		}
+	})
+}
+
 func (a *androidTopDownMutatorContext) VisitDirectDepsIf(pred func(Module) bool, visit func(Module)) {
 	a.TopDownMutatorContext.VisitDirectDepsIf(
 		// pred
diff --git a/android/neverallow.go b/android/neverallow.go
new file mode 100644
index 0000000..261f2ee
--- /dev/null
+++ b/android/neverallow.go
@@ -0,0 +1,273 @@
+// 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 android
+
+import (
+	"path/filepath"
+	"reflect"
+	"strconv"
+	"strings"
+
+	"github.com/google/blueprint/proptools"
+)
+
+// "neverallow" rules for the build system.
+//
+// This allows things which aren't related to the build system and are enforced
+// for sanity, in progress code refactors, or policy to be expressed in a
+// straightforward away disjoint from implementations and tests which should
+// work regardless of these restrictions.
+//
+// A module is disallowed if all of the following are true:
+// - it is in one of the "in" paths
+// - it is not in one of the "notIn" paths
+// - it has all "with" properties matched
+// - - values are matched in their entirety
+// - - nil is interpreted as an empty string
+// - - nested properties are separated with a '.'
+// - - if the property is a list, any of the values in the list being matches
+//     counts as a match
+// - it has none of the "without" properties matched (same rules as above)
+
+func registerNeverallowMutator(ctx RegisterMutatorsContext) {
+	ctx.BottomUp("neverallow", neverallowMutator).Parallel()
+}
+
+var neverallows = []*rule{
+	neverallow().in("vendor", "device").with("vndk.enabled", "true").
+		because("the VNDK can never contain a library that is device dependent."),
+	neverallow().with("vndk.enabled", "true").without("owner", "").
+		because("a VNDK module can never have an owner."),
+	neverallow().notIn("libcore").with("no_standard_libs", "true"),
+
+	// TODO(b/67974785): always enforce the manifest
+	neverallow().
+		without("name", "libhidltransport").
+		with("product_variables.enforce_vintf_manifest.cflags", "*").
+		because("manifest enforcement should be independent of ."),
+
+	// TODO(b/67975799): vendor code should always use /vendor/bin/sh
+	neverallow().
+		without("name", "libc_bionic_ndk").
+		with("product_variables.treble_linker_namespaces.cflags", "*").
+		because("nothing should care if linker namespaces are enabled or not"),
+
+	// Example:
+	// *neverallow().with("Srcs", "main.cpp"),
+}
+
+func neverallowMutator(ctx BottomUpMutatorContext) {
+	m, ok := ctx.Module().(Module)
+	if !ok {
+		return
+	}
+
+	dir := ctx.ModuleDir() + "/"
+	properties := m.GetProperties()
+
+	for _, n := range neverallows {
+		if !n.appliesToPath(dir) {
+			continue
+		}
+
+		if !n.appliesToProperties(properties) {
+			continue
+		}
+
+		ctx.ModuleErrorf("violates " + n.String())
+	}
+}
+
+type ruleProperty struct {
+	fields []string // e.x.: Vndk.Enabled
+	value  string   // e.x.: true
+}
+
+type rule struct {
+	// User string for why this is a thing.
+	reason string
+
+	paths       []string
+	unlessPaths []string
+
+	props       []ruleProperty
+	unlessProps []ruleProperty
+}
+
+func neverallow() *rule {
+	return &rule{}
+}
+func (r *rule) in(path ...string) *rule {
+	r.paths = append(r.paths, cleanPaths(path)...)
+	return r
+}
+func (r *rule) notIn(path ...string) *rule {
+	r.unlessPaths = append(r.unlessPaths, cleanPaths(path)...)
+	return r
+}
+func (r *rule) with(properties, value string) *rule {
+	r.props = append(r.props, ruleProperty{
+		fields: fieldNamesForProperties(properties),
+		value:  value,
+	})
+	return r
+}
+func (r *rule) without(properties, value string) *rule {
+	r.unlessProps = append(r.unlessProps, ruleProperty{
+		fields: fieldNamesForProperties(properties),
+		value:  value,
+	})
+	return r
+}
+func (r *rule) because(reason string) *rule {
+	r.reason = reason
+	return r
+}
+
+func (r *rule) String() string {
+	s := "neverallow"
+	for _, v := range r.paths {
+		s += " dir:" + v + "*"
+	}
+	for _, v := range r.unlessPaths {
+		s += " -dir:" + v + "*"
+	}
+	for _, v := range r.props {
+		s += " " + strings.Join(v.fields, ".") + "=" + v.value
+	}
+	for _, v := range r.unlessProps {
+		s += " -" + strings.Join(v.fields, ".") + "=" + v.value
+	}
+	if len(r.reason) != 0 {
+		s += " which is restricted because " + r.reason
+	}
+	return s
+}
+
+func (r *rule) appliesToPath(dir string) bool {
+	includePath := len(r.paths) == 0 || hasAnyPrefix(dir, r.paths)
+	excludePath := hasAnyPrefix(dir, r.unlessPaths)
+	return includePath && !excludePath
+}
+
+func (r *rule) appliesToProperties(properties []interface{}) bool {
+	includeProps := hasAllProperties(properties, r.props)
+	excludeProps := hasAnyProperty(properties, r.unlessProps)
+	return includeProps && !excludeProps
+}
+
+// assorted utils
+
+func cleanPaths(paths []string) []string {
+	res := make([]string, len(paths))
+	for i, v := range paths {
+		res[i] = filepath.Clean(v) + "/"
+	}
+	return res
+}
+
+func fieldNamesForProperties(propertyNames string) []string {
+	names := strings.Split(propertyNames, ".")
+	for i, v := range names {
+		names[i] = proptools.FieldNameForProperty(v)
+	}
+	return names
+}
+
+func hasAnyPrefix(s string, prefixes []string) bool {
+	for _, prefix := range prefixes {
+		if strings.HasPrefix(s, prefix) {
+			return true
+		}
+	}
+	return false
+}
+
+func hasAnyProperty(properties []interface{}, props []ruleProperty) bool {
+	for _, v := range props {
+		if hasProperty(properties, v) {
+			return true
+		}
+	}
+	return false
+}
+
+func hasAllProperties(properties []interface{}, props []ruleProperty) bool {
+	for _, v := range props {
+		if !hasProperty(properties, v) {
+			return false
+		}
+	}
+	return true
+}
+
+func hasProperty(properties []interface{}, prop ruleProperty) bool {
+	for _, propertyStruct := range properties {
+		propertiesValue := reflect.ValueOf(propertyStruct).Elem()
+		for _, v := range prop.fields {
+			if !propertiesValue.IsValid() {
+				break
+			}
+			propertiesValue = propertiesValue.FieldByName(v)
+		}
+		if !propertiesValue.IsValid() {
+			continue
+		}
+
+		check := func(v string) bool {
+			return prop.value == "*" || prop.value == v
+		}
+
+		if matchValue(propertiesValue, check) {
+			return true
+		}
+	}
+	return false
+}
+
+func matchValue(value reflect.Value, check func(string) bool) bool {
+	if !value.IsValid() {
+		return false
+	}
+
+	if value.Kind() == reflect.Ptr {
+		if value.IsNil() {
+			return check("")
+		}
+		value = value.Elem()
+	}
+
+	switch value.Kind() {
+	case reflect.String:
+		return check(value.String())
+	case reflect.Bool:
+		return check(strconv.FormatBool(value.Bool()))
+	case reflect.Int:
+		return check(strconv.FormatInt(value.Int(), 10))
+	case reflect.Slice:
+		slice, ok := value.Interface().([]string)
+		if !ok {
+			panic("Can only handle slice of string")
+		}
+		for _, v := range slice {
+			if check(v) {
+				return true
+			}
+		}
+		return false
+	}
+
+	panic("Can't handle type: " + value.Kind().String())
+}
diff --git a/android/prebuilt.go b/android/prebuilt.go
index d3f9704..9356aab 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -109,13 +109,11 @@
 			p.properties.UsePrebuilt = p.usePrebuilt(ctx, nil)
 		}
 	} else if s, ok := ctx.Module().(Module); ok {
-		ctx.VisitDirectDeps(func(m Module) {
-			if ctx.OtherModuleDependencyTag(m) == prebuiltDepTag {
-				p := m.(PrebuiltInterface).Prebuilt()
-				if p.usePrebuilt(ctx, s) {
-					p.properties.UsePrebuilt = true
-					s.SkipInstall()
-				}
+		ctx.VisitDirectDepsWithTag(prebuiltDepTag, func(m Module) {
+			p := m.(PrebuiltInterface).Prebuilt()
+			if p.usePrebuilt(ctx, s) {
+				p.properties.UsePrebuilt = true
+				s.SkipInstall()
 			}
 		})
 	}
diff --git a/android/util.go b/android/util.go
index 29bb9b1..854d782 100644
--- a/android/util.go
+++ b/android/util.go
@@ -54,7 +54,7 @@
 	return s
 }
 
-func indexList(s string, list []string) int {
+func IndexList(s string, list []string) int {
 	for i, l := range list {
 		if l == s {
 			return i
@@ -64,11 +64,11 @@
 	return -1
 }
 
-func inList(s string, list []string) bool {
-	return indexList(s, list) != -1
+func InList(s string, list []string) bool {
+	return IndexList(s, list) != -1
 }
 
-func prefixInList(s string, list []string) bool {
+func PrefixInList(s string, list []string) bool {
 	for _, prefix := range list {
 		if strings.HasPrefix(s, prefix) {
 			return true
@@ -77,6 +77,37 @@
 	return false
 }
 
+func FilterList(list []string, filter []string) (remainder []string, filtered []string) {
+	for _, l := range list {
+		if InList(l, filter) {
+			filtered = append(filtered, l)
+		} else {
+			remainder = append(remainder, l)
+		}
+	}
+
+	return
+}
+
+func RemoveListFromList(list []string, filter_out []string) (result []string) {
+	result = make([]string, 0, len(list))
+	for _, l := range list {
+		if !InList(l, filter_out) {
+			result = append(result, l)
+		}
+	}
+	return
+}
+
+func RemoveFromList(s string, list []string) (bool, []string) {
+	i := IndexList(s, list)
+	if i != -1 {
+		return true, append(list[:i], list[i+1:]...)
+	} else {
+		return false, list
+	}
+}
+
 // FirstUniqueStrings returns all unique elements of a slice of strings, keeping the first copy of
 // each.  It modifies the slice contents in place, and returns a subslice of the original slice.
 func FirstUniqueStrings(list []string) []string {
diff --git a/cc/coverage.go b/cc/coverage.go
index d2eede2..391b118 100644
--- a/cc/coverage.go
+++ b/cc/coverage.go
@@ -59,11 +59,7 @@
 			// For static libraries, the only thing that changes our object files
 			// are included whole static libraries, so check to see if any of
 			// those have coverage enabled.
-			ctx.VisitDirectDeps(func(m android.Module) {
-				if ctx.OtherModuleDependencyTag(m) != wholeStaticDepTag {
-					return
-				}
-
+			ctx.VisitDirectDepsWithTag(wholeStaticDepTag, func(m android.Module) {
 				if cc, ok := m.(*Module); ok && cc.coverage != nil {
 					if cc.coverage.linkCoverage {
 						cov.linkCoverage = true
diff --git a/cc/util.go b/cc/util.go
index 7041029..5131b09 100644
--- a/cc/util.go
+++ b/cc/util.go
@@ -40,50 +40,11 @@
 	return android.JoinWithPrefix(names, "-l")
 }
 
-func indexList(s string, list []string) int {
-	for i, l := range list {
-		if l == s {
-			return i
-		}
-	}
-
-	return -1
-}
-
-func inList(s string, list []string) bool {
-	return indexList(s, list) != -1
-}
-
-func filterList(list []string, filter []string) (remainder []string, filtered []string) {
-	for _, l := range list {
-		if inList(l, filter) {
-			filtered = append(filtered, l)
-		} else {
-			remainder = append(remainder, l)
-		}
-	}
-
-	return
-}
-
-func removeListFromList(list []string, filter_out []string) (result []string) {
-	result = make([]string, 0, len(list))
-	for _, l := range list {
-		if !inList(l, filter_out) {
-			result = append(result, l)
-		}
-	}
-	return
-}
-
-func removeFromList(s string, list []string) (bool, []string) {
-	i := indexList(s, list)
-	if i != -1 {
-		return true, append(list[:i], list[i+1:]...)
-	} else {
-		return false, list
-	}
-}
+var indexList = android.IndexList
+var inList = android.InList
+var filterList = android.FilterList
+var removeListFromList = android.RemoveListFromList
+var removeFromList = android.RemoveFromList
 
 var libNameRegexp = regexp.MustCompile(`^lib(.*)$`)
 
diff --git a/finder/Android.bp b/finder/Android.bp
index b5c0e13..47b47c9 100644
--- a/finder/Android.bp
+++ b/finder/Android.bp
@@ -30,7 +30,7 @@
         "finder_test.go",
     ],
     deps: [
-      "soong-fs",
+      "soong-finder-fs",
     ],
 }
 
diff --git a/finder/cmd/finder.go b/finder/cmd/finder.go
index 70c1dc4..ab9108f 100644
--- a/finder/cmd/finder.go
+++ b/finder/cmd/finder.go
@@ -28,7 +28,7 @@
 	"time"
 
 	"android/soong/finder"
-	"android/soong/fs"
+	"android/soong/finder/fs"
 )
 
 var (
diff --git a/finder/finder.go b/finder/finder.go
index 2dd8e0b..89be0f5 100644
--- a/finder/finder.go
+++ b/finder/finder.go
@@ -30,7 +30,7 @@
 	"sync/atomic"
 	"time"
 
-	"android/soong/fs"
+	"android/soong/finder/fs"
 )
 
 // This file provides a Finder struct that can quickly search for files satisfying
@@ -1384,7 +1384,7 @@
 		f.onFsError(path, err)
 		// if listing the contents of the directory fails (presumably due to
 		// permission denied), then treat the directory as empty
-		children = []os.FileInfo{}
+		children = nil
 	}
 
 	var subdirs []string
diff --git a/finder/finder_test.go b/finder/finder_test.go
index 1522c68..29711fc 100644
--- a/finder/finder_test.go
+++ b/finder/finder_test.go
@@ -21,12 +21,12 @@
 	"os"
 	"path/filepath"
 	"reflect"
+	"runtime/debug"
 	"sort"
 	"testing"
 	"time"
 
-	"android/soong/fs"
-	"runtime/debug"
+	"android/soong/finder/fs"
 )
 
 // some utils for tests to use
diff --git a/fs/Android.bp b/finder/fs/Android.bp
similarity index 85%
rename from fs/Android.bp
rename to finder/fs/Android.bp
index f4706ca..27e3c7d 100644
--- a/fs/Android.bp
+++ b/finder/fs/Android.bp
@@ -17,10 +17,14 @@
 //
 
 bootstrap_go_package {
-    name: "soong-fs",
-    pkgPath: "android/soong/fs",
+    name: "soong-finder-fs",
+    pkgPath: "android/soong/finder/fs",
     srcs: [
         "fs.go",
+        "readdir.go",
+    ],
+    testSrcs: [
+        "readdir_test.go",
     ],
     darwin: {
         srcs: [
diff --git a/fs/fs.go b/finder/fs/fs.go
similarity index 94%
rename from fs/fs.go
rename to finder/fs/fs.go
index eff8ad0..9c138cd 100644
--- a/fs/fs.go
+++ b/finder/fs/fs.go
@@ -51,7 +51,7 @@
 	// getting information about files
 	Open(name string) (file io.ReadCloser, err error)
 	Lstat(path string) (stats os.FileInfo, err error)
-	ReadDir(path string) (contents []os.FileInfo, err error)
+	ReadDir(path string) (contents []DirEntryInfo, err error)
 
 	InodeNumber(info os.FileInfo) (number uint64, err error)
 	DeviceNumber(info os.FileInfo) (number uint64, err error)
@@ -67,18 +67,50 @@
 	ViewId() (id string) // Some unique id of the user accessing the filesystem
 }
 
+// DentryInfo is a subset of the functionality available through os.FileInfo that might be able
+// to be gleaned through only a syscall.Getdents without requiring a syscall.Lstat of every file.
+type DirEntryInfo interface {
+	Name() string
+	Mode() os.FileMode // the file type encoded as an os.FileMode
+	IsDir() bool
+}
+
+type dirEntryInfo struct {
+	name       string
+	mode       os.FileMode
+	modeExists bool
+}
+
+var _ DirEntryInfo = os.FileInfo(nil)
+
+func (d *dirEntryInfo) Name() string      { return d.name }
+func (d *dirEntryInfo) Mode() os.FileMode { return d.mode }
+func (d *dirEntryInfo) IsDir() bool       { return d.mode.IsDir() }
+func (d *dirEntryInfo) String() string    { return d.name + ": " + d.mode.String() }
+
 // osFs implements FileSystem using the local disk.
 type osFs struct{}
 
+var _ FileSystem = (*osFs)(nil)
+
 func (osFs) Open(name string) (io.ReadCloser, error) { return os.Open(name) }
 
 func (osFs) Lstat(path string) (stats os.FileInfo, err error) {
 	return os.Lstat(path)
 }
 
-func (osFs) ReadDir(path string) (contents []os.FileInfo, err error) {
-	return ioutil.ReadDir(path)
+func (osFs) ReadDir(path string) (contents []DirEntryInfo, err error) {
+	entries, err := readdir(path)
+	if err != nil {
+		return nil, err
+	}
+	for _, entry := range entries {
+		contents = append(contents, entry)
+	}
+
+	return contents, nil
 }
+
 func (osFs) Rename(oldPath string, newPath string) error {
 	return os.Rename(oldPath, newPath)
 }
@@ -154,6 +186,8 @@
 	aggregatesLock sync.Mutex
 }
 
+var _ FileSystem = (*MockFs)(nil)
+
 type mockInode struct {
 	modTime     time.Time
 	permTime    time.Time
@@ -475,7 +509,7 @@
 		fmt.Errorf("%v is not a mockFileInfo", info)
 }
 
-func (m *MockFs) ReadDir(path string) (contents []os.FileInfo, err error) {
+func (m *MockFs) ReadDir(path string) (contents []DirEntryInfo, err error) {
 	// update aggregates
 	m.aggregatesLock.Lock()
 	m.ReadDirCalls = append(m.ReadDirCalls, path)
@@ -486,7 +520,7 @@
 	if err != nil {
 		return nil, err
 	}
-	results := []os.FileInfo{}
+	results := []DirEntryInfo{}
 	dir, err := m.getDir(path, false)
 	if err != nil {
 		return nil, err
diff --git a/fs/fs_darwin.go b/finder/fs/fs_darwin.go
similarity index 100%
rename from fs/fs_darwin.go
rename to finder/fs/fs_darwin.go
diff --git a/fs/fs_linux.go b/finder/fs/fs_linux.go
similarity index 100%
rename from fs/fs_linux.go
rename to finder/fs/fs_linux.go
diff --git a/finder/fs/readdir.go b/finder/fs/readdir.go
new file mode 100644
index 0000000..f6d7813
--- /dev/null
+++ b/finder/fs/readdir.go
@@ -0,0 +1,219 @@
+// 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 fs
+
+// This is based on the readdir implementation from Go 1.9:
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+import (
+	"os"
+	"syscall"
+	"unsafe"
+)
+
+const (
+	blockSize = 4096
+)
+
+func readdir(path string) ([]DirEntryInfo, error) {
+	f, err := os.Open(path)
+	defer f.Close()
+
+	if err != nil {
+		return nil, err
+	}
+	// This implicitly switches the fd to non-blocking mode, which is less efficient than what
+	// file.ReadDir does since it will keep a thread blocked and not just a goroutine.
+	fd := int(f.Fd())
+
+	buf := make([]byte, blockSize)
+	entries := make([]*dirEntryInfo, 0, 100)
+
+	for {
+		n, errno := syscall.ReadDirent(fd, buf)
+		if errno != nil {
+			err = os.NewSyscallError("readdirent", errno)
+			break
+		}
+		if n <= 0 {
+			break // EOF
+		}
+
+		entries = parseDirent(buf[:n], entries)
+	}
+
+	ret := make([]DirEntryInfo, 0, len(entries))
+
+	for _, entry := range entries {
+		if !entry.modeExists {
+			mode, lerr := lstatFileMode(path + "/" + entry.name)
+			if os.IsNotExist(lerr) {
+				// File disappeared between readdir + stat.
+				// Just treat it as if it didn't exist.
+				continue
+			}
+			if lerr != nil {
+				return ret, lerr
+			}
+			entry.mode = mode
+			entry.modeExists = true
+		}
+		ret = append(ret, entry)
+	}
+
+	return ret, err
+}
+
+func parseDirent(buf []byte, entries []*dirEntryInfo) []*dirEntryInfo {
+	for len(buf) > 0 {
+		reclen, ok := direntReclen(buf)
+		if !ok || reclen > uint64(len(buf)) {
+			return entries
+		}
+		rec := buf[:reclen]
+		buf = buf[reclen:]
+		ino, ok := direntIno(rec)
+		if !ok {
+			break
+		}
+		if ino == 0 { // File absent in directory.
+			continue
+		}
+		typ, ok := direntType(rec)
+		if !ok {
+			break
+		}
+		const namoff = uint64(unsafe.Offsetof(syscall.Dirent{}.Name))
+		namlen, ok := direntNamlen(rec)
+		if !ok || namoff+namlen > uint64(len(rec)) {
+			break
+		}
+		name := rec[namoff : namoff+namlen]
+
+		for i, c := range name {
+			if c == 0 {
+				name = name[:i]
+				break
+			}
+		}
+		// Check for useless names before allocating a string.
+		if string(name) == "." || string(name) == ".." {
+			continue
+		}
+
+		mode, modeExists := direntTypeToFileMode(typ)
+
+		entries = append(entries, &dirEntryInfo{string(name), mode, modeExists})
+	}
+	return entries
+}
+
+func direntIno(buf []byte) (uint64, bool) {
+	return readInt(buf, unsafe.Offsetof(syscall.Dirent{}.Ino), unsafe.Sizeof(syscall.Dirent{}.Ino))
+}
+
+func direntType(buf []byte) (uint64, bool) {
+	return readInt(buf, unsafe.Offsetof(syscall.Dirent{}.Type), unsafe.Sizeof(syscall.Dirent{}.Type))
+}
+
+func direntReclen(buf []byte) (uint64, bool) {
+	return readInt(buf, unsafe.Offsetof(syscall.Dirent{}.Reclen), unsafe.Sizeof(syscall.Dirent{}.Reclen))
+}
+
+func direntNamlen(buf []byte) (uint64, bool) {
+	reclen, ok := direntReclen(buf)
+	if !ok {
+		return 0, false
+	}
+	return reclen - uint64(unsafe.Offsetof(syscall.Dirent{}.Name)), true
+}
+
+// readInt returns the size-bytes unsigned integer in native byte order at offset off.
+func readInt(b []byte, off, size uintptr) (u uint64, ok bool) {
+	if len(b) < int(off+size) {
+		return 0, false
+	}
+	return readIntLE(b[off:], size), true
+}
+
+func readIntLE(b []byte, size uintptr) uint64 {
+	switch size {
+	case 1:
+		return uint64(b[0])
+	case 2:
+		_ = b[1] // bounds check hint to compiler; see golang.org/issue/14808
+		return uint64(b[0]) | uint64(b[1])<<8
+	case 4:
+		_ = b[3] // bounds check hint to compiler; see golang.org/issue/14808
+		return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24
+	case 8:
+		_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
+		return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 |
+			uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56
+	default:
+		panic("syscall: readInt with unsupported size")
+	}
+}
+
+// If the directory entry doesn't specify the type, fall back to using lstat to get the type.
+func lstatFileMode(name string) (os.FileMode, error) {
+	stat, err := os.Lstat(name)
+	if err != nil {
+		return 0, err
+	}
+
+	return stat.Mode() & (os.ModeType | os.ModeCharDevice), nil
+}
+
+// from Linux and Darwin dirent.h
+const (
+	DT_UNKNOWN = 0
+	DT_FIFO    = 1
+	DT_CHR     = 2
+	DT_DIR     = 4
+	DT_BLK     = 6
+	DT_REG     = 8
+	DT_LNK     = 10
+	DT_SOCK    = 12
+)
+
+func direntTypeToFileMode(typ uint64) (os.FileMode, bool) {
+	exists := true
+	var mode os.FileMode
+	switch typ {
+	case DT_UNKNOWN:
+		exists = false
+	case DT_FIFO:
+		mode = os.ModeNamedPipe
+	case DT_CHR:
+		mode = os.ModeDevice | os.ModeCharDevice
+	case DT_DIR:
+		mode = os.ModeDir
+	case DT_BLK:
+		mode = os.ModeDevice
+	case DT_REG:
+		mode = 0
+	case DT_LNK:
+		mode = os.ModeSymlink
+	case DT_SOCK:
+		mode = os.ModeSocket
+	default:
+		exists = false
+	}
+
+	return mode, exists
+}
diff --git a/finder/fs/readdir_test.go b/finder/fs/readdir_test.go
new file mode 100644
index 0000000..24a6d18
--- /dev/null
+++ b/finder/fs/readdir_test.go
@@ -0,0 +1,312 @@
+// 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 fs
+
+import (
+	"os"
+	"reflect"
+	"runtime"
+	"testing"
+)
+
+func TestParseDirent(t *testing.T) {
+	testCases := []struct {
+		name string
+		in   []byte
+		out  []*dirEntryInfo
+	}{
+		{
+			// Test that type DT_DIR is translated to os.ModeDir
+			name: "dir",
+			in: []byte{
+				// __ino64_t d_ino;
+				0xfb, 0x10, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00,
+				// __off64_t d_off;
+				0xeb, 0x85, 0x20, 0x91, 0xb9, 0x14, 0x34, 0x03,
+				// unsigned short int d_reclen;
+				0x28, 0x00,
+				// unsigned char d_type;
+				0x04,
+				// char d_name[];
+				0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			},
+			out: []*dirEntryInfo{
+				{".module_paths", os.ModeDir, true},
+			},
+		},
+		{
+			// Test that type DT_REG is translated to a regular file
+			name: "file",
+			in: []byte{
+				// __ino64_t d_ino;
+				0xfb, 0x10, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00,
+				// __off64_t d_off;
+				0xeb, 0x85, 0x20, 0x91, 0xb9, 0x14, 0x34, 0x03,
+				// unsigned short int d_reclen;
+				0x28, 0x00,
+				// unsigned char d_type;
+				0x08,
+				// char d_name[];
+				0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			},
+			out: []*dirEntryInfo{
+				{".module_paths", 0, true},
+			},
+		},
+		{
+			// Test that type DT_LNK is translated to a regular os.ModeSymlink
+			name: "symlink",
+			in: []byte{
+				// __ino64_t d_ino;
+				0xfb, 0x10, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00,
+				// __off64_t d_off;
+				0xeb, 0x85, 0x20, 0x91, 0xb9, 0x14, 0x34, 0x03,
+				// unsigned short int d_reclen;
+				0x28, 0x00,
+				// unsigned char d_type;
+				0x0a,
+				// char d_name[];
+				0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			},
+			out: []*dirEntryInfo{
+				{".module_paths", os.ModeSymlink, true},
+			},
+		},
+		{
+			// Test that type DT_UNKNOWN sets modeExists: false
+			name: "unknown",
+			in: []byte{
+				// __ino64_t d_ino;
+				0xfb, 0x10, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00,
+				// __off64_t d_off;
+				0xeb, 0x85, 0x20, 0x91, 0xb9, 0x14, 0x34, 0x03,
+				// unsigned short int d_reclen;
+				0x28, 0x00,
+				// unsigned char d_type;
+				0x00,
+				// char d_name[];
+				0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			},
+			out: []*dirEntryInfo{
+				{".module_paths", 0, false},
+			},
+		},
+		{
+			// Test a name with no padding after the null terminator
+			name: "no padding",
+			in: []byte{
+				// __ino64_t d_ino;
+				0xfb, 0x10, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00,
+				// __off64_t d_off;
+				0xeb, 0x85, 0x20, 0x91, 0xb9, 0x14, 0x34, 0x03,
+				// unsigned short int d_reclen;
+				0x20, 0x00,
+				// unsigned char d_type;
+				0x04,
+				// char d_name[];
+				0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x00,
+			},
+			out: []*dirEntryInfo{
+				{".module_path", os.ModeDir, true},
+			},
+		},
+		{
+			// Test two sequential entries
+			name: "two entries",
+			in: []byte{
+				// __ino64_t d_ino;
+				0xfb, 0x10, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00,
+				// __off64_t d_off;
+				0xeb, 0x85, 0x20, 0x91, 0xb9, 0x14, 0x34, 0x03,
+				// unsigned short int d_reclen;
+				0x28, 0x00,
+				// unsigned char d_type;
+				0x04,
+				// char d_name[];
+				0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+				// __ino64_t d_ino;
+				0xfb, 0x10, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00,
+				// __off64_t d_off;
+				0xeb, 0x85, 0x20, 0x91, 0xb9, 0x14, 0x34, 0x03,
+				// unsigned short int d_reclen;
+				0x28, 0x00,
+				// unsigned char d_type;
+				0x04,
+				// char d_name[];
+				0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x74,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			},
+			out: []*dirEntryInfo{
+				{".module_paths", os.ModeDir, true},
+				{".module_patht", os.ModeDir, true},
+			},
+		},
+		{
+			// Test two sequential entries with no padding between them
+			name: "two entries no padding",
+			in: []byte{
+				// __ino64_t d_ino;
+				0xfb, 0x10, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00,
+				// __off64_t d_off;
+				0xeb, 0x85, 0x20, 0x91, 0xb9, 0x14, 0x34, 0x03,
+				// unsigned short int d_reclen;
+				0x20, 0x00,
+				// unsigned char d_type;
+				0x04,
+				// char d_name[];
+				0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x00,
+
+				// __ino64_t d_ino;
+				0xfb, 0x10, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00,
+				// __off64_t d_off;
+				0xeb, 0x85, 0x20, 0x91, 0xb9, 0x14, 0x34, 0x03,
+				// unsigned short int d_reclen;
+				0x28, 0x00,
+				// unsigned char d_type;
+				0x04,
+				// char d_name[];
+				0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			},
+			out: []*dirEntryInfo{
+				{".module_path", os.ModeDir, true},
+				{".module_paths", os.ModeDir, true},
+			},
+		},
+		{
+			// Test an empty buffer.  This shouldn't happen in practice because
+			// readdir doesn't call parseDirent if no bytes were returned.
+			name: "empty",
+			in:   []byte{},
+			out:  nil,
+		},
+		{
+			name: "missing null terminator",
+			in: []byte{
+				// __ino64_t d_ino;
+				0xfb, 0x10, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00,
+				// __off64_t d_off;
+				0xeb, 0x85, 0x20, 0x91, 0xb9, 0x14, 0x34, 0x03,
+				// unsigned short int d_reclen;
+				0x20, 0x00,
+				// unsigned char d_type;
+				0x04,
+				// char d_name[];
+				0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73,
+			},
+			out: []*dirEntryInfo{
+				{".module_paths", os.ModeDir, true},
+			},
+		},
+		{
+			// Test two sequential entries where the first has an incorrect d_reclen.
+			// Should return with no entries.
+			name: "two entries first malformed",
+			in: []byte{
+				// __ino64_t d_ino;
+				0xfb, 0x10, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00,
+				// __off64_t d_off;
+				0xeb, 0x85, 0x20, 0x91, 0xb9, 0x14, 0x34, 0x03,
+				// unsigned short int d_reclen;
+				0x10, 0x00,
+				// unsigned char d_type;
+				0x04,
+				// char d_name[];
+				0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x00,
+
+				// __ino64_t d_ino;
+				0xfb, 0x10, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00,
+				// __off64_t d_off;
+				0xeb, 0x85, 0x20, 0x91, 0xb9, 0x14, 0x34, 0x03,
+				// unsigned short int d_reclen;
+				0x28, 0x00,
+				// unsigned char d_type;
+				0x04,
+				// char d_name[];
+				0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			},
+			out: nil,
+		},
+		{
+			// Test two sequential entries where the second has an incorrect d_reclen.
+			// Should return the first entry.
+			name: "two entries second malformed",
+			in: []byte{
+				// __ino64_t d_ino;
+				0xfb, 0x10, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00,
+				// __off64_t d_off;
+				0xeb, 0x85, 0x20, 0x91, 0xb9, 0x14, 0x34, 0x03,
+				// unsigned short int d_reclen;
+				0x28, 0x00,
+				// unsigned char d_type;
+				0x04,
+				// char d_name[];
+				0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x00,
+
+				// __ino64_t d_ino;
+				0xfb, 0x10, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00,
+				// __off64_t d_off;
+				0xeb, 0x85, 0x20, 0x91, 0xb9, 0x14, 0x34, 0x03,
+				// unsigned short int d_reclen;
+				0x10, 0x00,
+				// unsigned char d_type;
+				0x04,
+				// char d_name[];
+				0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			},
+			out: []*dirEntryInfo{
+				{".module_path", os.ModeDir, true},
+			},
+		},
+		{
+			// Test a reclen that goes past the end of the buffer.
+			name: "overrun",
+			in: []byte{
+				// __ino64_t d_ino;
+				0xfb, 0x10, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00,
+				// __off64_t d_off;
+				0xeb, 0x85, 0x20, 0x91, 0xb9, 0x14, 0x34, 0x03,
+				// unsigned short int d_reclen;
+				0x30, 0x00,
+				// unsigned char d_type;
+				0x04,
+				// char d_name[];
+				0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x00,
+			},
+			out: nil,
+		},
+	}
+
+	if runtime.GOOS != "linux" {
+		t.Skip("depends on Linux definitions of syscall.Dirent")
+	}
+
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			entries := parseDirent(testCase.in, nil)
+			if !reflect.DeepEqual(testCase.out, entries) {
+				t.Fatalf("expected:\n %v\ngot:\n %v\n", testCase.out, entries)
+			}
+		})
+	}
+}
diff --git a/java/java.go b/java/java.go
index dbf202a..8159af8 100644
--- a/java/java.go
+++ b/java/java.go
@@ -597,6 +597,29 @@
 	// classpath
 	flags.bootClasspath.AddPaths(deps.bootClasspath)
 	flags.classpath.AddPaths(deps.classpath)
+
+	if len(flags.bootClasspath) == 0 && ctx.Host() && !ctx.Config().TargetOpenJDK9() &&
+		!Bool(j.properties.No_standard_libs) &&
+		inList(flags.javaVersion, []string{"1.6", "1.7", "1.8"}) {
+		// Give host-side tools a version of OpenJDK's standard libraries
+		// close to what they're targeting. As of Dec 2017, AOSP is only
+		// bundling OpenJDK 8 and 9, so nothing < 8 is available.
+		//
+		// When building with OpenJDK 8, the following should have no
+		// effect since those jars would be available by default.
+		//
+		// When building with OpenJDK 9 but targeting a version < 1.8,
+		// putting them on the bootclasspath means that:
+		// a) code can't (accidentally) refer to OpenJDK 9 specific APIs
+		// b) references to existing APIs are not reinterpreted in an
+		//    OpenJDK 9-specific way, eg. calls to subclasses of
+		//    java.nio.Buffer as in http://b/70862583
+		java8Home := ctx.Config().Getenv("ANDROID_JAVA8_HOME")
+		flags.bootClasspath = append(flags.bootClasspath,
+			android.PathForSource(ctx, java8Home, "jre/lib/jce.jar"),
+			android.PathForSource(ctx, java8Home, "jre/lib/rt.jar"))
+	}
+
 	// systemModules
 	if deps.systemModules != nil {
 		flags.systemModules = append(flags.systemModules, deps.systemModules)
diff --git a/java/java_test.go b/java/java_test.go
index 84fe903..6e14a70 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -53,6 +53,12 @@
 }
 
 func testConfig(env map[string]string) android.Config {
+	if env == nil {
+		env = make(map[string]string)
+	}
+	if env["ANDROID_JAVA8_HOME"] == "" {
+		env["ANDROID_JAVA8_HOME"] = "jdk8"
+	}
 	return android.TestArchConfig(buildDir, env)
 
 }
@@ -150,6 +156,9 @@
 		"build/target/product/security/testkey": nil,
 
 		"build/soong/scripts/jar-wrapper.sh": nil,
+
+		"jdk8/jre/lib/jce.jar": nil,
+		"jdk8/jre/lib/rt.jar":  nil,
 	}
 
 	for k, v := range fs {
@@ -364,11 +373,12 @@
 	},
 	{
 
-		name:       "host default",
-		moduleType: "java_library_host",
-		properties: ``,
-		host:       android.Host,
-		classpath:  []string{},
+		name:          "host default",
+		moduleType:    "java_library_host",
+		properties:    ``,
+		host:          android.Host,
+		bootclasspath: []string{"jdk8/jre/lib/jce.jar", "jdk8/jre/lib/rt.jar"},
+		classpath:     []string{},
 	},
 	{
 		name:       "host nostdlib",
@@ -379,10 +389,11 @@
 	},
 	{
 
-		name:       "host supported default",
-		host:       android.Host,
-		properties: `host_supported: true,`,
-		classpath:  []string{},
+		name:          "host supported default",
+		host:          android.Host,
+		properties:    `host_supported: true,`,
+		classpath:     []string{},
+		bootclasspath: []string{"jdk8/jre/lib/jce.jar", "jdk8/jre/lib/rt.jar"},
 	},
 	{
 		name:       "host supported nostdlib",
diff --git a/java/system_modules.go b/java/system_modules.go
index 5234d17..196d041 100644
--- a/java/system_modules.go
+++ b/java/system_modules.go
@@ -112,11 +112,9 @@
 func (system *SystemModules) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	var jars android.Paths
 
-	ctx.VisitDirectDeps(func(module android.Module) {
-		if ctx.OtherModuleDependencyTag(module) == libTag {
-			dep, _ := module.(Dependency)
-			jars = append(jars, dep.HeaderJars()...)
-		}
+	ctx.VisitDirectDepsWithTag(libTag, func(module android.Module) {
+		dep, _ := module.(Dependency)
+		jars = append(jars, dep.HeaderJars()...)
 	})
 
 	jars = append(jars, android.PathsForModuleSrc(ctx, system.properties.Jars)...)
diff --git a/python/binary.go b/python/binary.go
index 457c7fa..0314edb 100644
--- a/python/binary.go
+++ b/python/binary.go
@@ -133,10 +133,7 @@
 
 	var launcher_path android.Path
 	if embedded_launcher {
-		ctx.VisitDirectDeps(func(m android.Module) {
-			if ctx.OtherModuleDependencyTag(m) != launcherTag {
-				return
-			}
+		ctx.VisitDirectDepsWithTag(launcherTag, func(m android.Module) {
 			if provider, ok := m.(IntermPathProvider); ok {
 				if launcher_path != nil {
 					panic(fmt.Errorf("launcher path was found before: %q",
diff --git a/ui/build/finder.go b/ui/build/finder.go
index a0f5d08..3bd6d87 100644
--- a/ui/build/finder.go
+++ b/ui/build/finder.go
@@ -16,7 +16,7 @@
 
 import (
 	"android/soong/finder"
-	"android/soong/fs"
+	"android/soong/finder/fs"
 	"android/soong/ui/logger"
 	"bytes"
 	"io/ioutil"