Merge "Revert "Revert "Test for dangling rules in make checkbuild"""
diff --git a/Android.bp b/Android.bp
index e89f908..4dddb28 100644
--- a/Android.bp
+++ b/Android.bp
@@ -56,6 +56,7 @@
         "android/prebuilt.go",
         "android/proto.go",
         "android/register.go",
+        "android/singleton.go",
         "android/testing.go",
         "android/util.go",
         "android/variable.go",
@@ -208,11 +209,13 @@
         "soong-java-config",
     ],
     srcs: [
+        "java/aapt2.go",
         "java/androidmk.go",
         "java/app_builder.go",
         "java/app.go",
         "java/builder.go",
         "java/gen.go",
+        "java/jacoco.go",
         "java/java.go",
         "java/proto.go",
         "java/resources.go",
diff --git a/android/androidmk.go b/android/androidmk.go
index 19d5ea6..aff43fa 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -53,13 +53,13 @@
 
 type AndroidMkExtraFunc func(w io.Writer, outputFile Path)
 
-func AndroidMkSingleton() blueprint.Singleton {
+func AndroidMkSingleton() Singleton {
 	return &androidMkSingleton{}
 }
 
 type androidMkSingleton struct{}
 
-func (c *androidMkSingleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
+func (c *androidMkSingleton) GenerateBuildActions(ctx SingletonContext) {
 	config := ctx.Config().(Config)
 
 	if !config.EmbeddedInMake() {
@@ -68,10 +68,8 @@
 
 	var androidMkModulesList []Module
 
-	ctx.VisitAllModules(func(module blueprint.Module) {
-		if amod, ok := module.(Module); ok {
-			androidMkModulesList = append(androidMkModulesList, amod)
-		}
+	ctx.VisitAllModules(func(module Module) {
+		androidMkModulesList = append(androidMkModulesList, module)
 	})
 
 	sort.Sort(AndroidModulesByName{androidMkModulesList, ctx})
@@ -86,14 +84,13 @@
 		ctx.Errorf(err.Error())
 	}
 
-	ctx.Build(pctx, blueprint.BuildParams{
-		Rule:     blueprint.Phony,
-		Outputs:  []string{transMk.String()},
-		Optional: true,
+	ctx.Build(pctx, BuildParams{
+		Rule:   blueprint.Phony,
+		Output: transMk,
 	})
 }
 
-func translateAndroidMk(ctx blueprint.SingletonContext, mkFile string, mods []Module) error {
+func translateAndroidMk(ctx SingletonContext, mkFile string, mods []Module) error {
 	buf := &bytes.Buffer{}
 
 	fmt.Fprintln(buf, "LOCAL_MODULE_MAKEFILE := $(lastword $(MAKEFILE_LIST))")
@@ -145,7 +142,7 @@
 	return ioutil.WriteFile(mkFile, buf.Bytes(), 0666)
 }
 
-func translateAndroidMkModule(ctx blueprint.SingletonContext, w io.Writer, mod blueprint.Module) error {
+func translateAndroidMkModule(ctx SingletonContext, w io.Writer, mod blueprint.Module) error {
 	provider, ok := mod.(AndroidMkDataProvider)
 	if !ok {
 		return nil
diff --git a/android/api_levels.go b/android/api_levels.go
index 2c4ae1a..bdfbc43 100644
--- a/android/api_levels.go
+++ b/android/api_levels.go
@@ -16,22 +16,19 @@
 
 import (
 	"encoding/json"
-	"path/filepath"
-
-	"github.com/google/blueprint"
 )
 
 func init() {
 	RegisterSingletonType("api_levels", ApiLevelsSingleton)
 }
 
-func ApiLevelsSingleton() blueprint.Singleton {
+func ApiLevelsSingleton() Singleton {
 	return &apiLevelsSingleton{}
 }
 
 type apiLevelsSingleton struct{}
 
-func createApiLevelsJson(ctx blueprint.SingletonContext, file string,
+func createApiLevelsJson(ctx SingletonContext, file WritablePath,
 	apiLevelsMap map[string]int) {
 
 	jsonStr, err := json.Marshal(apiLevelsMap)
@@ -39,21 +36,21 @@
 		ctx.Errorf(err.Error())
 	}
 
-	ctx.Build(pctx, blueprint.BuildParams{
+	ctx.Build(pctx, BuildParams{
 		Rule:        WriteFile,
-		Description: "generate " + filepath.Base(file),
-		Outputs:     []string{file},
+		Description: "generate " + file.Base(),
+		Output:      file,
 		Args: map[string]string{
 			"content": string(jsonStr[:]),
 		},
 	})
 }
 
-func GetApiLevelsJson(ctx PathContext) Path {
+func GetApiLevelsJson(ctx PathContext) WritablePath {
 	return PathForOutput(ctx, "api_levels.json")
 }
 
-func (a *apiLevelsSingleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
+func (a *apiLevelsSingleton) GenerateBuildActions(ctx SingletonContext) {
 	baseApiLevel := 9000
 	apiLevelsMap := map[string]int{
 		"G":     9,
@@ -74,5 +71,5 @@
 	}
 
 	apiLevelsJson := GetApiLevelsJson(ctx)
-	createApiLevelsJson(ctx, apiLevelsJson.String(), apiLevelsMap)
+	createApiLevelsJson(ctx, apiLevelsJson, apiLevelsMap)
 }
diff --git a/android/config.go b/android/config.go
index 79ff32a..2589f71 100644
--- a/android/config.go
+++ b/android/config.go
@@ -175,7 +175,12 @@
 func TestConfig(buildDir string, env map[string]string) Config {
 	config := &config{
 		ProductVariables: productVariables{
-			DeviceName: stringPtr("test_device"),
+			DeviceName:           stringPtr("test_device"),
+			Platform_sdk_version: intPtr(26),
+			AAPTConfig:           &[]string{"normal", "large", "xlarge", "hdpi", "xhdpi", "xxhdpi"},
+			AAPTPreferredConfig:  stringPtr("xhdpi"),
+			AAPTCharacteristics:  stringPtr("nosdcard"),
+			AAPTPrebuiltDPI:      &[]string{"xhdpi", "xxhdpi"},
 		},
 
 		buildDir:     buildDir,
@@ -417,12 +422,11 @@
 	return true
 }
 
-func (c *config) ResourceOverlays() []SourcePath {
-	return nil
-}
-
-func (c *config) PlatformVersion() string {
-	return "M"
+func (c *config) ResourceOverlays() []string {
+	if c.ProductVariables.ResourceOverlays == nil {
+		return nil
+	}
+	return *c.ProductVariables.ResourceOverlays
 }
 
 func (c *config) PlatformSdkVersionInt() int {
@@ -445,6 +449,10 @@
 	}
 }
 
+func (c *config) AppsDefaultVersionName() string {
+	return String(c.ProductVariables.AppsDefaultVersionName)
+}
+
 // Codenames that are active in the current lunch target.
 func (c *config) PlatformVersionActiveCodenames() []string {
 	return c.ProductVariables.Platform_version_active_codenames
@@ -466,10 +474,6 @@
 	return combined
 }
 
-func (c *config) BuildNumber() string {
-	return "000000"
-}
-
 func (c *config) ProductAAPTConfig() []string {
 	return *c.ProductVariables.AAPTConfig
 }
diff --git a/android/makevars.go b/android/makevars.go
index 024e015..d323613 100644
--- a/android/makevars.go
+++ b/android/makevars.go
@@ -21,7 +21,6 @@
 	"os"
 	"strconv"
 
-	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
 
@@ -66,7 +65,7 @@
 
 type MakeVarsProvider func(ctx MakeVarsContext)
 
-func RegisterMakeVarsProvider(pctx blueprint.PackageContext, provider MakeVarsProvider) {
+func RegisterMakeVarsProvider(pctx PackageContext, provider MakeVarsProvider) {
 	makeVarsProviders = append(makeVarsProviders, makeVarsProvider{pctx, provider})
 }
 
@@ -76,14 +75,14 @@
 	RegisterSingletonType("makevars", makeVarsSingletonFunc)
 }
 
-func makeVarsSingletonFunc() blueprint.Singleton {
+func makeVarsSingletonFunc() Singleton {
 	return &makeVarsSingleton{}
 }
 
 type makeVarsSingleton struct{}
 
 type makeVarsProvider struct {
-	pctx blueprint.PackageContext
+	pctx PackageContext
 	call MakeVarsProvider
 }
 
@@ -91,8 +90,8 @@
 
 type makeVarsContext struct {
 	config Config
-	ctx    blueprint.SingletonContext
-	pctx   blueprint.PackageContext
+	ctx    SingletonContext
+	pctx   PackageContext
 	vars   []makeVarsVariable
 }
 
@@ -105,7 +104,7 @@
 	strict bool
 }
 
-func (s *makeVarsSingleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
+func (s *makeVarsSingleton) GenerateBuildActions(ctx SingletonContext) {
 	config := ctx.Config().(Config)
 
 	if !config.EmbeddedInMake() {
diff --git a/android/module.go b/android/module.go
index 66859fa..0eb4820 100644
--- a/android/module.go
+++ b/android/module.go
@@ -17,6 +17,7 @@
 import (
 	"fmt"
 	"path/filepath"
+	"sort"
 	"strings"
 
 	"github.com/google/blueprint"
@@ -78,7 +79,7 @@
 	blueprint.BaseModuleContext
 
 	// Deprecated: use ModuleContext.Build instead.
-	ModuleBuild(pctx blueprint.PackageContext, params ModuleBuildParams)
+	ModuleBuild(pctx PackageContext, params ModuleBuildParams)
 
 	ExpandSources(srcFiles, excludes []string) Paths
 	ExpandSourcesSubDir(srcFiles, excludes []string, subDir string) Paths
@@ -115,15 +116,15 @@
 	VisitDepsDepthFirstIf(pred func(Module) bool, visit func(Module))
 	WalkDeps(visit func(Module, Module) bool)
 
-	Variable(pctx blueprint.PackageContext, name, value string)
-	Rule(pctx blueprint.PackageContext, name string, params blueprint.RuleParams, argNames ...string) blueprint.Rule
+	Variable(pctx PackageContext, name, value string)
+	Rule(pctx PackageContext, name string, params blueprint.RuleParams, argNames ...string) blueprint.Rule
 	// Similar to blueprint.ModuleContext.Build, but takes Paths instead of []string,
 	// and performs more verification.
-	Build(pctx blueprint.PackageContext, params BuildParams)
+	Build(pctx PackageContext, params BuildParams)
 
-	PrimaryModule() blueprint.Module
-	FinalModule() blueprint.Module
-	VisitAllModuleVariants(visit func(blueprint.Module))
+	PrimaryModule() Module
+	FinalModule() Module
+	VisitAllModuleVariants(visit func(Module))
 
 	GetMissingDependencies() []string
 }
@@ -325,8 +326,8 @@
 
 	// Used by buildTargetSingleton to create checkbuild and per-directory build targets
 	// Only set on the final variant of each module
-	installTarget    string
-	checkbuildTarget string
+	installTarget    WritablePath
+	checkbuildTarget WritablePath
 	blueprintDir     string
 
 	hooks hooks
@@ -464,36 +465,35 @@
 	return false
 }
 
-func (a *ModuleBase) generateModuleTarget(ctx blueprint.ModuleContext) {
+func (a *ModuleBase) generateModuleTarget(ctx ModuleContext) {
 	allInstalledFiles := Paths{}
 	allCheckbuildFiles := Paths{}
-	ctx.VisitAllModuleVariants(func(module blueprint.Module) {
-		a := module.(Module).base()
+	ctx.VisitAllModuleVariants(func(module Module) {
+		a := module.base()
 		allInstalledFiles = append(allInstalledFiles, a.installFiles...)
 		allCheckbuildFiles = append(allCheckbuildFiles, a.checkbuildFiles...)
 	})
 
-	deps := []string{}
+	var deps Paths
 
 	if len(allInstalledFiles) > 0 {
-		name := ctx.ModuleName() + "-install"
-		ctx.Build(pctx, blueprint.BuildParams{
+		name := PathForPhony(ctx, ctx.ModuleName()+"-install")
+		ctx.Build(pctx, BuildParams{
 			Rule:      blueprint.Phony,
-			Outputs:   []string{name},
-			Implicits: allInstalledFiles.Strings(),
-			Optional:  ctx.Config().(Config).EmbeddedInMake(),
+			Output:    name,
+			Implicits: allInstalledFiles,
+			Default:   !ctx.Config().(Config).EmbeddedInMake(),
 		})
 		deps = append(deps, name)
 		a.installTarget = name
 	}
 
 	if len(allCheckbuildFiles) > 0 {
-		name := ctx.ModuleName() + "-checkbuild"
-		ctx.Build(pctx, blueprint.BuildParams{
+		name := PathForPhony(ctx, ctx.ModuleName()+"-checkbuild")
+		ctx.Build(pctx, BuildParams{
 			Rule:      blueprint.Phony,
-			Outputs:   []string{name},
-			Implicits: allCheckbuildFiles.Strings(),
-			Optional:  true,
+			Output:    name,
+			Implicits: allCheckbuildFiles,
 		})
 		deps = append(deps, name)
 		a.checkbuildTarget = name
@@ -505,11 +505,10 @@
 			suffix = "-soong"
 		}
 
-		ctx.Build(pctx, blueprint.BuildParams{
+		ctx.Build(pctx, BuildParams{
 			Rule:      blueprint.Phony,
-			Outputs:   []string{ctx.ModuleName() + suffix},
+			Output:    PathForPhony(ctx, ctx.ModuleName()+suffix),
 			Implicits: deps,
-			Optional:  true,
 		})
 
 		a.blueprintDir = ctx.ModuleDir()
@@ -525,23 +524,23 @@
 	}
 }
 
-func (a *ModuleBase) GenerateBuildActions(ctx blueprint.ModuleContext) {
-	androidCtx := &androidModuleContext{
+func (a *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext) {
+	ctx := &androidModuleContext{
 		module:                 a.module,
-		ModuleContext:          ctx,
-		androidBaseContextImpl: a.androidBaseContextFactory(ctx),
-		installDeps:            a.computeInstallDeps(ctx),
+		ModuleContext:          blueprintCtx,
+		androidBaseContextImpl: a.androidBaseContextFactory(blueprintCtx),
+		installDeps:            a.computeInstallDeps(blueprintCtx),
 		installFiles:           a.installFiles,
-		missingDeps:            ctx.GetMissingDependencies(),
+		missingDeps:            blueprintCtx.GetMissingDependencies(),
 	}
 
 	desc := "//" + ctx.ModuleDir() + ":" + ctx.ModuleName() + " "
 	var suffix []string
-	if androidCtx.Os().Class != Device && androidCtx.Os().Class != Generic {
-		suffix = append(suffix, androidCtx.Os().String())
+	if ctx.Os().Class != Device && ctx.Os().Class != Generic {
+		suffix = append(suffix, ctx.Os().String())
 	}
-	if !androidCtx.PrimaryArch() {
-		suffix = append(suffix, androidCtx.Arch().ArchType.String())
+	if !ctx.PrimaryArch() {
+		suffix = append(suffix, ctx.Arch().ArchType.String())
 	}
 
 	ctx.Variable(pctx, "moduleDesc", desc)
@@ -553,13 +552,13 @@
 	ctx.Variable(pctx, "moduleDescSuffix", s)
 
 	if a.Enabled() {
-		a.module.GenerateAndroidBuildActions(androidCtx)
+		a.module.GenerateAndroidBuildActions(ctx)
 		if ctx.Failed() {
 			return
 		}
 
-		a.installFiles = append(a.installFiles, androidCtx.installFiles...)
-		a.checkbuildFiles = append(a.checkbuildFiles, androidCtx.checkbuildFiles...)
+		a.installFiles = append(a.installFiles, ctx.installFiles...)
+		a.checkbuildFiles = append(a.checkbuildFiles, ctx.checkbuildFiles...)
 	}
 
 	if a == ctx.FinalModule().(Module).base() {
@@ -569,7 +568,7 @@
 		}
 	}
 
-	a.buildParams = androidCtx.buildParams
+	a.buildParams = ctx.buildParams
 }
 
 type androidBaseContextImpl struct {
@@ -594,7 +593,7 @@
 }
 
 func (a *androidModuleContext) ninjaError(desc string, outputs []string, err error) {
-	a.ModuleContext.Build(pctx, blueprint.BuildParams{
+	a.ModuleContext.Build(pctx.PackageContext, blueprint.BuildParams{
 		Rule:        ErrorRule,
 		Description: desc,
 		Outputs:     outputs,
@@ -606,17 +605,14 @@
 	return
 }
 
-func (a *androidModuleContext) ModuleBuild(pctx blueprint.PackageContext, params ModuleBuildParams) {
+func (a *androidModuleContext) ModuleBuild(pctx PackageContext, params ModuleBuildParams) {
 	a.Build(pctx, BuildParams(params))
 }
 
-func (a *androidModuleContext) Build(pctx blueprint.PackageContext, params BuildParams) {
-	if a.config.captureBuild {
-		a.buildParams = append(a.buildParams, params)
-	}
-
+func convertBuildParams(params BuildParams) blueprint.BuildParams {
 	bparams := blueprint.BuildParams{
 		Rule:            params.Rule,
+		Description:     params.Description,
 		Deps:            params.Deps,
 		Outputs:         params.Outputs.Strings(),
 		ImplicitOutputs: params.ImplicitOutputs.Strings(),
@@ -627,10 +623,6 @@
 		Optional:        !params.Default,
 	}
 
-	if params.Description != "" {
-		bparams.Description = "${moduleDesc}" + params.Description + "${moduleDescSuffix}"
-	}
-
 	if params.Depfile != nil {
 		bparams.Depfile = params.Depfile.String()
 	}
@@ -647,6 +639,30 @@
 		bparams.Implicits = append(bparams.Implicits, params.Implicit.String())
 	}
 
+	return bparams
+}
+
+func (a *androidModuleContext) Variable(pctx PackageContext, name, value string) {
+	a.ModuleContext.Variable(pctx.PackageContext, name, value)
+}
+
+func (a *androidModuleContext) Rule(pctx PackageContext, name string, params blueprint.RuleParams,
+	argNames ...string) blueprint.Rule {
+
+	return a.ModuleContext.Rule(pctx.PackageContext, name, params, argNames...)
+}
+
+func (a *androidModuleContext) Build(pctx PackageContext, params BuildParams) {
+	if a.config.captureBuild {
+		a.buildParams = append(a.buildParams, params)
+	}
+
+	bparams := convertBuildParams(params)
+
+	if bparams.Description != "" {
+		bparams.Description = "${moduleDesc}" + params.Description + "${moduleDescSuffix}"
+	}
+
 	if a.missingDeps != nil {
 		a.ninjaError(bparams.Description, bparams.Outputs,
 			fmt.Errorf("module %s missing dependencies: %s\n",
@@ -654,7 +670,7 @@
 		return
 	}
 
-	a.ModuleContext.Build(pctx, bparams)
+	a.ModuleContext.Build(pctx.PackageContext, bparams)
 }
 
 func (a *androidModuleContext) GetMissingDependencies() []string {
@@ -751,6 +767,20 @@
 	})
 }
 
+func (a *androidModuleContext) VisitAllModuleVariants(visit func(Module)) {
+	a.ModuleContext.VisitAllModuleVariants(func(module blueprint.Module) {
+		visit(module.(Module))
+	})
+}
+
+func (a *androidModuleContext) PrimaryModule() Module {
+	return a.ModuleContext.PrimaryModule().(Module)
+}
+
+func (a *androidModuleContext) FinalModule() Module {
+	return a.ModuleContext.FinalModule().(Module)
+}
+
 func (a *androidBaseContextImpl) Target() Target {
 	return a.target
 }
@@ -1027,7 +1057,7 @@
 	RegisterSingletonType("buildtarget", BuildTargetSingleton)
 }
 
-func BuildTargetSingleton() blueprint.Singleton {
+func BuildTargetSingleton() Singleton {
 	return &buildTargetSingleton{}
 }
 
@@ -1038,29 +1068,28 @@
 
 type buildTargetSingleton struct{}
 
-func (c *buildTargetSingleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
-	checkbuildDeps := []string{}
+func (c *buildTargetSingleton) GenerateBuildActions(ctx SingletonContext) {
+	var checkbuildDeps Paths
 
-	mmTarget := func(dir string) string {
-		return "MODULES-IN-" + strings.Replace(filepath.Clean(dir), "/", "-", -1)
+	mmTarget := func(dir string) WritablePath {
+		return PathForPhony(ctx,
+			"MODULES-IN-"+strings.Replace(filepath.Clean(dir), "/", "-", -1))
 	}
 
-	modulesInDir := make(map[string][]string)
+	modulesInDir := make(map[string]Paths)
 
-	ctx.VisitAllModules(func(module blueprint.Module) {
-		if a, ok := module.(Module); ok {
-			blueprintDir := a.base().blueprintDir
-			installTarget := a.base().installTarget
-			checkbuildTarget := a.base().checkbuildTarget
+	ctx.VisitAllModules(func(module Module) {
+		blueprintDir := module.base().blueprintDir
+		installTarget := module.base().installTarget
+		checkbuildTarget := module.base().checkbuildTarget
 
-			if checkbuildTarget != "" {
-				checkbuildDeps = append(checkbuildDeps, checkbuildTarget)
-				modulesInDir[blueprintDir] = append(modulesInDir[blueprintDir], checkbuildTarget)
-			}
+		if checkbuildTarget != nil {
+			checkbuildDeps = append(checkbuildDeps, checkbuildTarget)
+			modulesInDir[blueprintDir] = append(modulesInDir[blueprintDir], checkbuildTarget)
+		}
 
-			if installTarget != "" {
-				modulesInDir[blueprintDir] = append(modulesInDir[blueprintDir], installTarget)
-			}
+		if installTarget != nil {
+			modulesInDir[blueprintDir] = append(modulesInDir[blueprintDir], installTarget)
 		}
 	})
 
@@ -1070,11 +1099,10 @@
 	}
 
 	// Create a top-level checkbuild target that depends on all modules
-	ctx.Build(pctx, blueprint.BuildParams{
+	ctx.Build(pctx, BuildParams{
 		Rule:      blueprint.Phony,
-		Outputs:   []string{"checkbuild" + suffix},
+		Output:    PathForPhony(ctx, "checkbuild"+suffix),
 		Implicits: checkbuildDeps,
-		Optional:  true,
 	})
 
 	// Make will generate the MODULES-IN-* targets
@@ -1082,6 +1110,15 @@
 		return
 	}
 
+	sortedKeys := func(m map[string]Paths) []string {
+		s := make([]string, 0, len(m))
+		for k := range m {
+			s = append(s, k)
+		}
+		sort.Strings(s)
+		return s
+	}
+
 	// Ensure ancestor directories are in modulesInDir
 	dirs := sortedKeys(modulesInDir)
 	for _, dir := range dirs {
@@ -1108,28 +1145,26 @@
 	// depends on the MODULES-IN-* targets of all of its subdirectories that contain Android.bp
 	// files.
 	for _, dir := range dirs {
-		ctx.Build(pctx, blueprint.BuildParams{
+		ctx.Build(pctx, BuildParams{
 			Rule:      blueprint.Phony,
-			Outputs:   []string{mmTarget(dir)},
+			Output:    mmTarget(dir),
 			Implicits: modulesInDir[dir],
 			// HACK: checkbuild should be an optional build, but force it
 			// enabled for now in standalone builds
-			Optional: ctx.Config().(Config).EmbeddedInMake(),
+			Default: !ctx.Config().(Config).EmbeddedInMake(),
 		})
 	}
 
 	// Create (host|host-cross|target)-<OS> phony rules to build a reduced checkbuild.
 	osDeps := map[OsType]Paths{}
-	ctx.VisitAllModules(func(module blueprint.Module) {
-		if a, ok := module.(Module); ok {
-			if a.Enabled() {
-				os := a.Target().Os
-				osDeps[os] = append(osDeps[os], a.base().checkbuildFiles...)
-			}
+	ctx.VisitAllModules(func(module Module) {
+		if module.Enabled() {
+			os := module.Target().Os
+			osDeps[os] = append(osDeps[os], module.base().checkbuildFiles...)
 		}
 	})
 
-	osClass := make(map[string][]string)
+	osClass := make(map[string]Paths)
 	for os, deps := range osDeps {
 		var className string
 
@@ -1144,25 +1179,23 @@
 			continue
 		}
 
-		name := className + "-" + os.Name
+		name := PathForPhony(ctx, className+"-"+os.Name)
 		osClass[className] = append(osClass[className], name)
 
-		ctx.Build(pctx, blueprint.BuildParams{
+		ctx.Build(pctx, BuildParams{
 			Rule:      blueprint.Phony,
-			Outputs:   []string{name},
-			Implicits: deps.Strings(),
-			Optional:  true,
+			Output:    name,
+			Implicits: deps,
 		})
 	}
 
 	// Wrap those into host|host-cross|target phony rules
 	osClasses := sortedKeys(osClass)
 	for _, class := range osClasses {
-		ctx.Build(pctx, blueprint.BuildParams{
+		ctx.Build(pctx, BuildParams{
 			Rule:      blueprint.Phony,
-			Outputs:   []string{class},
+			Output:    PathForPhony(ctx, class),
 			Implicits: osClass[class],
-			Optional:  true,
 		})
 	}
 }
diff --git a/android/package_ctx.go b/android/package_ctx.go
index d32e82b..1626f76 100644
--- a/android/package_ctx.go
+++ b/android/package_ctx.go
@@ -22,14 +22,14 @@
 	"github.com/google/blueprint/pathtools"
 )
 
-// AndroidPackageContext is a wrapper for blueprint.PackageContext that adds
+// PackageContext is a wrapper for blueprint.PackageContext that adds
 // some android-specific helper functions.
-type AndroidPackageContext struct {
+type PackageContext struct {
 	blueprint.PackageContext
 }
 
-func NewPackageContext(pkgPath string) AndroidPackageContext {
-	return AndroidPackageContext{blueprint.NewPackageContext(pkgPath)}
+func NewPackageContext(pkgPath string) PackageContext {
+	return PackageContext{blueprint.NewPackageContext(pkgPath)}
 }
 
 // configErrorWrapper can be used with Path functions when a Context is not
@@ -39,7 +39,7 @@
 // The most common use here will be with VariableFunc, where only a config is
 // provided, and an error should be returned.
 type configErrorWrapper struct {
-	pctx   AndroidPackageContext
+	pctx   PackageContext
 	config Config
 	errors []error
 }
@@ -61,13 +61,43 @@
 	return nil
 }
 
+// VariableFunc wraps blueprint.PackageContext.VariableFunc, converting the interface{} config
+// argument to a Config.
+func (p PackageContext) VariableFunc(name string,
+	f func(Config) (string, error)) blueprint.Variable {
+
+	return p.PackageContext.VariableFunc(name, func(config interface{}) (string, error) {
+		return f(config.(Config))
+	})
+}
+
+// PoolFunc wraps blueprint.PackageContext.PoolFunc, converting the interface{} config
+// argument to a Config.
+func (p PackageContext) PoolFunc(name string,
+	f func(Config) (blueprint.PoolParams, error)) blueprint.Pool {
+
+	return p.PackageContext.PoolFunc(name, func(config interface{}) (blueprint.PoolParams, error) {
+		return f(config.(Config))
+	})
+}
+
+// RuleFunc wraps blueprint.PackageContext.RuleFunc, converting the interface{} config
+// argument to a Config.
+func (p PackageContext) RuleFunc(name string,
+	f func(Config) (blueprint.RuleParams, error), argNames ...string) blueprint.Rule {
+
+	return p.PackageContext.RuleFunc(name, func(config interface{}) (blueprint.RuleParams, error) {
+		return f(config.(Config))
+	}, argNames...)
+}
+
 // SourcePathVariable returns a Variable whose value is the source directory
 // appended with the supplied path. It may only be called during a Go package's
 // initialization - either from the init() function or as part of a
 // package-scoped variable's initialization.
-func (p AndroidPackageContext) SourcePathVariable(name, path string) blueprint.Variable {
-	return p.VariableFunc(name, func(config interface{}) (string, error) {
-		ctx := &configErrorWrapper{p, config.(Config), []error{}}
+func (p PackageContext) SourcePathVariable(name, path string) blueprint.Variable {
+	return p.VariableFunc(name, func(config Config) (string, error) {
+		ctx := &configErrorWrapper{p, config, []error{}}
 		p := safePathForSource(ctx, path)
 		if len(ctx.errors) > 0 {
 			return "", ctx.errors[0]
@@ -80,9 +110,9 @@
 // appended with the supplied paths, joined with separator. It may only be
 // called during a Go package's initialization - either from the init()
 // function or as part of a package-scoped variable's initialization.
-func (p AndroidPackageContext) SourcePathsVariable(name, separator string, paths ...string) blueprint.Variable {
-	return p.VariableFunc(name, func(config interface{}) (string, error) {
-		ctx := &configErrorWrapper{p, config.(Config), []error{}}
+func (p PackageContext) SourcePathsVariable(name, separator string, paths ...string) blueprint.Variable {
+	return p.VariableFunc(name, func(config Config) (string, error) {
+		ctx := &configErrorWrapper{p, config, []error{}}
 		var ret []string
 		for _, path := range paths {
 			p := safePathForSource(ctx, path)
@@ -100,14 +130,14 @@
 // The environment variable is not required to point to a path inside the source tree.
 // It may only be called during a Go package's initialization - either from the init() function or
 // as part of a package-scoped variable's initialization.
-func (p AndroidPackageContext) SourcePathVariableWithEnvOverride(name, path, env string) blueprint.Variable {
-	return p.VariableFunc(name, func(config interface{}) (string, error) {
-		ctx := &configErrorWrapper{p, config.(Config), []error{}}
+func (p PackageContext) SourcePathVariableWithEnvOverride(name, path, env string) blueprint.Variable {
+	return p.VariableFunc(name, func(config Config) (string, error) {
+		ctx := &configErrorWrapper{p, config, []error{}}
 		p := safePathForSource(ctx, path)
 		if len(ctx.errors) > 0 {
 			return "", ctx.errors[0]
 		}
-		return config.(Config).GetenvWithDefault(env, p.String()), nil
+		return config.GetenvWithDefault(env, p.String()), nil
 	})
 }
 
@@ -115,8 +145,8 @@
 // in the bin directory for host targets. It may only be called during a Go
 // package's initialization - either from the init() function or as part of a
 // package-scoped variable's initialization.
-func (p AndroidPackageContext) HostBinToolVariable(name, path string) blueprint.Variable {
-	return p.VariableFunc(name, func(config interface{}) (string, error) {
+func (p PackageContext) HostBinToolVariable(name, path string) blueprint.Variable {
+	return p.VariableFunc(name, func(config Config) (string, error) {
 		po, err := p.HostBinToolPath(config, path)
 		if err != nil {
 			return "", err
@@ -125,8 +155,8 @@
 	})
 }
 
-func (p AndroidPackageContext) HostBinToolPath(config interface{}, path string) (Path, error) {
-	ctx := &configErrorWrapper{p, config.(Config), []error{}}
+func (p PackageContext) HostBinToolPath(config Config, path string) (Path, error) {
+	ctx := &configErrorWrapper{p, config, []error{}}
 	pa := PathForOutput(ctx, "host", ctx.config.PrebuiltOS(), "bin", path)
 	if len(ctx.errors) > 0 {
 		return nil, ctx.errors[0]
@@ -138,9 +168,9 @@
 // tool in the frameworks directory for host targets. It may only be called
 // during a Go package's initialization - either from the init() function or as
 // part of a package-scoped variable's initialization.
-func (p AndroidPackageContext) HostJavaToolVariable(name, path string) blueprint.Variable {
-	return p.VariableFunc(name, func(config interface{}) (string, error) {
-		ctx := &configErrorWrapper{p, config.(Config), []error{}}
+func (p PackageContext) HostJavaToolVariable(name, path string) blueprint.Variable {
+	return p.VariableFunc(name, func(config Config) (string, error) {
+		ctx := &configErrorWrapper{p, config, []error{}}
 		p := PathForOutput(ctx, "host", ctx.config.PrebuiltOS(), "framework", path)
 		if len(ctx.errors) > 0 {
 			return "", ctx.errors[0]
@@ -149,8 +179,8 @@
 	})
 }
 
-func (p AndroidPackageContext) HostJavaToolPath(config interface{}, path string) (Path, error) {
-	ctx := &configErrorWrapper{p, config.(Config), []error{}}
+func (p PackageContext) HostJavaToolPath(config Config, path string) (Path, error) {
+	ctx := &configErrorWrapper{p, config, []error{}}
 	pa := PathForOutput(ctx, "host", ctx.config.PrebuiltOS(), "framework", path)
 	if len(ctx.errors) > 0 {
 		return nil, ctx.errors[0]
@@ -162,9 +192,9 @@
 // directory appended with the supplied path. It may only be called during a Go
 // package's initialization - either from the init() function or as part of a
 // package-scoped variable's initialization.
-func (p AndroidPackageContext) IntermediatesPathVariable(name, path string) blueprint.Variable {
-	return p.VariableFunc(name, func(config interface{}) (string, error) {
-		ctx := &configErrorWrapper{p, config.(Config), []error{}}
+func (p PackageContext) IntermediatesPathVariable(name, path string) blueprint.Variable {
+	return p.VariableFunc(name, func(config Config) (string, error) {
+		ctx := &configErrorWrapper{p, config, []error{}}
 		p := PathForIntermediates(ctx, path)
 		if len(ctx.errors) > 0 {
 			return "", ctx.errors[0]
@@ -177,11 +207,11 @@
 // list of present source paths prefixed with the supplied prefix. It may only
 // be called during a Go package's initialization - either from the init()
 // function or as part of a package-scoped variable's initialization.
-func (p AndroidPackageContext) PrefixedExistentPathsForSourcesVariable(
+func (p PackageContext) PrefixedExistentPathsForSourcesVariable(
 	name, prefix string, paths []string) blueprint.Variable {
 
-	return p.VariableFunc(name, func(config interface{}) (string, error) {
-		ctx := &configErrorWrapper{p, config.(Config), []error{}}
+	return p.VariableFunc(name, func(config Config) (string, error) {
+		ctx := &configErrorWrapper{p, config, []error{}}
 		paths := ExistentPathsForSources(ctx, "", paths)
 		if len(ctx.errors) > 0 {
 			return "", ctx.errors[0]
@@ -196,7 +226,7 @@
 }
 
 // AndroidStaticRule wraps blueprint.StaticRule and provides a default Pool if none is specified
-func (p AndroidPackageContext) AndroidStaticRule(name string, params blueprint.RuleParams,
+func (p PackageContext) AndroidStaticRule(name string, params blueprint.RuleParams,
 	argNames ...string) blueprint.Rule {
 	return p.AndroidRuleFunc(name, func(Config) (blueprint.RuleParams, error) {
 		return params, nil
@@ -204,16 +234,16 @@
 }
 
 // AndroidGomaStaticRule wraps blueprint.StaticRule but uses goma's parallelism if goma is enabled
-func (p AndroidPackageContext) AndroidGomaStaticRule(name string, params blueprint.RuleParams,
+func (p PackageContext) AndroidGomaStaticRule(name string, params blueprint.RuleParams,
 	argNames ...string) blueprint.Rule {
 	return p.StaticRule(name, params, argNames...)
 }
 
-func (p AndroidPackageContext) AndroidRuleFunc(name string,
+func (p PackageContext) AndroidRuleFunc(name string,
 	f func(Config) (blueprint.RuleParams, error), argNames ...string) blueprint.Rule {
-	return p.PackageContext.RuleFunc(name, func(config interface{}) (blueprint.RuleParams, error) {
-		params, err := f(config.(Config))
-		if config.(Config).UseGoma() && params.Pool == nil {
+	return p.RuleFunc(name, func(config Config) (blueprint.RuleParams, error) {
+		params, err := f(config)
+		if config.UseGoma() && params.Pool == nil {
 			// When USE_GOMA=true is set and the rule is not supported by goma, restrict jobs to the
 			// local parallelism value
 			params.Pool = localPool
diff --git a/android/paths.go b/android/paths.go
index 4adaa2d..71049ee 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -428,6 +428,18 @@
 	return ret
 }
 
+// Paths returns the WritablePaths as a Paths
+func (p WritablePaths) Paths() Paths {
+	if p == nil {
+		return nil
+	}
+	ret := make(Paths, len(p))
+	for i, path := range p {
+		ret[i] = path
+	}
+	return ret
+}
+
 type basePath struct {
 	path   string
 	config Config
@@ -449,6 +461,10 @@
 	return p.path
 }
 
+func (p basePath) String() string {
+	return p.path
+}
+
 // SourcePath is a Path representing a file path rooted from SrcDir
 type SourcePath struct {
 	basePath
@@ -885,6 +901,13 @@
 	return validateSafePath(ctx, pathComponents...)
 }
 
+func PathForPhony(ctx PathContext, phony string) WritablePath {
+	if strings.ContainsAny(phony, "$/") {
+		reportPathError(ctx, "Phony target contains invalid character ($ or /): %s", phony)
+	}
+	return OutputPath{basePath{phony, pathConfig(ctx), ""}}
+}
+
 type testPath struct {
 	basePath
 }
diff --git a/android/register.go b/android/register.go
index 81a266d..78ae481 100644
--- a/android/register.go
+++ b/android/register.go
@@ -44,7 +44,7 @@
 
 type ModuleFactory func() Module
 
-// ModuleFactoryAdaptor Wraps a ModuleFactory into a blueprint.ModuleFactory by converting an Module
+// ModuleFactoryAdaptor wraps a ModuleFactory into a blueprint.ModuleFactory by converting a Module
 // into a blueprint.Module and a list of property structs
 func ModuleFactoryAdaptor(factory ModuleFactory) blueprint.ModuleFactory {
 	return func() (blueprint.Module, []interface{}) {
@@ -53,16 +53,27 @@
 	}
 }
 
+type SingletonFactory func() Singleton
+
+// SingletonFactoryAdaptor wraps a SingletonFactory into a blueprint.SingletonFactory by converting
+// a Singleton into a blueprint.Singleton
+func SingletonFactoryAdaptor(factory SingletonFactory) blueprint.SingletonFactory {
+	return func() blueprint.Singleton {
+		singleton := factory()
+		return singletonAdaptor{singleton}
+	}
+}
+
 func RegisterModuleType(name string, factory ModuleFactory) {
 	moduleTypes = append(moduleTypes, moduleType{name, ModuleFactoryAdaptor(factory)})
 }
 
-func RegisterSingletonType(name string, factory blueprint.SingletonFactory) {
-	singletons = append(singletons, singleton{name, factory})
+func RegisterSingletonType(name string, factory SingletonFactory) {
+	singletons = append(singletons, singleton{name, SingletonFactoryAdaptor(factory)})
 }
 
-func RegisterPreSingletonType(name string, factory blueprint.SingletonFactory) {
-	preSingletons = append(preSingletons, singleton{name, factory})
+func RegisterPreSingletonType(name string, factory SingletonFactory) {
+	preSingletons = append(preSingletons, singleton{name, SingletonFactoryAdaptor(factory)})
 }
 
 type Context struct {
diff --git a/android/singleton.go b/android/singleton.go
new file mode 100644
index 0000000..f2f575f
--- /dev/null
+++ b/android/singleton.go
@@ -0,0 +1,162 @@
+// 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 (
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/pathtools"
+)
+
+// SingletonContext
+type SingletonContext interface {
+	// TODO(ccross): make this return an android.Config
+	Config() interface{}
+
+	ModuleName(module blueprint.Module) string
+	ModuleDir(module blueprint.Module) string
+	ModuleSubDir(module blueprint.Module) string
+	ModuleType(module blueprint.Module) string
+	BlueprintFile(module blueprint.Module) string
+
+	ModuleErrorf(module blueprint.Module, format string, args ...interface{})
+	Errorf(format string, args ...interface{})
+	Failed() bool
+
+	Variable(pctx PackageContext, name, value string)
+	Rule(pctx PackageContext, name string, params RuleParams, argNames ...string) blueprint.Rule
+	Build(pctx PackageContext, params BuildParams)
+	RequireNinjaVersion(major, minor, micro int)
+
+	// SetNinjaBuildDir sets the value of the top-level "builddir" Ninja variable
+	// that controls where Ninja stores its build log files.  This value can be
+	// set at most one time for a single build, later calls are ignored.
+	SetNinjaBuildDir(pctx PackageContext, value string)
+
+	// Eval takes a string with embedded ninja variables, and returns a string
+	// with all of the variables recursively expanded. Any variables references
+	// are expanded in the scope of the PackageContext.
+	Eval(pctx PackageContext, ninjaStr string) (string, error)
+
+	VisitAllModules(visit func(Module))
+	VisitAllModulesIf(pred func(Module) bool, visit func(Module))
+	VisitDepsDepthFirst(module Module, visit func(Module))
+	VisitDepsDepthFirstIf(module Module, pred func(Module) bool,
+		visit func(Module))
+
+	VisitAllModuleVariants(module Module, visit func(Module))
+
+	PrimaryModule(module Module) Module
+	FinalModule(module Module) Module
+
+	AddNinjaFileDeps(deps ...string)
+
+	// GlobWithDeps returns a list of files that match the specified pattern but do not match any
+	// of the patterns in excludes.  It also adds efficient dependencies to rerun the primary
+	// builder whenever a file matching the pattern as added or removed, without rerunning if a
+	// file that does not match the pattern is added to a searched directory.
+	GlobWithDeps(pattern string, excludes []string) ([]string, error)
+
+	Fs() pathtools.FileSystem
+}
+
+type singletonAdaptor struct {
+	Singleton
+}
+
+func (s singletonAdaptor) GenerateBuildActions(ctx blueprint.SingletonContext) {
+	s.Singleton.GenerateBuildActions(singletonContextAdaptor{ctx})
+}
+
+type Singleton interface {
+	GenerateBuildActions(SingletonContext)
+}
+
+type singletonContextAdaptor struct {
+	blueprint.SingletonContext
+}
+
+func (s singletonContextAdaptor) Variable(pctx PackageContext, name, value string) {
+	s.SingletonContext.Variable(pctx.PackageContext, name, value)
+}
+
+func (s singletonContextAdaptor) Rule(pctx PackageContext, name string, params RuleParams, argNames ...string) blueprint.Rule {
+	return s.SingletonContext.Rule(pctx.PackageContext, name, params.RuleParams, argNames...)
+}
+
+func (s singletonContextAdaptor) Build(pctx PackageContext, params BuildParams) {
+	bparams := convertBuildParams(params)
+	s.SingletonContext.Build(pctx.PackageContext, bparams)
+
+}
+
+func (s singletonContextAdaptor) SetNinjaBuildDir(pctx PackageContext, value string) {
+	s.SingletonContext.SetNinjaBuildDir(pctx.PackageContext, value)
+}
+
+func (s singletonContextAdaptor) Eval(pctx PackageContext, ninjaStr string) (string, error) {
+	return s.SingletonContext.Eval(pctx.PackageContext, ninjaStr)
+}
+
+// visitAdaptor wraps a visit function that takes an android.Module parameter into
+// a function that takes an blueprint.Module parameter and only calls the visit function if the
+// blueprint.Module is an android.Module.
+func visitAdaptor(visit func(Module)) func(blueprint.Module) {
+	return func(module blueprint.Module) {
+		if aModule, ok := module.(Module); ok {
+			visit(aModule)
+		}
+	}
+}
+
+// predAdaptor wraps a pred function that takes an android.Module parameter
+// into a function that takes an blueprint.Module parameter and only calls the visit function if the
+// blueprint.Module is an android.Module, otherwise returns false.
+func predAdaptor(pred func(Module) bool) func(blueprint.Module) bool {
+	return func(module blueprint.Module) bool {
+		if aModule, ok := module.(Module); ok {
+			return pred(aModule)
+		} else {
+			return false
+		}
+	}
+}
+
+func (s singletonContextAdaptor) VisitAllModules(visit func(Module)) {
+	s.SingletonContext.VisitAllModules(visitAdaptor(visit))
+}
+
+func (s singletonContextAdaptor) VisitAllModulesIf(pred func(Module) bool, visit func(Module)) {
+	s.SingletonContext.VisitAllModulesIf(predAdaptor(pred), visitAdaptor(visit))
+}
+
+func (s singletonContextAdaptor) VisitDepsDepthFirst(module Module, visit func(Module)) {
+	s.SingletonContext.VisitDepsDepthFirst(module, visitAdaptor(visit))
+}
+
+func (s singletonContextAdaptor) VisitDepsDepthFirstIf(module Module, pred func(Module) bool, visit func(Module)) {
+	s.SingletonContext.VisitDepsDepthFirstIf(module, predAdaptor(pred), visitAdaptor(visit))
+}
+
+func (s singletonContextAdaptor) VisitAllModuleVariants(module Module, visit func(Module)) {
+	s.SingletonContext.VisitAllModuleVariants(module, visitAdaptor(visit))
+}
+
+func (s singletonContextAdaptor) PrimaryModule(module Module) Module {
+	return s.SingletonContext.PrimaryModule(module).(Module)
+}
+
+func (s singletonContextAdaptor) FinalModule(module Module) Module {
+	return s.SingletonContext.FinalModule(module).(Module)
+}
diff --git a/android/variable.go b/android/variable.go
index 4272817..13b5abf 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -189,6 +189,7 @@
 	Override_rs_driver *string `json:",omitempty"`
 
 	DeviceKernelHeaders []string `json:",omitempty"`
+	DistDir             *string  `json:",omitempty"`
 }
 
 func boolPtr(v bool) *bool {
diff --git a/androidmk/cmd/androidmk/android.go b/androidmk/cmd/androidmk/android.go
index df01cf4..73ea4ac 100644
--- a/androidmk/cmd/androidmk/android.go
+++ b/androidmk/cmd/androidmk/android.go
@@ -24,7 +24,8 @@
 )
 
 const (
-	clear_vars = "__android_mk_clear_vars"
+	clear_vars      = "__android_mk_clear_vars"
+	include_ignored = "__android_mk_include_ignored"
 )
 
 type bpVariable struct {
@@ -693,6 +694,10 @@
 	return "**/*.java"
 }
 
+func includeIgnored(args []string) string {
+	return include_ignored
+}
+
 var moduleTypes = map[string]string{
 	"BUILD_SHARED_LIBRARY":        "cc_library_shared",
 	"BUILD_STATIC_LIBRARY":        "cc_library_static",
@@ -729,6 +734,10 @@
 	globalScope.SetFunc("all-java-files-under", allJavaFilesUnder)
 	globalScope.SetFunc("all-proto-files-under", allProtoFilesUnder)
 	globalScope.SetFunc("all-subdir-java-files", allSubdirJavaFiles)
+	globalScope.SetFunc("all-makefiles-under", includeIgnored)
+	globalScope.SetFunc("first-makefiles-under", includeIgnored)
+	globalScope.SetFunc("all-named-subdir-makefiles", includeIgnored)
+	globalScope.SetFunc("all-subdir-makefiles", includeIgnored)
 
 	for k, v := range moduleTypes {
 		globalScope.Set(k, v)
diff --git a/androidmk/cmd/androidmk/androidmk.go b/androidmk/cmd/androidmk/androidmk.go
index 660d9a0..385690c 100644
--- a/androidmk/cmd/androidmk/androidmk.go
+++ b/androidmk/cmd/androidmk/androidmk.go
@@ -177,6 +177,9 @@
 					makeModule(file, val)
 				case val == clear_vars:
 					resetModule(file)
+				case val == include_ignored:
+					// subdirs are already automatically included in Soong
+					continue
 				default:
 					file.errorf(x, "unsupported include")
 					continue
diff --git a/androidmk/cmd/androidmk/androidmk_test.go b/androidmk/cmd/androidmk/androidmk_test.go
index 0b86540..9986889 100644
--- a/androidmk/cmd/androidmk/androidmk_test.go
+++ b/androidmk/cmd/androidmk/androidmk_test.go
@@ -460,6 +460,13 @@
 // endif
 		`,
 	},
+	{
+		desc: "ignore all-makefiles-under",
+		in: `
+include $(call all-makefiles-under,$(LOCAL_PATH))
+`,
+		expected: ``,
+	},
 }
 
 func reformatBlueprint(input string) string {
diff --git a/cc/androidmk.go b/cc/androidmk.go
index 065d0aa..44e977f 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -318,7 +318,7 @@
 	ret.Class = "SHARED_LIBRARIES"
 
 	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
-		path, file := filepath.Split(c.installPath)
+		path, file := filepath.Split(c.installPath.String())
 		stem := strings.TrimSuffix(file, filepath.Ext(file))
 		fmt.Fprintln(w, "LOCAL_SYSTEM_SHARED_LIBRARIES :=")
 		fmt.Fprintln(w, "LOCAL_MODULE_SUFFIX := "+outputFile.Ext())
diff --git a/cc/builder.go b/cc/builder.go
index a81dc89..b5f4c5c 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -196,11 +196,19 @@
 
 	_ = pctx.SourcePathVariable("sAbiDiffer", "prebuilts/build-tools/${config.HostPrebuiltTag}/bin/header-abi-diff")
 
-	// Abidiff check turned on in advice-only mode. Builds will not fail on abi incompatibilties / extensions.
-	sAbiDiff = pctx.AndroidStaticRule("sAbiDiff",
-		blueprint.RuleParams{
-			Command:     "$sAbiDiffer $allowFlags -lib $libName -arch $arch -check-all-apis -o ${out} -new $in -old $referenceDump",
-			CommandDeps: []string{"$sAbiDiffer"},
+	sAbiDiff = pctx.AndroidRuleFunc("sAbiDiff",
+		func(config android.Config) (blueprint.RuleParams, error) {
+
+			commandStr := "($sAbiDiffer $allowFlags -lib $libName -arch $arch -check-all-apis -o ${out} -new $in -old $referenceDump)"
+			distDir := config.ProductVariables.DistDir
+			if distDir != nil && *distDir != "" {
+				distAbiDiffDir := *distDir + "/abidiffs/"
+				commandStr += "  || (mkdir -p " + distAbiDiffDir + " && cp ${out} " + distAbiDiffDir + " && exit 1)"
+			}
+			return blueprint.RuleParams{
+				Command:     commandStr,
+				CommandDeps: []string{"$sAbiDiffer"},
+			}, nil
 		},
 		"allowFlags", "referenceDump", "libName", "arch")
 
diff --git a/cc/cc.go b/cc/cc.go
index 384b240..e18b2cc 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -326,9 +326,12 @@
 	flags Flags
 
 	// When calling a linker, if module A depends on module B, then A must precede B in its command
-	// line invocation. staticDepsInLinkOrder stores the proper ordering of all of the transitive
+	// line invocation. depsInLinkOrder stores the proper ordering of all of the transitive
 	// deps of this module
-	staticDepsInLinkOrder android.Paths
+	depsInLinkOrder android.Paths
+
+	// only non-nil when this is a shared library that reuses the objects of a static library
+	staticVariant *Module
 }
 
 func (c *Module) Init() android.Module {
@@ -537,7 +540,8 @@
 // orderDeps reorders dependencies into a list such that if module A depends on B, then
 // A will precede B in the resultant list.
 // This is convenient for passing into a linker.
-func orderDeps(directDeps []android.Path, transitiveDeps map[android.Path][]android.Path) (orderedAllDeps []android.Path, orderedDeclaredDeps []android.Path) {
+// Note that directSharedDeps should be the analogous static library for each shared lib dep
+func orderDeps(directStaticDeps []android.Path, directSharedDeps []android.Path, allTransitiveDeps map[android.Path][]android.Path) (orderedAllDeps []android.Path, orderedDeclaredDeps []android.Path) {
 	// If A depends on B, then
 	//   Every list containing A will also contain B later in the list
 	//   So, after concatenating all lists, the final instance of B will have come from the same
@@ -545,38 +549,46 @@
 	//   So, the final instance of B will be later in the concatenation than the final A
 	//   So, keeping only the final instance of A and of B ensures that A is earlier in the output
 	//     list than B
-	for _, dep := range directDeps {
+	for _, dep := range directStaticDeps {
 		orderedAllDeps = append(orderedAllDeps, dep)
-		orderedAllDeps = append(orderedAllDeps, transitiveDeps[dep]...)
+		orderedAllDeps = append(orderedAllDeps, allTransitiveDeps[dep]...)
+	}
+	for _, dep := range directSharedDeps {
+		orderedAllDeps = append(orderedAllDeps, dep)
+		orderedAllDeps = append(orderedAllDeps, allTransitiveDeps[dep]...)
 	}
 
 	orderedAllDeps = android.LastUniquePaths(orderedAllDeps)
 
-	// We don't want to add any new dependencies into directDeps (to allow the caller to
+	// We don't want to add any new dependencies into directStaticDeps (to allow the caller to
 	// intentionally exclude or replace any unwanted transitive dependencies), so we limit the
-	// resultant list to only what the caller has chosen to include in directDeps
-	_, orderedDeclaredDeps = android.FilterPathList(orderedAllDeps, directDeps)
+	// resultant list to only what the caller has chosen to include in directStaticDeps
+	_, orderedDeclaredDeps = android.FilterPathList(orderedAllDeps, directStaticDeps)
 
 	return orderedAllDeps, orderedDeclaredDeps
 }
 
-func orderStaticModuleDeps(module *Module, deps []*Module) (results []android.Path) {
-	// make map of transitive dependencies
-	transitiveStaticDepNames := make(map[android.Path][]android.Path, len(deps))
-	for _, dep := range deps {
-		transitiveStaticDepNames[dep.outputFile.Path()] = dep.staticDepsInLinkOrder
+func orderStaticModuleDeps(module *Module, staticDeps []*Module, sharedDeps []*Module) (results []android.Path) {
+	// convert Module to Path
+	allTransitiveDeps := make(map[android.Path][]android.Path, len(staticDeps))
+	staticDepFiles := []android.Path{}
+	for _, dep := range staticDeps {
+		allTransitiveDeps[dep.outputFile.Path()] = dep.depsInLinkOrder
+		staticDepFiles = append(staticDepFiles, dep.outputFile.Path())
 	}
-	// get the output file for each declared dependency
-	depFiles := []android.Path{}
-	for _, dep := range deps {
-		depFiles = append(depFiles, dep.outputFile.Path())
+	sharedDepFiles := []android.Path{}
+	for _, sharedDep := range sharedDeps {
+		staticAnalogue := sharedDep.staticVariant
+		if staticAnalogue != nil {
+			allTransitiveDeps[staticAnalogue.outputFile.Path()] = staticAnalogue.depsInLinkOrder
+			sharedDepFiles = append(sharedDepFiles, staticAnalogue.outputFile.Path())
+		}
 	}
 
 	// reorder the dependencies based on transitive dependencies
-	module.staticDepsInLinkOrder, results = orderDeps(depFiles, transitiveStaticDepNames)
+	module.depsInLinkOrder, results = orderDeps(staticDepFiles, sharedDepFiles, allTransitiveDeps)
 
 	return results
-
 }
 
 func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) {
@@ -1026,6 +1038,7 @@
 	var depPaths PathDeps
 
 	directStaticDeps := []*Module{}
+	directSharedDeps := []*Module{}
 
 	ctx.VisitDirectDeps(func(dep android.Module) {
 		depName := ctx.OtherModuleName(dep)
@@ -1092,6 +1105,7 @@
 		// re-exporting flags
 		if depTag == reuseObjTag {
 			if l, ok := ccDep.compiler.(libraryInterface); ok {
+				c.staticVariant = ccDep
 				objs, flags, deps := l.reuseObjs()
 				depPaths.Objs = depPaths.Objs.Append(objs)
 				depPaths.ReexportedFlags = append(depPaths.ReexportedFlags, flags...)
@@ -1130,6 +1144,7 @@
 			ptr = &depPaths.SharedLibs
 			depPtr = &depPaths.SharedLibsDeps
 			depFile = ccDep.linker.(libraryInterface).toc()
+			directSharedDeps = append(directSharedDeps, ccDep)
 		case lateSharedDepTag, ndkLateStubDepTag:
 			ptr = &depPaths.LateSharedLibs
 			depPtr = &depPaths.LateSharedLibsDeps
@@ -1221,7 +1236,7 @@
 	})
 
 	// use the ordered dependencies as this module's dependencies
-	depPaths.StaticLibs = append(depPaths.StaticLibs, orderStaticModuleDeps(c, directStaticDeps)...)
+	depPaths.StaticLibs = append(depPaths.StaticLibs, orderStaticModuleDeps(c, directStaticDeps, directSharedDeps)...)
 
 	// Dedup exported flags from dependencies
 	depPaths.Flags = android.FirstUniqueStrings(depPaths.Flags)
diff --git a/cc/cc_test.go b/cc/cc_test.go
index bdc8c17..148b4dd 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -289,7 +289,11 @@
 var staticLinkDepOrderTestCases = []struct {
 	// This is a string representation of a map[moduleName][]moduleDependency .
 	// It models the dependencies declared in an Android.bp file.
-	in string
+	inStatic string
+
+	// This is a string representation of a map[moduleName][]moduleDependency .
+	// It models the dependencies declared in an Android.bp file.
+	inShared string
 
 	// allOrdered is a string representation of a map[moduleName][]moduleDependency .
 	// The keys of allOrdered specify which modules we would like to check.
@@ -305,82 +309,99 @@
 }{
 	// Simple tests
 	{
-		in:         "",
+		inStatic:   "",
 		outOrdered: "",
 	},
 	{
-		in:         "a:",
+		inStatic:   "a:",
 		outOrdered: "a:",
 	},
 	{
-		in:         "a:b; b:",
+		inStatic:   "a:b; b:",
 		outOrdered: "a:b; b:",
 	},
 	// Tests of reordering
 	{
 		// diamond example
-		in:         "a:d,b,c; b:d; c:d; d:",
+		inStatic:   "a:d,b,c; b:d; c:d; d:",
 		outOrdered: "a:b,c,d; b:d; c:d; d:",
 	},
 	{
 		// somewhat real example
-		in:         "bsdiff_unittest:b,c,d,e,f,g,h,i; e:b",
+		inStatic:   "bsdiff_unittest:b,c,d,e,f,g,h,i; e:b",
 		outOrdered: "bsdiff_unittest:c,d,e,b,f,g,h,i; e:b",
 	},
 	{
 		// multiple reorderings
-		in:         "a:b,c,d,e; d:b; e:c",
+		inStatic:   "a:b,c,d,e; d:b; e:c",
 		outOrdered: "a:d,b,e,c; d:b; e:c",
 	},
 	{
 		// should reorder without adding new transitive dependencies
-		in:         "bin:lib2,lib1;             lib1:lib2,liboptional",
+		inStatic:   "bin:lib2,lib1;             lib1:lib2,liboptional",
 		allOrdered: "bin:lib1,lib2,liboptional; lib1:lib2,liboptional",
 		outOrdered: "bin:lib1,lib2;             lib1:lib2,liboptional",
 	},
 	{
 		// multiple levels of dependencies
-		in:         "a:b,c,d,e,f,g,h; f:b,c,d; b:c,d; c:d",
+		inStatic:   "a:b,c,d,e,f,g,h; f:b,c,d; b:c,d; c:d",
 		allOrdered: "a:e,f,b,c,d,g,h; f:b,c,d; b:c,d; c:d",
 		outOrdered: "a:e,f,b,c,d,g,h; f:b,c,d; b:c,d; c:d",
 	},
+	// shared dependencies
+	{
+		// Note that this test doesn't recurse, to minimize the amount of logic it tests.
+		// So, we don't actually have to check that a shared dependency of c will change the order
+		// of a library that depends statically on b and on c.  We only need to check that if c has
+		// a shared dependency on b, that that shows up in allOrdered.
+		inShared:   "c:b",
+		allOrdered: "c:b",
+		outOrdered: "c:",
+	},
+	{
+		// This test doesn't actually include any shared dependencies but it's a reminder of what
+		// the second phase of the above test would look like
+		inStatic:   "a:b,c; c:b",
+		allOrdered: "a:c,b; c:b",
+		outOrdered: "a:c,b; c:b",
+	},
 	// tiebreakers for when two modules specifying different orderings and there is no dependency
 	// to dictate an order
 	{
 		// if the tie is between two modules at the end of a's deps, then a's order wins
-		in:         "a1:b,c,d,e; a2:b,c,e,d; b:d,e; c:e,d",
+		inStatic:   "a1:b,c,d,e; a2:b,c,e,d; b:d,e; c:e,d",
 		outOrdered: "a1:b,c,d,e; a2:b,c,e,d; b:d,e; c:e,d",
 	},
 	{
 		// if the tie is between two modules at the start of a's deps, then c's order is used
-		in:         "a1:d,e,b1,c1; b1:d,e; c1:e,d;   a2:d,e,b2,c2; b2:d,e; c2:d,e",
+		inStatic:   "a1:d,e,b1,c1; b1:d,e; c1:e,d;   a2:d,e,b2,c2; b2:d,e; c2:d,e",
 		outOrdered: "a1:b1,c1,e,d; b1:d,e; c1:e,d;   a2:b2,c2,d,e; b2:d,e; c2:d,e",
 	},
 	// Tests involving duplicate dependencies
 	{
 		// simple duplicate
-		in:         "a:b,c,c,b",
+		inStatic:   "a:b,c,c,b",
 		outOrdered: "a:c,b",
 	},
 	{
 		// duplicates with reordering
-		in:         "a:b,c,d,c; c:b",
+		inStatic:   "a:b,c,d,c; c:b",
 		outOrdered: "a:d,c,b",
 	},
 	// Tests to confirm the nonexistence of infinite loops.
 	// These cases should never happen, so as long as the test terminates and the
 	// result is deterministic then that should be fine.
 	{
-		in:         "a:a",
+		inStatic:   "a:a",
 		outOrdered: "a:a",
 	},
 	{
-		in:         "a:b;   b:c;   c:a",
+		inStatic:   "a:b;   b:c;   c:a",
 		allOrdered: "a:b,c; b:c,a; c:a,b",
 		outOrdered: "a:b;   b:c;   c:a",
 	},
 	{
-		in:         "a:b,c;   b:c,a;   c:a,b",
+		inStatic:   "a:b,c;   b:c,a;   c:a,b",
 		allOrdered: "a:c,a,b; b:a,b,c; c:b,c,a",
 		outOrdered: "a:c,b;   b:a,c;   c:b,a",
 	},
@@ -427,43 +448,47 @@
 	return modulesInOrder, allDeps
 }
 
-func TestStaticLinkDependencyOrdering(t *testing.T) {
+func TestLinkReordering(t *testing.T) {
 	for _, testCase := range staticLinkDepOrderTestCases {
 		errs := []string{}
 
 		// parse testcase
-		_, givenTransitiveDeps := parseModuleDeps(testCase.in)
+		_, givenTransitiveDeps := parseModuleDeps(testCase.inStatic)
 		expectedModuleNames, expectedTransitiveDeps := parseModuleDeps(testCase.outOrdered)
 		if testCase.allOrdered == "" {
 			// allow the test case to skip specifying allOrdered
 			testCase.allOrdered = testCase.outOrdered
 		}
 		_, expectedAllDeps := parseModuleDeps(testCase.allOrdered)
+		_, givenAllSharedDeps := parseModuleDeps(testCase.inShared)
 
 		// For each module whose post-reordered dependencies were specified, validate that
 		// reordering the inputs produces the expected outputs.
 		for _, moduleName := range expectedModuleNames {
 			moduleDeps := givenTransitiveDeps[moduleName]
-			orderedAllDeps, orderedDeclaredDeps := orderDeps(moduleDeps, givenTransitiveDeps)
+			givenSharedDeps := givenAllSharedDeps[moduleName]
+			orderedAllDeps, orderedDeclaredDeps := orderDeps(moduleDeps, givenSharedDeps, givenTransitiveDeps)
 
 			correctAllOrdered := expectedAllDeps[moduleName]
 			if !reflect.DeepEqual(orderedAllDeps, correctAllOrdered) {
 				errs = append(errs, fmt.Sprintf("orderDeps returned incorrect orderedAllDeps."+
-					"\nInput:    %q"+
+					"\nin static:%q"+
+					"\nin shared:%q"+
 					"\nmodule:   %v"+
 					"\nexpected: %s"+
 					"\nactual:   %s",
-					testCase.in, moduleName, correctAllOrdered, orderedAllDeps))
+					testCase.inStatic, testCase.inShared, moduleName, correctAllOrdered, orderedAllDeps))
 			}
 
 			correctOutputDeps := expectedTransitiveDeps[moduleName]
 			if !reflect.DeepEqual(correctOutputDeps, orderedDeclaredDeps) {
 				errs = append(errs, fmt.Sprintf("orderDeps returned incorrect orderedDeclaredDeps."+
-					"\nInput:    %q"+
+					"\nin static:%q"+
+					"\nin shared:%q"+
 					"\nmodule:   %v"+
 					"\nexpected: %s"+
 					"\nactual:   %s",
-					testCase.in, moduleName, correctOutputDeps, orderedDeclaredDeps))
+					testCase.inStatic, testCase.inShared, moduleName, correctOutputDeps, orderedDeclaredDeps))
 			}
 		}
 
@@ -493,7 +518,7 @@
 	return paths
 }
 
-func TestLibDeps(t *testing.T) {
+func TestStaticLibDepReordering(t *testing.T) {
 	ctx := testCc(t, `
 	cc_library {
 		name: "a",
@@ -514,7 +539,7 @@
 
 	variant := "android_arm64_armv8-a_core_static"
 	moduleA := ctx.ModuleForTests("a", variant).Module().(*Module)
-	actual := moduleA.staticDepsInLinkOrder
+	actual := moduleA.depsInLinkOrder
 	expected := getOutputPaths(ctx, variant, []string{"c", "b", "d"})
 
 	if !reflect.DeepEqual(actual, expected) {
@@ -527,6 +552,37 @@
 	}
 }
 
+func TestStaticLibDepReorderingWithShared(t *testing.T) {
+	ctx := testCc(t, `
+	cc_library {
+		name: "a",
+		static_libs: ["b", "c"],
+	}
+	cc_library {
+		name: "b",
+	}
+	cc_library {
+		name: "c",
+		shared_libs: ["b"],
+	}
+
+	`)
+
+	variant := "android_arm64_armv8-a_core_static"
+	moduleA := ctx.ModuleForTests("a", variant).Module().(*Module)
+	actual := moduleA.depsInLinkOrder
+	expected := getOutputPaths(ctx, variant, []string{"c", "b"})
+
+	if !reflect.DeepEqual(actual, expected) {
+		t.Errorf("staticDeps orderings did not account for shared libs"+
+			"\nactual:   %v"+
+			"\nexpected: %v",
+			actual,
+			expected,
+		)
+	}
+}
+
 var compilerFlagsTestCases = []struct {
 	in  string
 	out bool
diff --git a/cc/cmakelists.go b/cc/cmakelists.go
index 13a2e8e..9b32182 100644
--- a/cc/cmakelists.go
+++ b/cc/cmakelists.go
@@ -23,8 +23,6 @@
 	"path"
 	"path/filepath"
 	"strings"
-
-	"github.com/google/blueprint"
 )
 
 // This singleton generates CMakeLists.txt files. It does so for each blueprint Android.bp resulting in a cc.Module
@@ -35,7 +33,7 @@
 	android.RegisterSingletonType("cmakelists_generator", cMakeListsGeneratorSingleton)
 }
 
-func cMakeListsGeneratorSingleton() blueprint.Singleton {
+func cMakeListsGeneratorSingleton() android.Singleton {
 	return &cmakelistsGeneratorSingleton{}
 }
 
@@ -57,14 +55,14 @@
 // This is done to ease investigating bug reports.
 var outputDebugInfo = false
 
-func (c *cmakelistsGeneratorSingleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
+func (c *cmakelistsGeneratorSingleton) GenerateBuildActions(ctx android.SingletonContext) {
 	if getEnvVariable(envVariableGenerateCMakeLists, ctx) != envVariableTrue {
 		return
 	}
 
 	outputDebugInfo = (getEnvVariable(envVariableGenerateDebugInfo, ctx) == envVariableTrue)
 
-	ctx.VisitAllModules(func(module blueprint.Module) {
+	ctx.VisitAllModules(func(module android.Module) {
 		if ccModule, ok := module.(*Module); ok {
 			if compiledModule, ok := ccModule.compiler.(CompiledInterface); ok {
 				generateCLionProject(compiledModule, ctx, ccModule)
@@ -81,7 +79,7 @@
 	return
 }
 
-func getEnvVariable(name string, ctx blueprint.SingletonContext) string {
+func getEnvVariable(name string, ctx android.SingletonContext) string {
 	// Using android.Config.Getenv instead of os.getEnv to guarantee soong will
 	// re-run in case this environment variable changes.
 	return ctx.Config().(android.Config).Getenv(name)
@@ -116,7 +114,7 @@
 	return nil
 }
 
-func generateCLionProject(compiledModule CompiledInterface, ctx blueprint.SingletonContext, ccModule *Module) {
+func generateCLionProject(compiledModule CompiledInterface, ctx android.SingletonContext, ccModule *Module) {
 	srcs := compiledModule.Srcs()
 	if len(srcs) == 0 {
 		return
@@ -287,7 +285,7 @@
 	return flag
 }
 
-func parseCompilerParameters(params []string, ctx blueprint.SingletonContext, f *os.File) compilerParameters {
+func parseCompilerParameters(params []string, ctx android.SingletonContext, f *os.File) compilerParameters {
 	var compilerParameters = makeCompilerParameters()
 
 	for i, str := range params {
@@ -388,7 +386,7 @@
 	c1.flags = append(c1.flags, c2.flags...)
 }
 
-func evalVariable(ctx blueprint.SingletonContext, str string) (string, error) {
+func evalVariable(ctx android.SingletonContext, str string) (string, error) {
 	evaluated, err := ctx.Eval(pctx, str)
 	if err == nil {
 		return evaluated, nil
@@ -396,7 +394,7 @@
 	return "", err
 }
 
-func getCMakeListsForModule(module *Module, ctx blueprint.SingletonContext) string {
+func getCMakeListsForModule(module *Module, ctx android.SingletonContext) string {
 	return filepath.Join(getAndroidSrcRootDirectory(ctx),
 		cLionOutputProjectsDirectory,
 		path.Dir(ctx.BlueprintFile(module)),
@@ -406,7 +404,7 @@
 		cMakeListsFilename)
 }
 
-func getAndroidSrcRootDirectory(ctx blueprint.SingletonContext) string {
+func getAndroidSrcRootDirectory(ctx android.SingletonContext) string {
 	srcPath, _ := filepath.Abs(android.PathForSource(ctx).String())
 	return srcPath
 }
diff --git a/cc/config/global.go b/cc/config/global.go
index 4322436..8881b4b 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -216,14 +216,14 @@
 		[]string{"libnativehelper/include_deprecated"})
 
 	pctx.SourcePathVariable("ClangDefaultBase", ClangDefaultBase)
-	pctx.VariableFunc("ClangBase", func(config interface{}) (string, error) {
-		if override := config.(android.Config).Getenv("LLVM_PREBUILTS_BASE"); override != "" {
+	pctx.VariableFunc("ClangBase", func(config android.Config) (string, error) {
+		if override := config.Getenv("LLVM_PREBUILTS_BASE"); override != "" {
 			return override, nil
 		}
 		return "${ClangDefaultBase}", nil
 	})
-	pctx.VariableFunc("ClangVersion", func(config interface{}) (string, error) {
-		if override := config.(android.Config).Getenv("LLVM_PREBUILTS_VERSION"); override != "" {
+	pctx.VariableFunc("ClangVersion", func(config android.Config) (string, error) {
+		if override := config.Getenv("LLVM_PREBUILTS_VERSION"); override != "" {
 			return override, nil
 		}
 		return ClangDefaultVersion, nil
@@ -231,8 +231,8 @@
 	pctx.StaticVariable("ClangPath", "${ClangBase}/${HostPrebuiltTag}/${ClangVersion}")
 	pctx.StaticVariable("ClangBin", "${ClangPath}/bin")
 
-	pctx.VariableFunc("ClangShortVersion", func(config interface{}) (string, error) {
-		if override := config.(android.Config).Getenv("LLVM_RELEASE_VERSION"); override != "" {
+	pctx.VariableFunc("ClangShortVersion", func(config android.Config) (string, error) {
+		if override := config.Getenv("LLVM_RELEASE_VERSION"); override != "" {
 			return override, nil
 		}
 		return ClangDefaultShortVersion, nil
@@ -258,8 +258,8 @@
 			"frameworks/rs/script_api/include",
 		})
 
-	pctx.VariableFunc("CcWrapper", func(config interface{}) (string, error) {
-		if override := config.(android.Config).Getenv("CC_WRAPPER"); override != "" {
+	pctx.VariableFunc("CcWrapper", func(config android.Config) (string, error) {
+		if override := config.Getenv("CC_WRAPPER"); override != "" {
 			return override + " ", nil
 		}
 		return "", nil
diff --git a/cc/config/tidy.go b/cc/config/tidy.go
index a2fa5a2..76a5f9e 100644
--- a/cc/config/tidy.go
+++ b/cc/config/tidy.go
@@ -25,8 +25,8 @@
 	// Global tidy checks include only google*, performance*,
 	// and misc-macro-parentheses, but not google-readability*
 	// or google-runtime-references.
-	pctx.VariableFunc("TidyDefaultGlobalChecks", func(config interface{}) (string, error) {
-		if override := config.(android.Config).Getenv("DEFAULT_GLOBAL_TIDY_CHECKS"); override != "" {
+	pctx.VariableFunc("TidyDefaultGlobalChecks", func(config android.Config) (string, error) {
+		if override := config.Getenv("DEFAULT_GLOBAL_TIDY_CHECKS"); override != "" {
 			return override, nil
 		}
 		return strings.Join([]string{
@@ -41,8 +41,8 @@
 
 	// There are too many clang-tidy warnings in external and vendor projects.
 	// Enable only some google checks for these projects.
-	pctx.VariableFunc("TidyExternalVendorChecks", func(config interface{}) (string, error) {
-		if override := config.(android.Config).Getenv("DEFAULT_EXTERNAL_VENDOR_TIDY_CHECKS"); override != "" {
+	pctx.VariableFunc("TidyExternalVendorChecks", func(config android.Config) (string, error) {
+		if override := config.Getenv("DEFAULT_EXTERNAL_VENDOR_TIDY_CHECKS"); override != "" {
 			return override, nil
 		}
 		return strings.Join([]string{
diff --git a/cc/config/x86_darwin_host.go b/cc/config/x86_darwin_host.go
index 8d805c9..dbaa6fa 100644
--- a/cc/config/x86_darwin_host.go
+++ b/cc/config/x86_darwin_host.go
@@ -107,25 +107,25 @@
 )
 
 func init() {
-	pctx.VariableFunc("macSdkPath", func(config interface{}) (string, error) {
-		xcodeselect := config.(android.Config).HostSystemTool("xcode-select")
+	pctx.VariableFunc("macSdkPath", func(config android.Config) (string, error) {
+		xcodeselect := config.HostSystemTool("xcode-select")
 		bytes, err := exec.Command(xcodeselect, "--print-path").Output()
 		return strings.TrimSpace(string(bytes)), err
 	})
-	pctx.VariableFunc("macSdkRoot", func(config interface{}) (string, error) {
-		return xcrunSdk(config.(android.Config), "--show-sdk-path")
+	pctx.VariableFunc("macSdkRoot", func(config android.Config) (string, error) {
+		return xcrunSdk(config, "--show-sdk-path")
 	})
 	pctx.StaticVariable("macMinVersion", "10.8")
-	pctx.VariableFunc("MacArPath", func(config interface{}) (string, error) {
-		return xcrun(config.(android.Config), "--find", "ar")
+	pctx.VariableFunc("MacArPath", func(config android.Config) (string, error) {
+		return xcrun(config, "--find", "ar")
 	})
 
-	pctx.VariableFunc("MacStripPath", func(config interface{}) (string, error) {
-		return xcrun(config.(android.Config), "--find", "strip")
+	pctx.VariableFunc("MacStripPath", func(config android.Config) (string, error) {
+		return xcrun(config, "--find", "strip")
 	})
 
-	pctx.VariableFunc("MacToolPath", func(config interface{}) (string, error) {
-		path, err := xcrun(config.(android.Config), "--find", "ld")
+	pctx.VariableFunc("MacToolPath", func(config android.Config) (string, error) {
+		path, err := xcrun(config, "--find", "ld")
 		return filepath.Dir(path), err
 	})
 
diff --git a/cc/library.go b/cc/library.go
index 192496a..cf10617 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -85,9 +85,6 @@
 	VariantIsShared bool `blueprint:"mutated"`
 	// This variant is static
 	VariantIsStatic bool `blueprint:"mutated"`
-	// Location of the static library in the sysroot. Empty if the library is
-	// not included in the NDK.
-	NdkSysrootPath string `blueprint:"mutated"`
 }
 
 type FlagExporterProperties struct {
@@ -246,6 +243,10 @@
 	// Source Abi Diff
 	sAbiDiff android.OptionalPath
 
+	// Location of the static library in the sysroot. Empty if the library is
+	// not included in the NDK.
+	ndkSysrootPath android.Path
+
 	// Decorated interafaces
 	*baseCompiler
 	*baseLinker
@@ -742,7 +743,7 @@
 			Input:       file,
 		})
 
-		library.MutatedProperties.NdkSysrootPath = installPath.String()
+		library.ndkSysrootPath = installPath
 	}
 }
 
diff --git a/cc/ndk_headers.go b/cc/ndk_headers.go
index 0458fb6..d7c2a06 100644
--- a/cc/ndk_headers.go
+++ b/cc/ndk_headers.go
@@ -18,6 +18,7 @@
 	"fmt"
 	"os"
 	"path/filepath"
+	"strings"
 
 	"github.com/google/blueprint"
 
@@ -73,7 +74,7 @@
 
 	properties headerProperies
 
-	installPaths []string
+	installPaths android.Paths
 	licensePath  android.ModuleSrcPath
 }
 
@@ -119,6 +120,14 @@
 
 	m.licensePath = android.PathForModuleSrc(ctx, String(m.properties.License))
 
+	// When generating NDK prebuilts, skip installing MIPS headers,
+	// but keep them when doing regular platform build.
+	// Ndk_abis property is only set to true with build/soong/scripts/build-ndk-prebuilts.sh
+	// TODO: Revert this once MIPS is supported in NDK again.
+	if Bool(ctx.AConfig().Ndk_abis) && strings.Contains(ctx.ModuleName(), "mips") {
+		return
+	}
+
 	srcFiles := ctx.ExpandSources(m.properties.Srcs, nil)
 	for _, header := range srcFiles {
 		installDir := getHeaderInstallDir(ctx, header, String(m.properties.From),
@@ -130,7 +139,7 @@
 				"expected header install path (%q) not equal to actual install path %q",
 				installPath, installedPath))
 		}
-		m.installPaths = append(m.installPaths, installPath.String())
+		m.installPaths = append(m.installPaths, installPath)
 	}
 
 	if len(m.installPaths) == 0 {
@@ -177,7 +186,7 @@
 
 	properties preprocessedHeaderProperies
 
-	installPaths []string
+	installPaths android.Paths
 	licensePath  android.ModuleSrcPath
 }
 
@@ -199,7 +208,7 @@
 		installDir := getHeaderInstallDir(ctx, header, String(m.properties.From), String(m.properties.To))
 		installPath := installDir.Join(ctx, header.Base())
 		installPaths = append(installPaths, installPath)
-		m.installPaths = append(m.installPaths, installPath.String())
+		m.installPaths = append(m.installPaths, installPath)
 	}
 
 	if len(m.installPaths) == 0 {
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index 96a90fb..e69128c 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -98,7 +98,7 @@
 	properties libraryProperties
 
 	versionScriptPath android.ModuleGenPath
-	installPath       string
+	installPath       android.Path
 }
 
 // OMG GO
@@ -344,7 +344,7 @@
 
 	installDir := getNdkInstallBase(ctx).Join(ctx, fmt.Sprintf(
 		"platforms/android-%s/arch-%s/usr/%s", apiLevel, arch, libDir))
-	stub.installPath = ctx.InstallFile(installDir, path.Base(), path).String()
+	stub.installPath = ctx.InstallFile(installDir, path.Base(), path)
 }
 
 func newStubLibrary() *Module {
diff --git a/cc/ndk_sysroot.go b/cc/ndk_sysroot.go
index e213965..4324458 100644
--- a/cc/ndk_sysroot.go
+++ b/cc/ndk_sysroot.go
@@ -53,8 +53,6 @@
 // TODO(danalbert): Write `ndk_static_library` rule.
 
 import (
-	"github.com/google/blueprint"
-
 	"android/soong/android"
 )
 
@@ -76,32 +74,32 @@
 	return getNdkInstallBase(ctx).Join(ctx, "sysroot")
 }
 
-func getNdkSysrootTimestampFile(ctx android.PathContext) android.Path {
+func getNdkSysrootTimestampFile(ctx android.PathContext) android.WritablePath {
 	return android.PathForOutput(ctx, "ndk.timestamp")
 }
 
-func NdkSingleton() blueprint.Singleton {
+func NdkSingleton() android.Singleton {
 	return &ndkSingleton{}
 }
 
 type ndkSingleton struct{}
 
-func (n *ndkSingleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
-	installPaths := []string{}
-	licensePaths := []string{}
-	ctx.VisitAllModules(func(module blueprint.Module) {
+func (n *ndkSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+	var installPaths android.Paths
+	var licensePaths android.Paths
+	ctx.VisitAllModules(func(module android.Module) {
 		if m, ok := module.(android.Module); ok && !m.Enabled() {
 			return
 		}
 
 		if m, ok := module.(*headerModule); ok {
 			installPaths = append(installPaths, m.installPaths...)
-			licensePaths = append(licensePaths, m.licensePath.String())
+			licensePaths = append(licensePaths, m.licensePath)
 		}
 
 		if m, ok := module.(*preprocessedHeaderModule); ok {
 			installPaths = append(installPaths, m.installPaths...)
-			licensePaths = append(licensePaths, m.licensePath.String())
+			licensePaths = append(licensePaths, m.licensePath)
 		}
 
 		if m, ok := module.(*Module); ok {
@@ -110,30 +108,28 @@
 			}
 
 			if library, ok := m.linker.(*libraryDecorator); ok {
-				if library.MutatedProperties.NdkSysrootPath != "" {
-					installPaths = append(installPaths, library.MutatedProperties.NdkSysrootPath)
+				if library.ndkSysrootPath != nil {
+					installPaths = append(installPaths, library.ndkSysrootPath)
 				}
 			}
 		}
 	})
 
 	combinedLicense := getNdkInstallBase(ctx).Join(ctx, "NOTICE")
-	ctx.Build(pctx, blueprint.BuildParams{
+	ctx.Build(pctx, android.BuildParams{
 		Rule:        android.Cat,
 		Description: "combine licenses",
-		Outputs:     []string{combinedLicense.String()},
+		Output:      combinedLicense,
 		Inputs:      licensePaths,
-		Optional:    true,
 	})
 
-	depPaths := append(installPaths, combinedLicense.String())
+	depPaths := append(installPaths, combinedLicense)
 
 	// There's a dummy "ndk" rule defined in ndk/Android.mk that depends on
 	// this. `m ndk` will build the sysroots.
-	ctx.Build(pctx, blueprint.BuildParams{
+	ctx.Build(pctx, android.BuildParams{
 		Rule:      android.Touch,
-		Outputs:   []string{getNdkSysrootTimestampFile(ctx).String()},
+		Output:    getNdkSysrootTimestampFile(ctx),
 		Implicits: depPaths,
-		Optional:  true,
 	})
 }
diff --git a/cc/sanitize.go b/cc/sanitize.go
index fcea4ef..bf58d0f 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -17,6 +17,7 @@
 import (
 	"fmt"
 	"io"
+	"sort"
 	"strings"
 	"sync"
 
@@ -649,5 +650,6 @@
 
 func cfiMakeVarsProvider(ctx android.MakeVarsContext) {
 	cfiStaticLibs := cfiStaticLibs(ctx.Config())
+	sort.Strings(*cfiStaticLibs)
 	ctx.Strict("SOONG_CFI_STATIC_LIBRARIES", strings.Join(*cfiStaticLibs, " "))
 }
diff --git a/cmd/merge_zips/merge_zips.go b/cmd/merge_zips/merge_zips.go
index 207b161..556dad0 100644
--- a/cmd/merge_zips/merge_zips.go
+++ b/cmd/merge_zips/merge_zips.go
@@ -54,13 +54,14 @@
 }
 
 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       fileList
-	stripFiles      fileList
-	zipsToNotStrip  = make(zipsToNotStripSet)
-	stripDirEntries = flag.Bool("D", false, "strip directory entries from the output zip file")
-	manifest        = flag.String("m", "", "manifest file to insert in jar")
+	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        fileList
+	stripFiles       fileList
+	zipsToNotStrip   = make(zipsToNotStripSet)
+	stripDirEntries  = flag.Bool("D", false, "strip directory entries from the output zip file")
+	manifest         = flag.String("m", "", "manifest file to insert in jar")
+	ignoreDuplicates = flag.Bool("ignore-duplicates", false, "take each entry from the first zip it exists in and don't warn")
 )
 
 func init() {
@@ -118,7 +119,7 @@
 	}
 
 	// do merge
-	err = mergeZips(readers, writer, *manifest, *sortEntries, *emulateJar, *stripDirEntries)
+	err = mergeZips(readers, writer, *manifest, *sortEntries, *emulateJar, *stripDirEntries, *ignoreDuplicates)
 	if err != nil {
 		log.Fatal(err)
 	}
@@ -210,7 +211,7 @@
 }
 
 func mergeZips(readers []namedZipReader, writer *zip.Writer, manifest string,
-	sortEntries, emulateJar, stripDirEntries bool) error {
+	sortEntries, emulateJar, stripDirEntries, ignoreDuplicates bool) error {
 
 	sourceByDest := make(map[string]zipSource, 0)
 	orderedMappings := []fileMapping{}
@@ -266,6 +267,9 @@
 					return fmt.Errorf("Directory/file mismatch at %v from %v and %v\n",
 						dest, existingSource, source)
 				}
+				if ignoreDuplicates {
+					continue
+				}
 				if emulateJar &&
 					file.Name == jar.ManifestFile || file.Name == jar.ModuleInfoClass {
 					// Skip manifest and module info files that are not from the first input file
diff --git a/cmd/zip2zip/Android.bp b/cmd/zip2zip/Android.bp
index 6420219..68d8bc7 100644
--- a/cmd/zip2zip/Android.bp
+++ b/cmd/zip2zip/Android.bp
@@ -15,8 +15,9 @@
 blueprint_go_binary {
     name: "zip2zip",
     deps: [
-      "android-archive-zip",
-      "soong-jar",
+        "android-archive-zip",
+        "blueprint-pathtools",
+        "soong-jar",
     ],
     srcs: [
         "zip2zip.go",
diff --git a/cmd/zip2zip/zip2zip.go b/cmd/zip2zip/zip2zip.go
index f48d458..e8ea9b9 100644
--- a/cmd/zip2zip/zip2zip.go
+++ b/cmd/zip2zip/zip2zip.go
@@ -24,6 +24,8 @@
 	"strings"
 	"time"
 
+	"github.com/google/blueprint/pathtools"
+
 	"android/soong/jar"
 	"android/soong/third_party/zip"
 )
@@ -36,8 +38,14 @@
 	setTime   = flag.Bool("t", false, "set timestamps to 2009-01-01 00:00:00")
 
 	staticTime = time.Date(2009, 1, 1, 0, 0, 0, 0, time.UTC)
+
+	excludes excludeArgs
 )
 
+func init() {
+	flag.Var(&excludes, "x", "exclude a filespec from the output")
+}
+
 func main() {
 	flag.Usage = func() {
 		fmt.Fprintln(os.Stderr, "usage: zip2zip -i zipfile -o zipfile [-s|-j] [-t] [filespec]...")
@@ -45,15 +53,14 @@
 		fmt.Fprintln(os.Stderr, "  filespec:")
 		fmt.Fprintln(os.Stderr, "    <name>")
 		fmt.Fprintln(os.Stderr, "    <in_name>:<out_name>")
-		fmt.Fprintln(os.Stderr, "    <glob>:<out_dir>/")
+		fmt.Fprintln(os.Stderr, "    <glob>[:<out_dir>]")
 		fmt.Fprintln(os.Stderr, "")
-		fmt.Fprintln(os.Stderr, "<glob> uses the rules at https://golang.org/pkg/path/filepath/#Match")
-		fmt.Fprintln(os.Stderr, "As a special exception, '**' is supported to specify all files in the input zip.")
+		fmt.Fprintln(os.Stderr, "<glob> uses the rules at https://godoc.org/github.com/google/blueprint/pathtools/#Match")
 		fmt.Fprintln(os.Stderr, "")
 		fmt.Fprintln(os.Stderr, "Files will be copied with their existing compression from the input zipfile to")
 		fmt.Fprintln(os.Stderr, "the output zipfile, in the order of filespec arguments.")
 		fmt.Fprintln(os.Stderr, "")
-		fmt.Fprintln(os.Stderr, "If no filepsec is provided all files are copied (equivalent to '**').")
+		fmt.Fprintln(os.Stderr, "If no filepsec is provided all files and directories are copied.")
 	}
 
 	flag.Parse()
@@ -85,7 +92,9 @@
 		}
 	}()
 
-	if err := zip2zip(&reader.Reader, writer, *sortGlobs, *sortJava, *setTime, flag.Args()); err != nil {
+	if err := zip2zip(&reader.Reader, writer, *sortGlobs, *sortJava, *setTime,
+		flag.Args(), excludes); err != nil {
+
 		log.Fatal(err)
 	}
 }
@@ -95,91 +104,126 @@
 	newName string
 }
 
-func zip2zip(reader *zip.Reader, writer *zip.Writer, sortGlobs, sortJava, setTime bool, args []string) error {
-	if len(args) == 0 {
-		// If no filespec is provided, default to copying everything
-		args = []string{"**"}
-	}
-	for _, arg := range args {
-		var input string
-		var output string
+func zip2zip(reader *zip.Reader, writer *zip.Writer, sortOutput, sortJava, setTime bool,
+	includes []string, excludes []string) error {
 
+	matches := []pair{}
+
+	sortMatches := func(matches []pair) {
+		if sortJava {
+			sort.SliceStable(matches, func(i, j int) bool {
+				return jar.EntryNamesLess(matches[i].newName, matches[j].newName)
+			})
+		} else if sortOutput {
+			sort.SliceStable(matches, func(i, j int) bool {
+				return matches[i].newName < matches[j].newName
+			})
+		}
+	}
+
+	for _, include := range includes {
 		// Reserve escaping for future implementation, so make sure no
 		// one is using \ and expecting a certain behavior.
-		if strings.Contains(arg, "\\") {
+		if strings.Contains(include, "\\") {
 			return fmt.Errorf("\\ characters are not currently supported")
 		}
 
-		args := strings.SplitN(arg, ":", 2)
-		input = args[0]
-		if len(args) == 2 {
-			output = args[1]
-		}
+		input, output := includeSplit(include)
 
-		matches := []pair{}
-		if strings.IndexAny(input, "*?[") >= 0 {
-			matchAll := input == "**"
-			if !matchAll && strings.Contains(input, "**") {
-				return fmt.Errorf("** is only supported on its own, not with other characters")
-			}
+		var includeMatches []pair
 
-			for _, file := range reader.File {
-				match := matchAll
-
-				if !match {
-					var err error
-					match, err = filepath.Match(input, file.Name)
-					if err != nil {
-						return err
-					}
-				}
-
-				if match {
-					var newName string
-					if output == "" {
-						newName = file.Name
-					} else {
+		for _, file := range reader.File {
+			var newName string
+			if match, err := pathtools.Match(input, file.Name); err != nil {
+				return err
+			} else if match {
+				if output == "" {
+					newName = file.Name
+				} else {
+					if pathtools.IsGlob(input) {
+						// If the input is a glob then the output is a directory.
 						_, name := filepath.Split(file.Name)
 						newName = filepath.Join(output, name)
+					} else {
+						// Otherwise it is a file.
+						newName = output
 					}
-					matches = append(matches, pair{file, newName})
 				}
-			}
-
-			if sortJava {
-				jarSort(matches)
-			} else if sortGlobs {
-				sort.SliceStable(matches, func(i, j int) bool {
-					return matches[i].newName < matches[j].newName
-				})
-			}
-		} else {
-			if output == "" {
-				output = input
-			}
-			for _, file := range reader.File {
-				if input == file.Name {
-					matches = append(matches, pair{file, output})
-					break
-				}
+				includeMatches = append(includeMatches, pair{file, newName})
 			}
 		}
 
-		for _, match := range matches {
-			if setTime {
-				match.File.SetModTime(staticTime)
-			}
-			if err := writer.CopyFrom(match.File, match.newName); err != nil {
+		sortMatches(includeMatches)
+		matches = append(matches, includeMatches...)
+	}
+
+	if len(includes) == 0 {
+		// implicitly match everything
+		for _, file := range reader.File {
+			matches = append(matches, pair{file, file.Name})
+		}
+		sortMatches(matches)
+	}
+
+	var matchesAfterExcludes []pair
+	seen := make(map[string]*zip.File)
+
+	for _, match := range matches {
+		// Filter out matches whose original file name matches an exclude filter
+		excluded := false
+		for _, exclude := range excludes {
+			if excludeMatch, err := pathtools.Match(exclude, match.File.Name); err != nil {
 				return err
+			} else if excludeMatch {
+				excluded = true
+				break
 			}
 		}
+
+		if excluded {
+			continue
+		}
+
+		// Check for duplicate output names, ignoring ones that come from the same input zip entry.
+		if prev, exists := seen[match.newName]; exists {
+			if prev != match.File {
+				return fmt.Errorf("multiple entries for %q with different contents", match.newName)
+			}
+			continue
+		}
+		seen[match.newName] = match.File
+
+		matchesAfterExcludes = append(matchesAfterExcludes, match)
+	}
+
+	for _, match := range matchesAfterExcludes {
+		if setTime {
+			match.File.SetModTime(staticTime)
+		}
+		if err := writer.CopyFrom(match.File, match.newName); err != nil {
+			return err
+		}
 	}
 
 	return nil
 }
 
-func jarSort(files []pair) {
-	sort.SliceStable(files, func(i, j int) bool {
-		return jar.EntryNamesLess(files[i].newName, files[j].newName)
-	})
+func includeSplit(s string) (string, string) {
+	split := strings.SplitN(s, ":", 2)
+	if len(split) == 2 {
+		return split[0], split[1]
+	} else {
+		return split[0], ""
+	}
+}
+
+type excludeArgs []string
+
+func (e *excludeArgs) String() string {
+	return strings.Join(*e, " ")
+}
+
+func (e *excludeArgs) Set(s string) error {
+	*e = append(*e, s)
+	return nil
 }
diff --git a/cmd/zip2zip/zip2zip_test.go b/cmd/zip2zip/zip2zip_test.go
index 53c8ce2..212ab28 100644
--- a/cmd/zip2zip/zip2zip_test.go
+++ b/cmd/zip2zip/zip2zip_test.go
@@ -30,6 +30,7 @@
 	sortGlobs  bool
 	sortJava   bool
 	args       []string
+	excludes   []string
 
 	outputFiles []string
 	err         error
@@ -41,13 +42,6 @@
 
 		err: fmt.Errorf("\\ characters are not currently supported"),
 	},
-	{
-		name: "unsupported **",
-
-		args: []string{"a/**:b"},
-
-		err: fmt.Errorf("** is only supported on its own, not with other characters"),
-	},
 	{ // This is modelled after the update package build rules in build/make/core/Makefile
 		name: "filter globs",
 
@@ -95,16 +89,19 @@
 		name: "sort all",
 
 		inputFiles: []string{
+			"RADIO/",
 			"RADIO/a",
+			"IMAGES/",
 			"IMAGES/system.img",
 			"IMAGES/b.txt",
 			"IMAGES/recovery.img",
 			"IMAGES/vendor.img",
+			"OTA/",
 			"OTA/b",
 			"OTA/android-info.txt",
 		},
 		sortGlobs: true,
-		args:      []string{"**"},
+		args:      []string{"**/*"},
 
 		outputFiles: []string{
 			"IMAGES/b.txt",
@@ -120,11 +117,14 @@
 		name: "sort all implicit",
 
 		inputFiles: []string{
+			"RADIO/",
 			"RADIO/a",
+			"IMAGES/",
 			"IMAGES/system.img",
 			"IMAGES/b.txt",
 			"IMAGES/recovery.img",
 			"IMAGES/vendor.img",
+			"OTA/",
 			"OTA/b",
 			"OTA/android-info.txt",
 		},
@@ -132,12 +132,15 @@
 		args:      nil,
 
 		outputFiles: []string{
+			"IMAGES/",
 			"IMAGES/b.txt",
 			"IMAGES/recovery.img",
 			"IMAGES/system.img",
 			"IMAGES/vendor.img",
+			"OTA/",
 			"OTA/android-info.txt",
 			"OTA/b",
+			"RADIO/",
 			"RADIO/a",
 		},
 	},
@@ -177,7 +180,7 @@
 			"b",
 			"a",
 		},
-		args: []string{"a:a2", "**"},
+		args: []string{"a:a2", "**/*"},
 
 		outputFiles: []string{
 			"a2",
@@ -185,6 +188,69 @@
 			"a",
 		},
 	},
+	{
+		name: "multiple matches",
+
+		inputFiles: []string{
+			"a/a",
+		},
+		args: []string{"a/a", "a/*"},
+
+		outputFiles: []string{
+			"a/a",
+		},
+	},
+	{
+		name: "multiple conflicting matches",
+
+		inputFiles: []string{
+			"a/a",
+			"a/b",
+		},
+		args: []string{"a/b:a/a", "a/*"},
+
+		err: fmt.Errorf(`multiple entries for "a/a" with different contents`),
+	},
+	{
+		name: "excludes",
+
+		inputFiles: []string{
+			"a/a",
+			"a/b",
+		},
+		args:     nil,
+		excludes: []string{"a/a"},
+
+		outputFiles: []string{
+			"a/b",
+		},
+	},
+	{
+		name: "excludes with include",
+
+		inputFiles: []string{
+			"a/a",
+			"a/b",
+		},
+		args:     []string{"a/*"},
+		excludes: []string{"a/a"},
+
+		outputFiles: []string{
+			"a/b",
+		},
+	},
+	{
+		name: "excludes with glob",
+
+		inputFiles: []string{
+			"a/a",
+			"a/b",
+		},
+		args:     []string{"a/*"},
+		excludes: []string{"a/*"},
+
+		outputFiles: nil,
+	},
 }
 
 func errorString(e error) string {
@@ -216,7 +282,7 @@
 			}
 
 			outputWriter := zip.NewWriter(outputBuf)
-			err = zip2zip(inputReader, outputWriter, testCase.sortGlobs, testCase.sortJava, false, testCase.args)
+			err = zip2zip(inputReader, outputWriter, testCase.sortGlobs, testCase.sortJava, false, testCase.args, testCase.excludes)
 			if errorString(testCase.err) != errorString(err) {
 				t.Fatalf("Unexpected error:\n got: %q\nwant: %q", errorString(err), errorString(testCase.err))
 			}
diff --git a/java/aapt2.go b/java/aapt2.go
new file mode 100644
index 0000000..ce307fe
--- /dev/null
+++ b/java/aapt2.go
@@ -0,0 +1,162 @@
+// 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 (
+	"path/filepath"
+	"strconv"
+	"strings"
+
+	"github.com/google/blueprint"
+
+	"android/soong/android"
+)
+
+const AAPT2_SHARD_SIZE = 100
+
+// Convert input resource file path to output file path.
+// values-[config]/<file>.xml -> values-[config]_<file>.arsc.flat;
+// For other resource file, just replace the last "/" with "_" and
+// add .flat extension.
+func pathToAapt2Path(ctx android.ModuleContext, res android.Path) android.WritablePath {
+
+	name := res.Base()
+	subDir := filepath.Dir(res.String())
+	subDir, lastDir := filepath.Split(subDir)
+	if strings.HasPrefix(lastDir, "values") {
+		name = strings.TrimSuffix(name, ".xml") + ".arsc"
+	}
+	name = lastDir + "_" + name + ".flat"
+	return android.PathForModuleOut(ctx, "aapt2", subDir, name)
+}
+
+func pathsToAapt2Paths(ctx android.ModuleContext, resPaths android.Paths) android.WritablePaths {
+	outPaths := make(android.WritablePaths, len(resPaths))
+
+	for i, res := range resPaths {
+		outPaths[i] = pathToAapt2Path(ctx, res)
+	}
+
+	return outPaths
+}
+
+var aapt2CompileRule = pctx.AndroidStaticRule("aapt2Compile",
+	blueprint.RuleParams{
+		Command:     `${config.Aapt2Cmd compile -o $outDir $cFlags --legacy $in`,
+		CommandDeps: []string{"${config.Aapt2Cmd}"},
+	},
+	"outDir", "cFlags")
+
+func aapt2Compile(ctx android.ModuleContext, dir android.Path, paths android.Paths) android.WritablePaths {
+	shards := shardPaths(paths, AAPT2_SHARD_SIZE)
+
+	ret := make(android.WritablePaths, 0, len(paths))
+
+	for i, shard := range shards {
+		outPaths := pathsToAapt2Paths(ctx, shard)
+		ret = append(ret, outPaths...)
+
+		shardDesc := ""
+		if i != 0 {
+			shardDesc = " " + strconv.Itoa(i+1)
+		}
+
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        aapt2CompileRule,
+			Description: "aapt2 compile " + dir.String() + shardDesc,
+			Inputs:      shard,
+			Outputs:     outPaths,
+			Args: map[string]string{
+				"outDir": android.PathForModuleOut(ctx, "aapt2", dir.String()).String(),
+				"cFlags": "--pseudo-localize",
+			},
+		})
+	}
+
+	return ret
+}
+
+var aapt2LinkRule = pctx.AndroidStaticRule("aapt2Link",
+	blueprint.RuleParams{
+		Command: `$aapt2Cmd link -o $out $flags --java $genDir --proguard $proguardOptions $inFlags && ` +
+			`${config.SoongZipCmd} -write_if_changed -jar -o $genJar -C $genDir -D $genDir`,
+		CommandDeps: []string{
+			"$aapt2Cmd",
+			"${config.SoongZipCmd}",
+		},
+		Restat: true,
+	},
+	"flags", "inFlags", "proguardOptions", "genDir", "genJar")
+
+var fileListToFileRule = pctx.AndroidStaticRule("fileListToFile",
+	blueprint.RuleParams{
+		Command:        `cp $out.rsp $out`,
+		Rspfile:        "$out.rsp",
+		RspfileContent: "$in",
+	})
+
+func aapt2Link(ctx android.ModuleContext,
+	packageRes, genJar, proguardOptions android.WritablePath,
+	flags []string, deps android.Paths,
+	compiledRes, compiledOverlay android.Paths) {
+
+	genDir := android.PathForModuleGen(ctx, "aapt2", "R")
+
+	var inFlags []string
+
+	if len(compiledRes) > 0 {
+		resFileList := android.PathForModuleOut(ctx, "aapt2", "res.list")
+		// Write out file lists to files
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        fileListToFileRule,
+			Description: "resource file list",
+			Inputs:      compiledRes,
+			Output:      resFileList,
+		})
+
+		deps = append(deps, compiledRes...)
+		deps = append(deps, resFileList)
+		inFlags = append(inFlags, "@"+resFileList.String())
+	}
+
+	if len(compiledOverlay) > 0 {
+		overlayFileList := android.PathForModuleOut(ctx, "aapt2", "overlay.list")
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        fileListToFileRule,
+			Description: "overlay resource file list",
+			Inputs:      compiledOverlay,
+			Output:      overlayFileList,
+		})
+
+		deps = append(deps, compiledOverlay...)
+		deps = append(deps, overlayFileList)
+		inFlags = append(inFlags, "-R", "@"+overlayFileList.String())
+	}
+
+	ctx.Build(pctx, android.BuildParams{
+		Rule:            aapt2LinkRule,
+		Description:     "aapt2 link",
+		Implicits:       deps,
+		Output:          packageRes,
+		ImplicitOutputs: android.WritablePaths{proguardOptions, genJar},
+		Args: map[string]string{
+			"flags":           strings.Join(flags, " "),
+			"inFlags":         strings.Join(inFlags, " "),
+			"proguardOptions": proguardOptions.String(),
+			"genDir":          genDir.String(),
+			"genJar":          genJar.String(),
+		},
+	})
+}
diff --git a/java/androidmk.go b/java/androidmk.go
index 1c0526a..df83faa 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -36,12 +36,16 @@
 				}
 				if library.dexJarFile != nil {
 					fmt.Fprintln(w, "LOCAL_SOONG_DEX_JAR :=", library.dexJarFile.String())
-					if library.deviceProperties.Dex_preopt == nil || *library.deviceProperties.Dex_preopt == false {
+					if library.deviceProperties.Dex_preopt != nil && *library.deviceProperties.Dex_preopt == false {
 						fmt.Fprintln(w, "LOCAL_DEX_PREOPT := false")
 					}
 				}
 				fmt.Fprintln(w, "LOCAL_SDK_VERSION :=", String(library.deviceProperties.Sdk_version))
 				fmt.Fprintln(w, "LOCAL_SOONG_HEADER_JAR :=", library.headerJarFile.String())
+
+				if library.jacocoReportClassesFile != nil {
+					fmt.Fprintln(w, "LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR :=", library.jacocoReportClassesFile.String())
+				}
 			},
 		},
 		Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
diff --git a/java/app.go b/java/app.go
index 05cc975..fd1fe33 100644
--- a/java/app.go
+++ b/java/app.go
@@ -25,6 +25,10 @@
 	"android/soong/android"
 )
 
+func init() {
+	android.RegisterPreSingletonType("overlay", OverlaySingletonFactory)
+}
+
 // AAR prebuilts
 // AndroidManifest.xml merging
 // package splits
@@ -54,6 +58,8 @@
 	// list of directories relative to the Blueprints file containing
 	// Android resources
 	Resource_dirs []string
+
+	Instrumentation_for *string
 }
 
 type AndroidApp struct {
@@ -61,14 +67,14 @@
 
 	appProperties androidAppProperties
 
-	aaptJavaFileList android.Path
-	exportPackage    android.Path
+	aaptSrcJar    android.Path
+	exportPackage android.Path
 }
 
 func (a *AndroidApp) DepsMutator(ctx android.BottomUpMutatorContext) {
 	a.Module.deps(ctx)
 
-	if !proptools.Bool(a.properties.No_standard_libs) {
+	if !Bool(a.properties.No_framework_libs) && !Bool(a.properties.No_standard_libs) {
 		switch String(a.deviceProperties.Sdk_version) { // TODO: Res_sdk_version?
 		case "current", "system_current", "":
 			ctx.AddDependency(ctx.Module(), frameworkResTag, "framework-res")
@@ -79,38 +85,29 @@
 }
 
 func (a *AndroidApp) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	aaptFlags, aaptDeps, hasResources := a.aaptFlags(ctx)
+	linkFlags, linkDeps, resDirs, overlayDirs := a.aapt2Flags(ctx)
 
-	if hasResources {
-		// First generate R.java so we can build the .class files
-		aaptRJavaFlags := append([]string(nil), aaptFlags...)
+	packageRes := android.PathForModuleOut(ctx, "package-res.apk")
+	srcJar := android.PathForModuleGen(ctx, "R.jar")
+	proguardOptionsFile := android.PathForModuleGen(ctx, "proguard.options")
 
-		publicResourcesFile, proguardOptionsFile, aaptJavaFileList :=
-			CreateResourceJavaFiles(ctx, aaptRJavaFlags, aaptDeps)
-		a.aaptJavaFileList = aaptJavaFileList
-		// TODO(ccross):  export aapt generated java files as a src jar
-
-		if Bool(a.appProperties.Export_package_resources) {
-			aaptPackageFlags := append([]string(nil), aaptFlags...)
-			var hasProduct bool
-			for _, f := range aaptPackageFlags {
-				if strings.HasPrefix(f, "--product") {
-					hasProduct = true
-					break
-				}
-			}
-
-			if !hasProduct {
-				aaptPackageFlags = append(aaptPackageFlags,
-					"--product "+ctx.AConfig().ProductAAPTCharacteristics())
-			}
-			a.exportPackage = CreateExportPackage(ctx, aaptPackageFlags, aaptDeps)
-			ctx.CheckbuildFile(a.exportPackage)
-		}
-		ctx.CheckbuildFile(publicResourcesFile)
-		ctx.CheckbuildFile(proguardOptionsFile)
-		ctx.CheckbuildFile(aaptJavaFileList)
+	var compiledRes, compiledOverlay android.Paths
+	for _, dir := range resDirs {
+		compiledRes = append(compiledRes, aapt2Compile(ctx, dir.dir, dir.files).Paths()...)
 	}
+	for _, dir := range overlayDirs {
+		compiledOverlay = append(compiledOverlay, aapt2Compile(ctx, dir.dir, dir.files).Paths()...)
+	}
+
+	aapt2Link(ctx, packageRes, srcJar, proguardOptionsFile,
+		linkFlags, linkDeps, compiledRes, compiledOverlay)
+
+	a.exportPackage = packageRes
+	a.aaptSrcJar = srcJar
+
+	ctx.CheckbuildFile(proguardOptionsFile)
+	ctx.CheckbuildFile(a.exportPackage)
+	ctx.CheckbuildFile(a.aaptSrcJar)
 
 	// apps manifests are handled by aapt, don't let Module see them
 	a.properties.Manifest = nil
@@ -121,20 +118,6 @@
 
 	a.Module.compile(ctx)
 
-	aaptPackageFlags := append([]string(nil), aaptFlags...)
-	var hasProduct bool
-	for _, f := range aaptPackageFlags {
-		if strings.HasPrefix(f, "--product") {
-			hasProduct = true
-			break
-		}
-	}
-
-	if !hasProduct {
-		aaptPackageFlags = append(aaptPackageFlags,
-			"--product "+ctx.AConfig().ProductAAPTCharacteristics())
-	}
-
 	certificate := String(a.appProperties.Certificate)
 	if certificate == "" {
 		certificate = ctx.AConfig().DefaultAppCertificate(ctx).String()
@@ -149,7 +132,12 @@
 		certificates = append(certificates, filepath.Join(android.PathForSource(ctx).String(), c))
 	}
 
-	a.outputFile = CreateAppPackage(ctx, aaptPackageFlags, a.outputFile, certificates)
+	packageFile := android.PathForModuleOut(ctx, "package.apk")
+
+	CreateAppPackage(ctx, packageFile, a.exportPackage, a.outputFile, certificates)
+
+	a.outputFile = packageFile
+
 	ctx.InstallFile(android.PathForModuleInstall(ctx, "app"), ctx.ModuleName()+".apk", a.outputFile)
 }
 
@@ -165,57 +153,55 @@
 	"*~",
 }
 
-func (a *AndroidApp) aaptFlags(ctx android.ModuleContext) ([]string, android.Paths, bool) {
-	aaptFlags := a.appProperties.Aaptflags
+type globbedResourceDir struct {
+	dir   android.Path
+	files android.Paths
+}
+
+func (a *AndroidApp) aapt2Flags(ctx android.ModuleContext) (flags []string, deps android.Paths,
+	resDirs, overlayDirs []globbedResourceDir) {
+
 	hasVersionCode := false
 	hasVersionName := false
-	for _, f := range aaptFlags {
+	hasProduct := false
+	for _, f := range a.appProperties.Aaptflags {
 		if strings.HasPrefix(f, "--version-code") {
 			hasVersionCode = true
 		} else if strings.HasPrefix(f, "--version-name") {
 			hasVersionName = true
+		} else if strings.HasPrefix(f, "--product") {
+			hasProduct = true
 		}
 	}
 
-	if true /* is not a test */ {
-		aaptFlags = append(aaptFlags, "-z")
-	}
+	var linkFlags []string
 
+	// Flags specified in Android.bp
+	linkFlags = append(linkFlags, a.appProperties.Aaptflags...)
+
+	linkFlags = append(linkFlags, "--no-static-lib-packages")
+
+	// Find implicit or explicit asset and resource dirs
 	assetDirs := android.PathsWithOptionalDefaultForModuleSrc(ctx, a.appProperties.Asset_dirs, "assets")
 	resourceDirs := android.PathsWithOptionalDefaultForModuleSrc(ctx, a.appProperties.Resource_dirs, "res")
 
-	var overlayResourceDirs android.Paths
-	// For every resource directory, check if there is an overlay directory with the same path.
-	// If found, it will be prepended to the list of resource directories.
-	for _, overlayDir := range ctx.AConfig().ResourceOverlays() {
-		for _, resourceDir := range resourceDirs {
-			overlay := overlayDir.OverlayPath(ctx, resourceDir)
-			if overlay.Valid() {
-				overlayResourceDirs = append(overlayResourceDirs, overlay.Path())
-			}
-		}
+	var linkDeps android.Paths
+
+	// Glob directories into lists of paths
+	for _, dir := range resourceDirs {
+		resDirs = append(resDirs, globbedResourceDir{
+			dir:   dir,
+			files: resourceGlob(ctx, dir),
+		})
+		overlayDirs = append(overlayDirs, overlayResourceGlob(ctx, dir)...)
 	}
 
-	if len(overlayResourceDirs) > 0 {
-		resourceDirs = append(overlayResourceDirs, resourceDirs...)
+	var assetFiles android.Paths
+	for _, dir := range assetDirs {
+		assetFiles = append(assetFiles, resourceGlob(ctx, dir)...)
 	}
 
-	// aapt needs to rerun if any files are added or modified in the assets or resource directories,
-	// use glob to create a filelist.
-	var aaptDeps android.Paths
-	var hasResources bool
-	for _, d := range resourceDirs {
-		newDeps := ctx.Glob(filepath.Join(d.String(), "**/*"), aaptIgnoreFilenames)
-		aaptDeps = append(aaptDeps, newDeps...)
-		if len(newDeps) > 0 {
-			hasResources = true
-		}
-	}
-	for _, d := range assetDirs {
-		newDeps := ctx.Glob(filepath.Join(d.String(), "**/*"), aaptIgnoreFilenames)
-		aaptDeps = append(aaptDeps, newDeps...)
-	}
-
+	// App manifest file
 	var manifestFile string
 	if a.properties.Manifest == nil {
 		manifestFile = "AndroidManifest.xml"
@@ -224,50 +210,73 @@
 	}
 
 	manifestPath := android.PathForModuleSrc(ctx, manifestFile)
-	aaptDeps = append(aaptDeps, manifestPath)
+	linkFlags = append(linkFlags, "--manifest "+manifestPath.String())
+	linkDeps = append(linkDeps, manifestPath)
 
-	aaptFlags = append(aaptFlags, "-M "+manifestPath.String())
-	aaptFlags = append(aaptFlags, android.JoinWithPrefix(assetDirs.Strings(), "-A "))
-	aaptFlags = append(aaptFlags, android.JoinWithPrefix(resourceDirs.Strings(), "-S "))
+	linkFlags = append(linkFlags, android.JoinWithPrefix(assetDirs.Strings(), "-A "))
+	linkDeps = append(linkDeps, assetFiles...)
 
+	// Include dirs
 	ctx.VisitDirectDeps(func(module android.Module) {
 		var depFiles android.Paths
 		if javaDep, ok := module.(Dependency); ok {
+			// TODO: shared android libraries
 			if ctx.OtherModuleName(module) == "framework-res" {
 				depFiles = android.Paths{javaDep.(*AndroidApp).exportPackage}
 			}
 		}
 
 		for _, dep := range depFiles {
-			aaptFlags = append(aaptFlags, "-I "+dep.String())
+			linkFlags = append(linkFlags, "-I "+dep.String())
 		}
-		aaptDeps = append(aaptDeps, depFiles...)
+		linkDeps = append(linkDeps, depFiles...)
 	})
 
+	// SDK version flags
 	sdkVersion := String(a.deviceProperties.Sdk_version)
-	if sdkVersion == "" {
-		sdkVersion = ctx.AConfig().PlatformSdkVersion()
+	switch sdkVersion {
+	case "", "current", "system_current", "test_current":
+		sdkVersion = ctx.AConfig().AppsDefaultVersionName()
 	}
 
-	aaptFlags = append(aaptFlags, "--min-sdk-version "+sdkVersion)
-	aaptFlags = append(aaptFlags, "--target-sdk-version "+sdkVersion)
+	linkFlags = append(linkFlags, "--min-sdk-version "+sdkVersion)
+	linkFlags = append(linkFlags, "--target-sdk-version "+sdkVersion)
 
+	// Product characteristics
+	if !hasProduct && len(ctx.AConfig().ProductAAPTCharacteristics()) > 0 {
+		linkFlags = append(linkFlags, "--product", ctx.AConfig().ProductAAPTCharacteristics())
+	}
+
+	// Product AAPT config
+	for _, aaptConfig := range ctx.AConfig().ProductAAPTConfig() {
+		linkFlags = append(linkFlags, "-c", aaptConfig)
+	}
+
+	// Product AAPT preferred config
+	if len(ctx.AConfig().ProductAAPTPreferredConfig()) > 0 {
+		linkFlags = append(linkFlags, "--preferred-density", ctx.AConfig().ProductAAPTPreferredConfig())
+	}
+
+	// Version code
 	if !hasVersionCode {
-		aaptFlags = append(aaptFlags, "--version-code "+ctx.AConfig().PlatformSdkVersion())
+		linkFlags = append(linkFlags, "--version-code", ctx.AConfig().PlatformSdkVersion())
 	}
 
 	if !hasVersionName {
-		aaptFlags = append(aaptFlags,
-			"--version-name "+ctx.AConfig().PlatformVersion()+"-"+ctx.AConfig().BuildNumber())
+		versionName := proptools.NinjaEscape([]string{ctx.AConfig().AppsDefaultVersionName()})[0]
+		linkFlags = append(linkFlags, "--version-name ", versionName)
+	}
+
+	if String(a.appProperties.Instrumentation_for) != "" {
+		linkFlags = append(linkFlags,
+			"--rename-instrumentation-target-package",
+			String(a.appProperties.Instrumentation_for))
 	}
 
 	// TODO: LOCAL_PACKAGE_OVERRIDES
 	//    $(addprefix --rename-manifest-package , $(PRIVATE_MANIFEST_PACKAGE_NAME)) \
 
-	// TODO: LOCAL_INSTRUMENTATION_FOR
-	//    $(addprefix --rename-instrumentation-target-package , $(PRIVATE_MANIFEST_INSTRUMENTATION_FOR))
-
-	return aaptFlags, aaptDeps, hasResources
+	return linkFlags, linkDeps, resDirs, overlayDirs
 }
 
 func AndroidAppFactory() android.Module {
@@ -281,3 +290,76 @@
 	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
 	return module
 }
+
+func resourceGlob(ctx android.ModuleContext, dir android.Path) android.Paths {
+	var ret android.Paths
+	files := ctx.Glob(filepath.Join(dir.String(), "**/*"), aaptIgnoreFilenames)
+	for _, f := range files {
+		if isDir, err := ctx.Fs().IsDir(f.String()); err != nil {
+			ctx.ModuleErrorf("error in IsDir(%s): %s", f.String(), err.Error())
+			return nil
+		} else if !isDir {
+			ret = append(ret, f)
+		}
+	}
+	return ret
+}
+
+type overlayGlobResult struct {
+	dir   string
+	paths android.DirectorySortedPaths
+}
+
+const overlayDataKey = "overlayDataKey"
+
+func overlayResourceGlob(ctx android.ModuleContext, dir android.Path) []globbedResourceDir {
+	overlayData := ctx.AConfig().Get(overlayDataKey).([]overlayGlobResult)
+
+	var ret []globbedResourceDir
+
+	for _, data := range overlayData {
+		files := data.paths.PathsInDirectory(filepath.Join(data.dir, dir.String()))
+		if len(files) > 0 {
+			ret = append(ret, globbedResourceDir{
+				dir:   android.PathForSource(ctx, data.dir, dir.String()),
+				files: files,
+			})
+		}
+	}
+
+	return ret
+}
+
+func OverlaySingletonFactory() android.Singleton {
+	return overlaySingleton{}
+}
+
+type overlaySingleton struct{}
+
+func (overlaySingleton) GenerateBuildActions(ctx android.SingletonContext) {
+	var overlayData []overlayGlobResult
+	for _, overlay := range ctx.Config().(android.Config).ResourceOverlays() {
+		var result overlayGlobResult
+		result.dir = overlay
+		files, err := ctx.GlobWithDeps(filepath.Join(overlay, "**/*"), aaptIgnoreFilenames)
+		if err != nil {
+			ctx.Errorf("failed to glob resource dir %q: %s", overlay, err.Error())
+			continue
+		}
+		var paths android.Paths
+		for _, f := range files {
+			if isDir, err := ctx.Fs().IsDir(f); err != nil {
+				ctx.Errorf("error in IsDir(%s): %s", f, err.Error())
+				return
+			} else if !isDir {
+				paths = append(paths, android.PathForSource(ctx, f))
+			}
+		}
+		result.paths = android.PathsToDirectorySortedPaths(paths)
+		overlayData = append(overlayData, result)
+	}
+
+	ctx.Config().(android.Config).Once(overlayDataKey, func() interface{} {
+		return overlayData
+	})
+}
diff --git a/java/app_builder.go b/java/app_builder.go
index ed7abce..82574ae 100644
--- a/java/app_builder.go
+++ b/java/app_builder.go
@@ -27,31 +27,6 @@
 )
 
 var (
-	aaptCreateResourceJavaFile = pctx.AndroidStaticRule("aaptCreateResourceJavaFile",
-		blueprint.RuleParams{
-			Command: `rm -rf "$javaDir" && mkdir -p "$javaDir" && ` +
-				`$aaptCmd package -m $aaptFlags -P $publicResourcesFile -G $proguardOptionsFile ` +
-				`-J $javaDir || ( rm -rf "$javaDir/*"; exit 41 ) && ` +
-				`find $javaDir -name "*.java" > $javaFileList`,
-			CommandDeps: []string{"$aaptCmd"},
-		},
-		"aaptFlags", "publicResourcesFile", "proguardOptionsFile", "javaDir", "javaFileList")
-
-	aaptCreateAssetsPackage = pctx.AndroidStaticRule("aaptCreateAssetsPackage",
-		blueprint.RuleParams{
-			Command:     `rm -f $out && $aaptCmd package $aaptFlags -F $out`,
-			CommandDeps: []string{"$aaptCmd"},
-		},
-		"aaptFlags", "publicResourcesFile", "proguardOptionsFile", "javaDir", "javaFileList")
-
-	aaptAddResources = pctx.AndroidStaticRule("aaptAddResources",
-		blueprint.RuleParams{
-			// TODO: add-jni-shared-libs-to-package
-			Command:     `cp -f $in $out.tmp && $aaptCmd package -u $aaptFlags -F $out.tmp && mv $out.tmp $out`,
-			CommandDeps: []string{"$aaptCmd"},
-		},
-		"aaptFlags")
-
 	signapk = pctx.AndroidStaticRule("signapk",
 		blueprint.RuleParams{
 			Command:     `java -jar $signapkCmd $certificates $in $out`,
@@ -75,63 +50,30 @@
 	pctx.HostJavaToolVariable("signapkCmd", "signapk.jar")
 }
 
-func CreateResourceJavaFiles(ctx android.ModuleContext, flags []string,
-	deps android.Paths) (android.Path, android.Path, android.Path) {
-	javaDir := android.PathForModuleGen(ctx, "R")
-	javaFileList := android.PathForModuleOut(ctx, "R.filelist")
-	publicResourcesFile := android.PathForModuleOut(ctx, "public_resources.xml")
-	proguardOptionsFile := android.PathForModuleOut(ctx, "proguard.options")
-
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        aaptCreateResourceJavaFile,
-		Description: "aapt create R.java",
-		Outputs:     android.WritablePaths{publicResourcesFile, proguardOptionsFile, javaFileList},
-		Implicits:   deps,
-		Args: map[string]string{
-			"aaptFlags":           strings.Join(flags, " "),
-			"publicResourcesFile": publicResourcesFile.String(),
-			"proguardOptionsFile": proguardOptionsFile.String(),
-			"javaDir":             javaDir.String(),
-			"javaFileList":        javaFileList.String(),
-		},
+var combineApk = pctx.AndroidStaticRule("combineApk",
+	blueprint.RuleParams{
+		Command:     `${config.MergeZipsCmd} $out $in`,
+		CommandDeps: []string{"${config.MergeZipsCmd}"},
 	})
 
-	return publicResourcesFile, proguardOptionsFile, javaFileList
-}
+func CreateAppPackage(ctx android.ModuleContext, outputFile android.WritablePath,
+	resJarFile, dexJarFile android.Path, certificates []string) {
 
-func CreateExportPackage(ctx android.ModuleContext, flags []string, deps android.Paths) android.ModuleOutPath {
-	outputFile := android.PathForModuleOut(ctx, "package-export.apk")
+	// TODO(ccross): JNI libs
+
+	unsignedApk := android.PathForModuleOut(ctx, "unsigned.apk")
+
+	inputs := android.Paths{resJarFile}
+	if dexJarFile != nil {
+		inputs = append(inputs, dexJarFile)
+	}
 
 	ctx.Build(pctx, android.BuildParams{
-		Rule:        aaptCreateAssetsPackage,
-		Description: "aapt export package",
-		Output:      outputFile,
-		Implicits:   deps,
-		Args: map[string]string{
-			"aaptFlags": strings.Join(flags, " "),
-		},
+		Rule:   combineApk,
+		Inputs: inputs,
+		Output: unsignedApk,
 	})
 
-	return outputFile
-}
-
-func CreateAppPackage(ctx android.ModuleContext, flags []string, jarFile android.Path,
-	certificates []string) android.Path {
-
-	resourceApk := android.PathForModuleOut(ctx, "resources.apk")
-
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        aaptAddResources,
-		Description: "aapt package",
-		Output:      resourceApk,
-		Input:       jarFile,
-		Args: map[string]string{
-			"aaptFlags": strings.Join(flags, " "),
-		},
-	})
-
-	outputFile := android.PathForModuleOut(ctx, "package.apk")
-
 	var certificateArgs []string
 	for _, c := range certificates {
 		certificateArgs = append(certificateArgs, c+".x509.pem", c+".pk8")
@@ -141,11 +83,9 @@
 		Rule:        signapk,
 		Description: "signapk",
 		Output:      outputFile,
-		Input:       resourceApk,
+		Input:       unsignedApk,
 		Args: map[string]string{
 			"certificates": strings.Join(certificateArgs, " "),
 		},
 	})
-
-	return outputFile
 }
diff --git a/java/app_test.go b/java/app_test.go
new file mode 100644
index 0000000..0b1491e
--- /dev/null
+++ b/java/app_test.go
@@ -0,0 +1,90 @@
+// 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 (
+	"android/soong/android"
+	"reflect"
+	"testing"
+)
+
+var (
+	resourceFiles = []string{
+		"res/layout/layout.xml",
+		"res/values/strings.xml",
+		"res/values-en-rUS/strings.xml",
+	}
+
+	compiledResourceFiles = []string{
+		"aapt2/res/layout_layout.xml.flat",
+		"aapt2/res/values_strings.arsc.flat",
+		"aapt2/res/values-en-rUS_strings.arsc.flat",
+	}
+)
+
+func testApp(t *testing.T, bp string) *android.TestContext {
+	bp += `
+		android_app {
+			name: "framework-res",
+			no_framework_libs: true,
+		}
+	`
+
+	appFs := map[string][]byte{
+		"AndroidManifest.xml":                   nil,
+		"build/target/product/security/testkey": nil,
+	}
+
+	for _, file := range resourceFiles {
+		appFs[file] = nil
+	}
+
+	return testJavaWithEnvFs(t, bp, nil, appFs)
+}
+
+func TestApp(t *testing.T) {
+	ctx := testApp(t, `
+		android_app {
+			name: "foo",
+			srcs: ["a.java"],
+		}
+	`)
+
+	foo := ctx.ModuleForTests("foo", "android_common")
+
+	expectedLinkImplicits := []string{"AndroidManifest.xml"}
+
+	frameworkRes := ctx.ModuleForTests("framework-res", "android_common")
+	expectedLinkImplicits = append(expectedLinkImplicits,
+		frameworkRes.Output("package-res.apk").Output.String())
+
+	// Test the mapping from input files to compiled output file names
+	compile := foo.Output(compiledResourceFiles[0])
+	if !reflect.DeepEqual(resourceFiles, compile.Inputs.Strings()) {
+		t.Errorf("expected aapt2 compile inputs expected:\n  %#v\n got:\n  %#v",
+			resourceFiles, compile.Inputs.Strings())
+	}
+	expectedLinkImplicits = append(expectedLinkImplicits, compile.Outputs.Strings()...)
+
+	list := foo.Output("aapt2/res.list")
+	expectedLinkImplicits = append(expectedLinkImplicits, list.Output.String())
+
+	// Check that the link rule uses
+	res := ctx.ModuleForTests("foo", "android_common").Output("package-res.apk")
+	if !reflect.DeepEqual(expectedLinkImplicits, res.Implicits.Strings()) {
+		t.Errorf("expected aapt2 link implicits expected:\n  %#v\n got:\n  %#v",
+			expectedLinkImplicits, res.Implicits.Strings())
+	}
+}
diff --git a/java/config/config.go b/java/config/config.go
index 3cd2841..603d43f 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -15,6 +15,8 @@
 package config
 
 import (
+	"path/filepath"
+	"runtime"
 	"strings"
 
 	_ "github.com/google/blueprint/bootstrap"
@@ -28,6 +30,16 @@
 	DefaultBootclasspathLibraries = []string{"core-oj", "core-libart"}
 	DefaultSystemModules          = "core-system-modules"
 	DefaultLibraries              = []string{"ext", "framework", "okhttp"}
+
+	DefaultJacocoExcludeFilter = []string{"org.junit.*", "org.jacoco.*", "org.mockito.*"}
+
+	InstrumentFrameworkModules = []string{
+		"framework",
+		"telephony-common",
+		"services",
+		"android.car",
+		"android.car7",
+	}
 )
 
 func init() {
@@ -54,9 +66,9 @@
 
 	pctx.VariableConfigMethod("hostPrebuiltTag", android.Config.PrebuiltOS)
 
-	pctx.VariableFunc("JavaHome", func(config interface{}) (string, error) {
+	pctx.VariableFunc("JavaHome", func(config android.Config) (string, error) {
 		// This is set up and guaranteed by soong_ui
-		return config.(android.Config).Getenv("ANDROID_JAVA_HOME"), nil
+		return config.Getenv("ANDROID_JAVA_HOME"), nil
 	})
 
 	pctx.SourcePathVariable("JavaToolchain", "${JavaHome}/bin")
@@ -74,9 +86,10 @@
 	pctx.SourcePathVariable("JarArgsCmd", "build/soong/scripts/jar-args.sh")
 	pctx.HostBinToolVariable("SoongZipCmd", "soong_zip")
 	pctx.HostBinToolVariable("MergeZipsCmd", "merge_zips")
-	pctx.VariableFunc("DxCmd", func(config interface{}) (string, error) {
-		if config.(android.Config).IsEnvFalse("USE_D8") {
-			if config.(android.Config).UnbundledBuild() || config.(android.Config).IsPdkBuild() {
+	pctx.HostBinToolVariable("Zip2ZipCmd", "zip2zip")
+	pctx.VariableFunc("DxCmd", func(config android.Config) (string, error) {
+		if config.IsEnvFalse("USE_D8") {
+			if config.UnbundledBuild() || config.IsPdkBuild() {
 				return "prebuilts/build-tools/common/bin/dx", nil
 			} else {
 				path, err := pctx.HostBinToolPath(config, "dx")
@@ -93,9 +106,9 @@
 			return path.String(), nil
 		}
 	})
-	pctx.VariableFunc("TurbineJar", func(config interface{}) (string, error) {
+	pctx.VariableFunc("TurbineJar", func(config android.Config) (string, error) {
 		turbine := "turbine.jar"
-		if config.(android.Config).UnbundledBuild() {
+		if config.UnbundledBuild() {
 			return "prebuilts/build-tools/common/framework/" + turbine, nil
 		} else {
 			path, err := pctx.HostJavaToolPath(config, turbine)
@@ -111,10 +124,28 @@
 
 	pctx.HostBinToolVariable("SoongJavacWrapper", "soong_javac_wrapper")
 
-	pctx.VariableFunc("JavacWrapper", func(config interface{}) (string, error) {
-		if override := config.(android.Config).Getenv("JAVAC_WRAPPER"); override != "" {
+	pctx.VariableFunc("JavacWrapper", func(config android.Config) (string, error) {
+		if override := config.Getenv("JAVAC_WRAPPER"); override != "" {
 			return override + " ", nil
 		}
 		return "", nil
 	})
+
+	pctx.HostJavaToolVariable("JacocoCLIJar", "jacoco-cli.jar")
+
+	hostBinToolVariableWithPrebuilt := func(name, prebuiltDir, tool string) {
+		pctx.VariableFunc(name, func(config android.Config) (string, error) {
+			if config.UnbundledBuild() || config.IsPdkBuild() {
+				return filepath.Join(prebuiltDir, runtime.GOOS, "bin", tool), nil
+			} else {
+				if path, err := pctx.HostBinToolPath(config, tool); err != nil {
+					return "", err
+				} else {
+					return path.String(), nil
+				}
+			}
+		})
+	}
+
+	hostBinToolVariableWithPrebuilt("Aapt2Cmd", "prebuilt/sdk/tools", "aapt2")
 }
diff --git a/java/config/error_prone.go b/java/config/error_prone.go
index 31cbf2c..862217f 100644
--- a/java/config/error_prone.go
+++ b/java/config/error_prone.go
@@ -14,6 +14,8 @@
 
 package config
 
+import "android/soong/android"
+
 var (
 	// These will be filled out by external/error_prone/soong/error_prone.go if it is available
 	ErrorProneJavacJar    string
@@ -25,7 +27,7 @@
 
 // Wrapper that grabs value of val late so it can be initialized by a later module's init function
 func errorProneVar(name string, val *string) {
-	pctx.VariableFunc(name, func(config interface{}) (string, error) {
+	pctx.VariableFunc(name, func(config android.Config) (string, error) {
 		return *val, nil
 	})
 }
diff --git a/java/config/makevars.go b/java/config/makevars.go
index dabf2e7..b9009f3 100644
--- a/java/config/makevars.go
+++ b/java/config/makevars.go
@@ -62,4 +62,7 @@
 
 	ctx.Strict("SOONG_JAVAC_WRAPPER", "${SoongJavacWrapper}")
 	ctx.Strict("EXTRACT_SRCJARS", "${ExtractSrcJarsCmd}")
+
+	ctx.Strict("JACOCO_CLI_JAR", "${JacocoCLIJar}")
+	ctx.Strict("DEFAULT_JACOCO_EXCLUDE_FILTER", strings.Join(DefaultJacocoExcludeFilter, ","))
 }
diff --git a/java/gen.go b/java/gen.go
index b5973ec..7a0dcac 100644
--- a/java/gen.go
+++ b/java/gen.go
@@ -28,8 +28,6 @@
 	pctx.HostBinToolVariable("aidlCmd", "aidl")
 	pctx.SourcePathVariable("logtagsCmd", "build/tools/java-event-log-tags.py")
 	pctx.SourcePathVariable("mergeLogtagsCmd", "build/tools/merge-event-log-tags.py")
-
-	pctx.IntermediatesPathVariable("allLogtagsFile", "all-event-log-tags.txt")
 }
 
 var (
@@ -117,7 +115,7 @@
 	return outSrcFiles
 }
 
-func LogtagsSingleton() blueprint.Singleton {
+func LogtagsSingleton() android.Singleton {
 	return &logtagsSingleton{}
 }
 
@@ -127,18 +125,18 @@
 
 type logtagsSingleton struct{}
 
-func (l *logtagsSingleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
+func (l *logtagsSingleton) GenerateBuildActions(ctx android.SingletonContext) {
 	var allLogtags android.Paths
-	ctx.VisitAllModules(func(module blueprint.Module) {
+	ctx.VisitAllModules(func(module android.Module) {
 		if logtags, ok := module.(logtagsProducer); ok {
 			allLogtags = append(allLogtags, logtags.logtags()...)
 		}
 	})
 
-	ctx.Build(pctx, blueprint.BuildParams{
+	ctx.Build(pctx, android.BuildParams{
 		Rule:        mergeLogtags,
 		Description: "merge logtags",
-		Outputs:     []string{"$allLogtagsFile"},
-		Inputs:      allLogtags.Strings(),
+		Output:      android.PathForIntermediates(ctx, "all-event-log-tags.txt"),
+		Inputs:      allLogtags,
 	})
 }
diff --git a/java/jacoco.go b/java/jacoco.go
new file mode 100644
index 0000000..b26b046
--- /dev/null
+++ b/java/jacoco.go
@@ -0,0 +1,108 @@
+// 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
+
+// Rules for instrumenting classes using jacoco
+
+import (
+	"strings"
+
+	"github.com/google/blueprint"
+
+	"android/soong/android"
+)
+
+var (
+	jacoco = pctx.AndroidStaticRule("jacoco", blueprint.RuleParams{
+		Command: `${config.Zip2ZipCmd} -i $in -o $strippedJar $stripSpec && ` +
+			`${config.JavaCmd} -jar ${config.JacocoCLIJar} instrument -quiet -dest $instrumentedJar $strippedJar && ` +
+			`${config.Ziptime} $instrumentedJar && ` +
+			`${config.MergeZipsCmd} --ignore-duplicates -j $out $instrumentedJar $in`,
+		CommandDeps: []string{
+			"${config.Zip2ZipCmd}",
+			"${config.JavaCmd}",
+			"${config.JacocoCLIJar}",
+			"${config.Ziptime}",
+			"${config.MergeZipsCmd}",
+		},
+	},
+		"strippedJar", "stripSpec", "instrumentedJar")
+)
+
+func jacocoInstrumentJar(ctx android.ModuleContext, outputJar, strippedJar android.WritablePath,
+	inputJar android.Path, stripSpec string) {
+	instrumentedJar := android.PathForModuleOut(ctx, "jacoco/instrumented.jar")
+
+	ctx.Build(pctx, android.BuildParams{
+		Rule:           jacoco,
+		Description:    "jacoco",
+		Output:         outputJar,
+		ImplicitOutput: strippedJar,
+		Input:          inputJar,
+		Args: map[string]string{
+			"strippedJar":     strippedJar.String(),
+			"stripSpec":       stripSpec,
+			"instrumentedJar": instrumentedJar.String(),
+		},
+	})
+}
+
+func (j *Module) jacocoStripSpecs(ctx android.ModuleContext) string {
+	includes := jacocoFiltersToSpecs(ctx,
+		j.properties.Jacoco.Include_filter, "jacoco.include_filter")
+	excludes := jacocoFiltersToSpecs(ctx,
+		j.properties.Jacoco.Exclude_filter, "jacoco.exclude_filter")
+
+	specs := ""
+	if len(excludes) > 0 {
+		specs += android.JoinWithPrefix(excludes, "-x") + " "
+	}
+
+	if len(includes) > 0 {
+		specs += strings.Join(includes, " ")
+	} else {
+		specs += "**/*.class"
+	}
+
+	return specs
+}
+
+func jacocoFiltersToSpecs(ctx android.ModuleContext, filters []string, property string) []string {
+	specs := make([]string, len(filters))
+	for i, f := range filters {
+		specs[i] = jacocoFilterToSpec(ctx, f, property)
+	}
+	return specs
+}
+
+func jacocoFilterToSpec(ctx android.ModuleContext, filter string, property string) string {
+	wildcard := strings.HasSuffix(filter, "*")
+	filter = strings.TrimSuffix(filter, "*")
+	recursiveWildcard := wildcard && (strings.HasSuffix(filter, ".") || filter == "")
+
+	if strings.ContainsRune(filter, '*') {
+		ctx.PropertyErrorf(property, "'*' is only supported as the last character in a filter")
+	}
+
+	spec := strings.Replace(filter, ".", "/", -1)
+
+	if recursiveWildcard {
+		spec += "**/*.class"
+	} else if wildcard {
+		spec += "*.class"
+	}
+
+	return spec
+}
diff --git a/java/java.go b/java/java.go
index b2bd2b0..0a3b9b4 100644
--- a/java/java.go
+++ b/java/java.go
@@ -51,9 +51,6 @@
 //  Renderscript
 // Post-jar passes:
 //  Proguard
-//  Jacoco
-//  Jarjar
-//  Dex
 // Rmtypedefs
 // DroidDoc
 // Findbugs
@@ -127,6 +124,26 @@
 		// List of javac flags that should only be used when passing -source 1.9
 		Javacflags []string
 	}
+
+	Jacoco struct {
+		// List of classes to include for instrumentation with jacoco to collect coverage
+		// information at runtime when building with coverage enabled.  If unset defaults to all
+		// classes.
+		// Supports '*' as the last character of an entry in the list as a wildcard match.
+		// If preceded by '.' it matches all classes in the package and subpackages, otherwise
+		// it matches classes in the package that have the class name as a prefix.
+		Include_filter []string
+
+		// List of classes to exclude from instrumentation with jacoco to collect coverage
+		// information at runtime when building with coverage enabled.  Overrides classes selected
+		// by the include_filter property.
+		// Supports '*' as the last character of an entry in the list as a wildcard match.
+		// If preceded by '.' it matches all classes in the package and subpackages, otherwise
+		// it matches classes in the package that have the class name as a prefix.
+		Exclude_filter []string
+	}
+
+	Instrument bool `blueprint:"mutated"`
 }
 
 type CompilerDeviceProperties struct {
@@ -177,6 +194,9 @@
 	// output file containing classes.dex
 	dexJarFile android.Path
 
+	// output file containing uninstrumented classes that will be instrumented by jacoco
+	jacocoReportClassesFile android.Path
+
 	// output file suitable for installing or running
 	outputFile android.Path
 
@@ -184,10 +204,6 @@
 
 	logtagsSrcs android.Paths
 
-	// jars containing source files that should be included in the javac command line,
-	// for example R.java generated by aapt for android apps
-	ExtraSrcJars android.Paths
-
 	// installed file for binary dependency
 	installFile android.Path
 }
@@ -467,7 +483,7 @@
 			if ctx.ModuleName() == "framework" {
 				// framework.jar has a one-off dependency on the R.java and Manifest.java files
 				// generated by framework-res.apk
-				// TODO(ccross): aapt java files should go in a src jar
+				deps.srcJars = append(deps.srcJars, dep.(*AndroidApp).aaptSrcJar)
 			}
 		case kotlinStdlibTag:
 			deps.kotlinStdlib = dep.HeaderJars()
@@ -535,7 +551,7 @@
 	return flags
 }
 
-func (j *Module) compile(ctx android.ModuleContext) {
+func (j *Module) compile(ctx android.ModuleContext, extraSrcJars ...android.Path) {
 
 	j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.deviceProperties.Aidl.Export_include_dirs)
 
@@ -554,7 +570,7 @@
 
 	srcJars := srcFiles.FilterByExt(".srcjar")
 	srcJars = append(srcJars, deps.srcJars...)
-	srcJars = append(srcJars, j.ExtraSrcJars...)
+	srcJars = append(srcJars, extraSrcJars...)
 
 	var jars android.Paths
 
@@ -718,6 +734,20 @@
 	}
 
 	if ctx.Device() && j.installable() {
+		outputFile = j.desugar(ctx, flags, outputFile, jarName)
+	}
+
+	if ctx.AConfig().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") {
+		if inList(ctx.ModuleName(), config.InstrumentFrameworkModules) {
+			j.properties.Instrument = true
+		}
+	}
+
+	if ctx.AConfig().IsEnvTrue("EMMA_INSTRUMENT") && j.properties.Instrument {
+		outputFile = j.instrument(ctx, flags, outputFile, jarName)
+	}
+
+	if ctx.Device() && j.installable() {
 		outputFile = j.compileDex(ctx, flags, outputFile, jarName)
 		if ctx.Failed() {
 			return
@@ -766,6 +796,42 @@
 	return headerJar
 }
 
+func (j *Module) desugar(ctx android.ModuleContext, flags javaBuilderFlags,
+	classesJar android.Path, jarName string) android.Path {
+
+	desugarFlags := []string{
+		"--min_sdk_version " + j.minSdkVersionNumber(ctx),
+		"--desugar_try_with_resources_if_needed=false",
+		"--allow_empty_bootclasspath",
+	}
+
+	if inList("--core-library", j.deviceProperties.Dxflags) {
+		desugarFlags = append(desugarFlags, "--core_library")
+	}
+
+	flags.desugarFlags = strings.Join(desugarFlags, " ")
+
+	desugarJar := android.PathForModuleOut(ctx, "desugar", jarName)
+	TransformDesugar(ctx, desugarJar, classesJar, flags)
+
+	return desugarJar
+}
+
+func (j *Module) instrument(ctx android.ModuleContext, flags javaBuilderFlags,
+	classesJar android.Path, jarName string) android.Path {
+
+	specs := j.jacocoStripSpecs(ctx)
+
+	jacocoReportClassesFile := android.PathForModuleOut(ctx, "jacoco", "jacoco-report-classes.jar")
+	instrumentedJar := android.PathForModuleOut(ctx, "jacoco", jarName)
+
+	jacocoInstrumentJar(ctx, instrumentedJar, jacocoReportClassesFile, classesJar, specs)
+
+	j.jacocoReportClassesFile = jacocoReportClassesFile
+
+	return instrumentedJar
+}
+
 func (j *Module) compileDex(ctx android.ModuleContext, flags javaBuilderFlags,
 	classesJar android.Path, jarName string) android.Path {
 
@@ -792,47 +858,29 @@
 			"--dump-width=1000")
 	}
 
-	var minSdkVersion string
-	switch String(j.deviceProperties.Sdk_version) {
-	case "", "current", "test_current", "system_current":
-		minSdkVersion = strconv.Itoa(ctx.AConfig().DefaultAppTargetSdkInt())
-	default:
-		minSdkVersion = String(j.deviceProperties.Sdk_version)
-	}
-
-	dxFlags = append(dxFlags, "--min-sdk-version="+minSdkVersion)
+	dxFlags = append(dxFlags, "--min-sdk-version="+j.minSdkVersionNumber(ctx))
 
 	flags.dxFlags = strings.Join(dxFlags, " ")
 
-	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 := android.PathForModuleOut(ctx, "desugar", jarName)
-	TransformDesugar(ctx, desugarJar, classesJar, flags)
-	if ctx.Failed() {
-		return nil
-	}
-
 	// Compile classes.jar into classes.dex and then javalib.jar
 	javalibJar := android.PathForModuleOut(ctx, "dex", jarName)
-	TransformClassesJarToDexJar(ctx, javalibJar, desugarJar, flags)
-	if ctx.Failed() {
-		return nil
-	}
+	TransformClassesJarToDexJar(ctx, javalibJar, classesJar, flags)
 
 	j.dexJarFile = javalibJar
 	return javalibJar
 }
 
+// Returns a sdk version as a string that is guaranteed to be a parseable as a number.  For
+// modules targeting an unreleased SDK (meaning it does not yet have a number) it returns "10000".
+func (j *Module) minSdkVersionNumber(ctx android.ModuleContext) string {
+	switch String(j.deviceProperties.Sdk_version) {
+	case "", "current", "test_current", "system_current":
+		return strconv.Itoa(ctx.AConfig().DefaultAppTargetSdkInt())
+	default:
+		return String(j.deviceProperties.Sdk_version)
+	}
+}
+
 func (j *Module) installable() bool {
 	return j.properties.Installable == nil || *j.properties.Installable
 }
diff --git a/java/java_test.go b/java/java_test.go
index e1c90ee..cf5047b 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -52,10 +52,11 @@
 	os.Exit(run())
 }
 func testJava(t *testing.T, bp string) *android.TestContext {
-	return testJavaWithEnv(t, bp, nil)
+	return testJavaWithEnvFs(t, bp, nil, nil)
 }
 
-func testJavaWithEnv(t *testing.T, bp string, env map[string]string) *android.TestContext {
+func testJavaWithEnvFs(t *testing.T, bp string,
+	env map[string]string, fs map[string][]byte) *android.TestContext {
 	config := android.TestArchConfig(buildDir, env)
 
 	ctx := android.NewTestArchContext()
@@ -70,6 +71,7 @@
 	ctx.PreArchMutators(android.RegisterPrebuiltsPreArchMutators)
 	ctx.PreArchMutators(android.RegisterPrebuiltsPostDepsMutators)
 	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
+	ctx.RegisterPreSingletonType("overlay", android.SingletonFactoryAdaptor(OverlaySingletonFactory))
 	ctx.Register()
 
 	extraModules := []string{
@@ -112,17 +114,17 @@
 		}
 	}
 
-	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,
-		"res/b":      nil,
-		"res2/a":     nil,
+	mockFS := map[string][]byte{
+		"Android.bp":  []byte(bp),
+		"a.java":      nil,
+		"b.java":      nil,
+		"c.java":      nil,
+		"b.kt":        nil,
+		"a.jar":       nil,
+		"b.jar":       nil,
+		"java-res/a":  nil,
+		"java-res/b":  nil,
+		"java-res2/a": nil,
 
 		"prebuilts/sdk/14/android.jar":                nil,
 		"prebuilts/sdk/14/framework.aidl":             nil,
@@ -132,7 +134,13 @@
 		"prebuilts/sdk/system_current/framework.aidl": nil,
 		"prebuilts/sdk/test_current/android.jar":      nil,
 		"prebuilts/sdk/test_current/framework.aidl":   nil,
-	})
+	}
+
+	for k, v := range fs {
+		mockFS[k] = v
+	}
+
+	ctx.MockFileSystem(mockFS)
 
 	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
 	fail(t, errs)
@@ -394,7 +402,7 @@
 
 			// 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"})
+				ctx := testJavaWithEnvFs(t, bp, map[string]string{"EXPERIMENTAL_USE_OPENJDK9": "true"}, nil)
 
 				javac := ctx.ModuleForTests("foo", variant).Rule("javac")
 				got := javac.Args["bootClasspath"]
@@ -497,14 +505,14 @@
 		{
 			// Test that a module with java_resource_dirs includes the files
 			name: "resource dirs",
-			prop: `java_resource_dirs: ["res"]`,
-			args: "-C res -f res/a -f res/b",
+			prop: `java_resource_dirs: ["java-res"]`,
+			args: "-C java-res -f java-res/a -f java-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 -f res/b",
+			prop: `java_resources: ["java-res/a", "java-res/b"]`,
+			args: "-C . -f java-res/a -f java-res/b",
 		},
 		{
 			// Test that a module with a filegroup in java_resources includes the files with the
@@ -514,10 +522,10 @@
 			extra: `
 				filegroup {
 					name: "foo-res",
-					path: "res",
-					srcs: ["res/a", "res/b"],
+					path: "java-res",
+					srcs: ["java-res/a", "java-res/b"],
 				}`,
-			args: "-C res -f res/a -f res/b",
+			args: "-C java-res -f java-res/a -f java-res/b",
 		},
 		{
 			// Test that a module with "include_srcs: true" includes its source files in the resources jar
@@ -562,21 +570,21 @@
 		java_library {
 			name: "foo",
 			srcs: ["a.java"],
-			java_resource_dirs: ["res", "res2"],
-			exclude_java_resource_dirs: ["res2"],
+			java_resource_dirs: ["java-res", "java-res2"],
+			exclude_java_resource_dirs: ["java-res2"],
 		}
 
 		java_library {
 			name: "bar",
 			srcs: ["a.java"],
-			java_resources: ["res/*"],
-			exclude_java_resources: ["res/b"],
+			java_resources: ["java-res/*"],
+			exclude_java_resources: ["java-res/b"],
 		}
 	`)
 
 	fooRes := ctx.ModuleForTests("foo", "android_common").Output("res/foo.jar")
 
-	expected := "-C res -f res/a -f res/b"
+	expected := "-C java-res -f java-res/a -f java-res/b"
 	if fooRes.Args["jarArgs"] != expected {
 		t.Errorf("foo resource jar args %q is not %q",
 			fooRes.Args["jarArgs"], expected)
@@ -585,7 +593,7 @@
 
 	barRes := ctx.ModuleForTests("bar", "android_common").Output("res/bar.jar")
 
-	expected = "-C . -f res/a"
+	expected = "-C . -f java-res/a"
 	if barRes.Args["jarArgs"] != expected {
 		t.Errorf("bar resource jar args %q is not %q",
 			barRes.Args["jarArgs"], expected)
@@ -606,7 +614,7 @@
 
 		genrule {
 			name: "gen",
-			tool_files: ["res/a"],
+			tool_files: ["java-res/a"],
 			out: ["gen.java"],
 		}
 	`)
diff --git a/ui/build/finder.go b/ui/build/finder.go
index 05dec3a..010b325 100644
--- a/ui/build/finder.go
+++ b/ui/build/finder.go
@@ -43,7 +43,7 @@
 		RootDirs:         []string{"."},
 		ExcludeDirs:      []string{".git", ".repo"},
 		PruneFiles:       []string{".out-dir", ".find-ignore"},
-		IncludeFiles:     []string{"Android.mk", "Android.bp", "Blueprints", "CleanSpec.mk"},
+		IncludeFiles:     []string{"Android.mk", "Android.bp", "Blueprints", "CleanSpec.mk", "TEST_MAPPING"},
 	}
 	dumpDir := config.FileListDir()
 	f, err = finder.New(cacheParams, fs.OsFs, logger.New(ioutil.Discard),
@@ -74,6 +74,12 @@
 		ctx.Fatalf("Could not export module list: %v", err)
 	}
 
+	testMappings := f.FindNamedAt(".", "TEST_MAPPING")
+	err = dumpListToFile(testMappings, filepath.Join(dumpDir, "TEST_MAPPING.list"))
+	if err != nil {
+		ctx.Fatalf("Could not find modules: %v", err)
+	}
+
 	isBlueprintFile := func(dir finder.DirEntries) (dirs []string, files []string) {
 		files = []string{}
 		for _, file := range dir.FileNames {