Replace aapt support with aapt2

Use aapt2 instead of aapt to compile Android app resources.
Also generate all files into srcjars instead of individual
sources.

Test: m checkbuild
Change-Id: I5a67991a0daf0017e8159b46fcff7d5564a91468
diff --git a/Android.bp b/Android.bp
index 886d63d..4dddb28 100644
--- a/Android.bp
+++ b/Android.bp
@@ -209,6 +209,7 @@
         "soong-java-config",
     ],
     srcs: [
+        "java/aapt2.go",
         "java/androidmk.go",
         "java/app_builder.go",
         "java/app.go",
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/paths.go b/android/paths.go
index b2ee627..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
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/app.go b/java/app.go
index 68e4d5c..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
@@ -63,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")
@@ -81,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,26 +116,8 @@
 	//	a.properties.Proguard.Enabled = true
 	//}
 
-	if String(a.appProperties.Instrumentation_for) == "" {
-		a.properties.Instrument = true
-	}
-
 	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()
@@ -155,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)
 }
 
@@ -171,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"
@@ -230,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 {
@@ -287,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 466563f..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"
@@ -130,4 +132,20 @@
 	})
 
 	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/java.go b/java/java.go
index 417cf74..0a3b9b4 100644
--- a/java/java.go
+++ b/java/java.go
@@ -204,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
 }
@@ -487,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()
@@ -555,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)
 
@@ -574,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
 
diff --git a/java/java_test.go b/java/java_test.go
index 143e82c..cf5047b 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -71,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{