Merge "Stop versioning NDK stubs pre-M."
diff --git a/Android.bp b/Android.bp
index 4a06a11..26aeac2 100644
--- a/Android.bp
+++ b/Android.bp
@@ -221,6 +221,7 @@
     srcs: [
         "java/aapt2.go",
         "java/aar.go",
+        "java/android_manifest.go",
         "java/android_resources.go",
         "java/androidmk.go",
         "java/app_builder.go",
@@ -233,8 +234,10 @@
         "java/jacoco.go",
         "java/java.go",
         "java/java_resources.go",
+        "java/prebuilt_apis.go",
         "java/proto.go",
         "java/sdk_library.go",
+        "java/support_libraries.go",
         "java/system_modules.go",
     ],
     testSrcs: [
@@ -334,6 +337,7 @@
     name: "libatomic",
     defaults: ["linux_bionic_supported"],
     vendor_available: true,
+    recovery_available: true,
     arch: {
         arm: {
             instruction_set: "arm",
@@ -345,6 +349,7 @@
     name: "libgcc",
     defaults: ["linux_bionic_supported"],
     vendor_available: true,
+    recovery_available: true,
     arch: {
         arm: {
             instruction_set: "arm",
diff --git a/README.md b/README.md
index 3549b02..9f427c4 100644
--- a/README.md
+++ b/README.md
@@ -217,6 +217,18 @@
 or [external/llvm/soong/llvm.go](https://android.googlesource.com/platform/external/llvm/+/master/soong/llvm.go)
 for examples of more complex conditionals on product variables or environment variables.
 
+## Developing for Soong
+
+To load Soong code in a Go-aware IDE, create a directory outside your android tree and then:
+```bash
+apt install bindfs
+export GOPATH=<path to the directory you created>
+build/soong/scripts/setup_go_workspace_for_soong.sh
+```
+
+This will bind mount the Soong source directories into the directory in the layout expected by
+the IDE.
+
 ## Contact
 
 Email android-building@googlegroups.com (external) for any questions, or see
diff --git a/android/arch.go b/android/arch.go
index 9f03f1b..d84c829 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -330,6 +330,10 @@
 			// Windows builds always prefer 32-bit
 			prefer32 = true
 		}
+		// only the primary arch in the recovery partition
+		if module.InstallInRecovery() {
+			targets = []Target{mctx.Config().Targets[Device][0]}
+		}
 		targets, err := decodeMultilib(multilib, targets, prefer32)
 		if err != nil {
 			mctx.ModuleErrorf("%s", err.Error())
@@ -956,6 +960,7 @@
 		{"arm", "armv7-a-neon", "cortex-a53", []string{"armeabi-v7a"}},
 		{"arm", "armv7-a-neon", "cortex-a53.a57", []string{"armeabi-v7a"}},
 		{"arm", "armv7-a-neon", "cortex-a73", []string{"armeabi-v7a"}},
+		{"arm", "armv7-a-neon", "cortex-a75", []string{"armeabi-v7a"}},
 		{"arm", "armv7-a-neon", "denver", []string{"armeabi-v7a"}},
 		{"arm", "armv7-a-neon", "krait", []string{"armeabi-v7a"}},
 		{"arm", "armv7-a-neon", "kryo", []string{"armeabi-v7a"}},
@@ -967,6 +972,7 @@
 		{"arm64", "armv8-a", "kryo", []string{"arm64-v8a"}},
 		{"arm64", "armv8-a", "exynos-m1", []string{"arm64-v8a"}},
 		{"arm64", "armv8-a", "exynos-m2", []string{"arm64-v8a"}},
+		{"arm64", "armv8-2a", "cortex-a75", []string{"arm64-v8a"}},
 		{"mips", "mips32-fp", "", []string{"mips"}},
 		{"mips", "mips32r2-fp", "", []string{"mips"}},
 		{"mips", "mips32r2-fp-xburst", "", []string{"mips"}},
diff --git a/android/config.go b/android/config.go
index 40ba8c1..7ba05c1 100644
--- a/android/config.go
+++ b/android/config.go
@@ -90,8 +90,9 @@
 	ConfigFileName           string
 	ProductVariablesFileName string
 
-	Targets        map[OsClass][]Target
-	BuildOsVariant string
+	Targets              map[OsClass][]Target
+	BuildOsVariant       string
+	BuildOsCommonVariant string
 
 	deviceConfig *deviceConfig
 
@@ -108,8 +109,7 @@
 	captureBuild      bool // true for tests, saves build parameters for each module
 	ignoreEnvironment bool // true for tests, returns empty from all Getenv calls
 
-	useOpenJDK9    bool // Use OpenJDK9, but possibly target 1.8
-	targetOpenJDK9 bool // Use OpenJDK9 and target 1.9
+	targetOpenJDK9 bool // Target 1.9
 
 	stopBefore bootstrap.StopBefore
 
@@ -311,6 +311,7 @@
 
 	config.Targets = targets
 	config.BuildOsVariant = targets[Host][0].String()
+	config.BuildOsCommonVariant = getCommonTargets(targets[Host])[0].String()
 
 	if err := config.fromEnv(); err != nil {
 		return Config{}, err
@@ -321,22 +322,13 @@
 
 func (c *config) fromEnv() error {
 	switch c.Getenv("EXPERIMENTAL_USE_OPENJDK9") {
-	case "":
-		if c.Getenv("RUN_ERROR_PRONE") != "true" {
-			// Use OpenJDK9, but target 1.8
-			c.useOpenJDK9 = true
-		}
-	case "false":
-		// Use OpenJDK8
-	case "1.8":
-		// Use OpenJDK9, but target 1.8
-		c.useOpenJDK9 = true
+	case "", "1.8":
+		// Nothing, we always use OpenJDK9
 	case "true":
 		// Use OpenJDK9 and target 1.9
-		c.useOpenJDK9 = true
 		c.targetOpenJDK9 = true
 	default:
-		return fmt.Errorf(`Invalid value for EXPERIMENTAL_USE_OPENJDK9, should be "", "false", "1.8", or "true"`)
+		return fmt.Errorf(`Invalid value for EXPERIMENTAL_USE_OPENJDK9, should be "", "1.8", or "true"`)
 	}
 
 	return nil
@@ -468,6 +460,10 @@
 	return *c.productVariables.ResourceOverlays
 }
 
+func (c *config) PlatformVersionName() string {
+	return String(c.productVariables.Platform_version_name)
+}
+
 func (c *config) PlatformSdkVersionInt() int {
 	return *c.productVariables.Platform_sdk_version
 }
@@ -580,6 +576,10 @@
 	return Bool(c.productVariables.DevicePrefer32BitExecutables)
 }
 
+func (c *config) DevicePrimaryArchType() ArchType {
+	return c.Targets[Device][0].Arch.ArchType
+}
+
 func (c *config) SkipDeviceInstall() bool {
 	return c.EmbeddedInMake()
 }
@@ -623,17 +623,12 @@
 	return false
 }
 
-func (c *config) UseD8Desugar() bool {
-	return !c.IsEnvFalse("USE_D8_DESUGAR")
-}
-
 func (c *config) UseGoma() bool {
 	return Bool(c.productVariables.UseGoma)
 }
 
-// Returns true if OpenJDK9 prebuilts are being used
-func (c *config) UseOpenJDK9() bool {
-	return c.useOpenJDK9
+func (c *config) RunErrorProne() bool {
+	return c.IsEnvTrue("RUN_ERROR_PRONE")
 }
 
 // Returns true if -source 1.9 -target 1.9 is being passed to javac
@@ -799,6 +794,22 @@
 	return c.config.productVariables.PgoAdditionalProfileDirs
 }
 
+func (c *deviceConfig) VendorSepolicyDirs() []string {
+	return c.config.productVariables.BoardVendorSepolicyDirs
+}
+
+func (c *deviceConfig) OdmSepolicyDirs() []string {
+	return c.config.productVariables.BoardOdmSepolicyDirs
+}
+
+func (c *deviceConfig) PlatPublicSepolicyDirs() []string {
+	return c.config.productVariables.BoardPlatPublicSepolicyDirs
+}
+
+func (c *deviceConfig) PlatPrivateSepolicyDirs() []string {
+	return c.config.productVariables.BoardPlatPrivateSepolicyDirs
+}
+
 func (c *config) IntegerOverflowDisabledForPath(path string) bool {
 	if c.productVariables.IntegerOverflowExcludePaths == nil {
 		return false
diff --git a/android/defaults.go b/android/defaults.go
index c704529..d4fbf48 100644
--- a/android/defaults.go
+++ b/android/defaults.go
@@ -131,11 +131,16 @@
 func defaultsMutator(ctx TopDownMutatorContext) {
 	if defaultable, ok := ctx.Module().(Defaultable); ok && len(defaultable.defaults().Defaults) > 0 {
 		var defaultsList []Defaults
+		seen := make(map[Defaults]bool)
+
 		ctx.WalkDeps(func(module, parent Module) bool {
 			if ctx.OtherModuleDependencyTag(module) == DefaultsDepTag {
 				if defaults, ok := module.(Defaults); ok {
-					defaultsList = append(defaultsList, defaults)
-					return len(defaults.defaults().Defaults) > 0
+					if !seen[defaults] {
+						seen[defaults] = true
+						defaultsList = append(defaultsList, defaults)
+						return len(defaults.defaults().Defaults) > 0
+					}
 				} else {
 					ctx.PropertyErrorf("defaults", "module %s is not an defaults module",
 						ctx.OtherModuleName(module))
diff --git a/android/makevars.go b/android/makevars.go
index 3094a48..accc4d3 100644
--- a/android/makevars.go
+++ b/android/makevars.go
@@ -20,6 +20,7 @@
 	"io/ioutil"
 	"os"
 	"strconv"
+	"strings"
 
 	"github.com/google/blueprint/proptools"
 )
@@ -240,8 +241,16 @@
 	return c.ctx
 }
 
+var ninjaDescaper = strings.NewReplacer("$$", "$")
+
 func (c *makeVarsContext) Eval(ninjaStr string) (string, error) {
-	return c.ctx.Eval(c.pctx, ninjaStr)
+	s, err := c.ctx.Eval(c.pctx, ninjaStr)
+	if err != nil {
+		return "", err
+	}
+	// SingletonContext.Eval returns an exapnded string that is valid for a ninja file, de-escape $$ to $ for use
+	// in a Makefile
+	return ninjaDescaper.Replace(s), nil
 }
 
 func (c *makeVarsContext) addVariableRaw(name, value string, strict, sort bool) {
diff --git a/android/module.go b/android/module.go
index 552d165..b6220dc 100644
--- a/android/module.go
+++ b/android/module.go
@@ -124,6 +124,7 @@
 
 	InstallInData() bool
 	InstallInSanitizerDir() bool
+	InstallInRecovery() bool
 
 	RequiredModuleNames() []string
 
@@ -143,7 +144,9 @@
 	VisitDirectDeps(visit func(Module))
 	VisitDirectDepsWithTag(tag blueprint.DependencyTag, visit func(Module))
 	VisitDirectDepsIf(pred func(Module) bool, visit func(Module))
+	// Deprecated: use WalkDeps instead to support multiple dependency tags on the same module
 	VisitDepsDepthFirst(visit func(Module))
+	// Deprecated: use WalkDeps instead to support multiple dependency tags on the same module
 	VisitDepsDepthFirstIf(pred func(Module) bool, visit func(Module))
 	WalkDeps(visit func(Module, Module) bool)
 
@@ -176,6 +179,7 @@
 	Target() Target
 	InstallInData() bool
 	InstallInSanitizerDir() bool
+	InstallInRecovery() bool
 	SkipInstall()
 	ExportedToMake() bool
 
@@ -191,8 +195,6 @@
 }
 
 type commonProperties struct {
-	Tags []string
-
 	// emit build rules for this module
 	Enabled *bool `android:"arch_variant"`
 
@@ -239,6 +241,9 @@
 	// /system/product if product partition does not exist).
 	Product_specific *bool
 
+	// Whether this module is installed to recovery partition
+	Recovery *bool
+
 	// init.rc files to be installed if this module is installed
 	Init_rc []string
 
@@ -536,6 +541,7 @@
 	ctx blueprint.ModuleContext) Paths {
 
 	result := Paths{}
+	// TODO(ccross): we need to use WalkDeps and have some way to know which dependencies require installation
 	ctx.VisitDepsDepthFirstIf(isFileInstaller,
 		func(m blueprint.Module) {
 			fileInstaller := m.(fileInstaller)
@@ -562,6 +568,10 @@
 	return false
 }
 
+func (p *ModuleBase) InstallInRecovery() bool {
+	return Bool(p.commonProperties.Recovery)
+}
+
 func (a *ModuleBase) generateModuleTarget(ctx ModuleContext) {
 	allInstalledFiles := Paths{}
 	allCheckbuildFiles := Paths{}
@@ -1010,11 +1020,22 @@
 	return a.module.InstallInSanitizerDir()
 }
 
+func (a *androidModuleContext) InstallInRecovery() bool {
+	return a.module.InstallInRecovery()
+}
+
 func (a *androidModuleContext) skipInstall(fullInstallPath OutputPath) bool {
 	if a.module.base().commonProperties.SkipInstall {
 		return true
 	}
 
+	// We'll need a solution for choosing which of modules with the same name in different
+	// namespaces to install.  For now, reuse the list of namespaces exported to Make as the
+	// list of namespaces to install in a Soong-only build.
+	if !a.module.base().commonProperties.NamespaceExportedToMake {
+		return true
+	}
+
 	if a.Device() {
 		if a.Config().SkipDeviceInstall() {
 			return true
diff --git a/android/paths.go b/android/paths.go
index 91dd9a6..af2f956 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -47,6 +47,7 @@
 
 	InstallInData() bool
 	InstallInSanitizerDir() bool
+	InstallInRecovery() bool
 }
 
 var _ ModuleInstallPathContext = ModuleContext(nil)
@@ -948,6 +949,9 @@
 		var partition string
 		if ctx.InstallInData() {
 			partition = "data"
+		} else if ctx.InstallInRecovery() {
+			// the layout of recovery partion is the same as that of system partition
+			partition = "recovery/root/system"
 		} else if ctx.SocSpecific() {
 			partition = ctx.DeviceConfig().VendorPath()
 		} else if ctx.DeviceSpecific() {
diff --git a/android/paths_test.go b/android/paths_test.go
index cd9fbfd..b3dc9de 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -201,6 +201,7 @@
 
 	inData         bool
 	inSanitizerDir bool
+	inRecovery     bool
 }
 
 func (moduleInstallPathContextImpl) Fs() pathtools.FileSystem {
@@ -221,6 +222,10 @@
 	return m.inSanitizerDir
 }
 
+func (m moduleInstallPathContextImpl) InstallInRecovery() bool {
+	return m.inRecovery
+}
+
 func TestPathForModuleInstall(t *testing.T) {
 	testConfig := TestConfig("", nil)
 
diff --git a/android/singleton.go b/android/singleton.go
index f577b0a..fa1efdc 100644
--- a/android/singleton.go
+++ b/android/singleton.go
@@ -50,7 +50,9 @@
 
 	VisitAllModules(visit func(Module))
 	VisitAllModulesIf(pred func(Module) bool, visit func(Module))
+	// Deprecated: use WalkDeps instead to support multiple dependency tags on the same module
 	VisitDepsDepthFirst(module Module, visit func(Module))
+	// Deprecated: use WalkDeps instead to support multiple dependency tags on the same module
 	VisitDepsDepthFirstIf(module Module, pred func(Module) bool,
 		visit func(Module))
 
diff --git a/android/testing.go b/android/testing.go
index f5d33e1..ca7e7ce 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -92,6 +92,16 @@
 	return TestingModule{module}
 }
 
+func (ctx *TestContext) ModuleVariantsForTests(name string) []string {
+	var variants []string
+	ctx.VisitAllModules(func(m blueprint.Module) {
+		if ctx.ModuleName(m) == name {
+			variants = append(variants, ctx.ModuleSubDir(m))
+		}
+	})
+	return variants
+}
+
 // MockFileSystem causes the Context to replace all reads with accesses to the provided map of
 // filenames to contents stored as a byte slice.
 func (ctx *TestContext) MockFileSystem(files map[string][]byte) {
diff --git a/android/variable.go b/android/variable.go
index 2057903..5edcdbc 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -62,6 +62,11 @@
 			Cflags []string
 		}
 
+		// Product_is_iot is true for Android Things devices.
+		Product_is_iot struct {
+			Cflags []string
+		}
+
 		// treble_linker_namespaces is true when the system/vendor linker namespace separation is
 		// enabled.
 		Treble_linker_namespaces struct {
@@ -87,6 +92,9 @@
 		Eng struct {
 			Cflags   []string
 			Cppflags []string
+			Lto      struct {
+				Never *bool
+			}
 		}
 
 		Pdk struct {
@@ -109,6 +117,7 @@
 	BuildNumberFromFile *string `json:",omitempty"`
 	DateFromFile        *string `json:",omitempty"`
 
+	Platform_version_name             *string  `json:",omitempty"`
 	Platform_sdk_version              *int     `json:",omitempty"`
 	Platform_sdk_codename             *string  `json:",omitempty"`
 	Platform_sdk_final                *bool    `json:",omitempty"`
@@ -200,6 +209,8 @@
 
 	Override_rs_driver *string `json:",omitempty"`
 
+	Product_is_iot *bool `json:",omitempty"`
+
 	DeviceKernelHeaders []string `json:",omitempty"`
 	DistDir             *string  `json:",omitempty"`
 
@@ -209,6 +220,11 @@
 
 	PgoAdditionalProfileDirs []string `json:",omitempty"`
 
+	BoardVendorSepolicyDirs      []string `json:",omitempty"`
+	BoardOdmSepolicyDirs         []string `json:",omitempty"`
+	BoardPlatPublicSepolicyDirs  []string `json:",omitempty"`
+	BoardPlatPrivateSepolicyDirs []string `json:",omitempty"`
+
 	VendorVars map[string]map[string]string `json:",omitempty"`
 }
 
diff --git a/androidmk/Android.bp b/androidmk/Android.bp
index 442452f..1d939b0 100644
--- a/androidmk/Android.bp
+++ b/androidmk/Android.bp
@@ -44,6 +44,6 @@
     ],
     testSrcs: [
         "parser/make_strings_test.go",
+        "parser/parser_test.go",
     ],
 }
-
diff --git a/androidmk/cmd/androidmk/android.go b/androidmk/cmd/androidmk/android.go
index 37877c8..29c7365 100644
--- a/androidmk/cmd/androidmk/android.go
+++ b/androidmk/cmd/androidmk/android.go
@@ -85,6 +85,7 @@
 			"LOCAL_MULTILIB":                "compile_multilib",
 			"LOCAL_ARM_MODE_HACK":           "instruction_set",
 			"LOCAL_SDK_VERSION":             "sdk_version",
+			"LOCAL_MIN_SDK_VERSION":         "min_sdk_version",
 			"LOCAL_NDK_STL_VARIANT":         "stl",
 			"LOCAL_JAR_MANIFEST":            "manifest",
 			"LOCAL_JARJAR_RULES":            "jarjar_rules",
@@ -147,7 +148,7 @@
 			"LOCAL_ANNOTATION_PROCESSOR_CLASSES": "annotation_processor_classes",
 
 			"LOCAL_PROGUARD_FLAGS":      "optimize.proguard_flags",
-			"LOCAL_PROGUARD_FLAG_FILES": "optimize.proguard_flag_files",
+			"LOCAL_PROGUARD_FLAG_FILES": "optimize.proguard_flags_files",
 
 			// These will be rewritten to libs/static_libs by bpfix, after their presence is used to convert
 			// java_library_static to android_library.
@@ -179,6 +180,8 @@
 			"LOCAL_DEX_PREOPT":                  "dex_preopt.enabled",
 			"LOCAL_DEX_PREOPT_APP_IMAGE":        "dex_preopt.app_image",
 			"LOCAL_DEX_PREOPT_GENERATE_PROFILE": "dex_preopt.profile_guided",
+
+			"LOCAL_PRIVATE_PLATFORM_APIS": "platform_apis",
 		})
 }
 
@@ -744,8 +747,8 @@
 	"BUILD_NATIVE_BENCHMARK":      "cc_benchmark",
 	"BUILD_HOST_NATIVE_BENCHMARK": "cc_benchmark_host",
 
-	"BUILD_JAVA_LIBRARY":             "java_library",
-	"BUILD_STATIC_JAVA_LIBRARY":      "java_library_static",
+	"BUILD_JAVA_LIBRARY":             "java_library_installable", // will be rewritten to java_library by bpfix
+	"BUILD_STATIC_JAVA_LIBRARY":      "java_library",
 	"BUILD_HOST_JAVA_LIBRARY":        "java_library_host",
 	"BUILD_HOST_DALVIK_JAVA_LIBRARY": "java_library_host_dalvik",
 	"BUILD_PACKAGE":                  "android_app",
diff --git a/androidmk/cmd/androidmk/androidmk_test.go b/androidmk/cmd/androidmk/androidmk_test.go
index edf3d42..80e7a75 100644
--- a/androidmk/cmd/androidmk/androidmk_test.go
+++ b/androidmk/cmd/androidmk/androidmk_test.go
@@ -331,7 +331,7 @@
 `,
 	},
 	{
-		desc: "Keep LOCAL_MODULE_TAGS non-optional",
+		desc: "Warn for LOCAL_MODULE_TAGS non-optional",
 		in: `
 include $(CLEAR_VARS)
 LOCAL_MODULE_TAGS := debug
@@ -340,7 +340,68 @@
 
 		expected: `
 cc_library_shared {
-	tags: ["debug"],
+	// WARNING: Module tags are not supported in Soong.
+	// Add this module to PRODUCT_PACKAGES_DEBUG in your product file if you want to
+	// force installation for -userdebug and -eng builds.
+}
+`,
+	},
+	{
+		desc: "Custom warning for LOCAL_MODULE_TAGS tests",
+		in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := debug tests
+include $(BUILD_SHARED_LIBRARY)
+`,
+
+		expected: `
+cc_library_shared {
+	// WARNING: Module tags are not supported in Soong.
+	// Add this module to PRODUCT_PACKAGES_DEBUG in your product file if you want to
+	// force installation for -userdebug and -eng builds.
+	// WARNING: Module tags are not supported in Soong.
+	// To make a shared library only for tests, use the "cc_test_library" module
+	// type. If you don't use gtest, set "gtest: false".
+}
+`,
+	},
+	{
+		desc: "Ignore LOCAL_MODULE_TAGS tests for cc_test",
+		in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := tests
+include $(BUILD_NATIVE_TEST)
+`,
+
+		expected: `
+cc_test {
+}
+`,
+	},
+	{
+		desc: "Convert LOCAL_MODULE_TAGS tests to java_test",
+		in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := tests
+include $(BUILD_JAVA_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := tests
+include $(BUILD_PACKAGE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := tests
+include $(BUILD_HOST_JAVA_LIBRARY)
+`,
+
+		expected: `
+java_test {
+}
+
+android_test {
+}
+
+java_test_host {
 }
 `,
 	},
@@ -451,7 +512,7 @@
 			LOCAL_PROGUARD_ENABLED := obfuscation optimization
 			# Custom
 			LOCAL_PROGUARD_ENABLED := custom
-			include $(BUILD_JAVA_LIBRARY)
+			include $(BUILD_STATIC_JAVA_LIBRARY)
 		`,
 		expected: `
 			java_library {
@@ -474,11 +535,53 @@
 		`,
 	},
 	{
+		desc: "java library",
+		in: `
+			include $(CLEAR_VARS)
+			LOCAL_SRC_FILES := a.java
+			include $(BUILD_STATIC_JAVA_LIBRARY)
+
+			include $(CLEAR_VARS)
+			LOCAL_SRC_FILES := b.java
+			include $(BUILD_JAVA_LIBRARY)
+
+			include $(CLEAR_VARS)
+			LOCAL_SRC_FILES := c.java
+			LOCAL_UNINSTALLABLE_MODULE := true
+			include $(BUILD_JAVA_LIBRARY)
+
+			include $(CLEAR_VARS)
+			LOCAL_SRC_FILES := d.java
+			LOCAL_UNINSTALLABLE_MODULE := false
+			include $(BUILD_JAVA_LIBRARY)
+		`,
+		expected: `
+			java_library {
+				srcs: ["a.java"],
+			}
+
+			java_library {
+				installable: true,
+				srcs: ["b.java"],
+			}
+
+			java_library {
+				installable: false,
+				srcs: ["c.java"],
+			}
+
+			java_library {
+				installable: true,
+				srcs: ["d.java"],
+			}
+		`,
+	},
+	{
 		desc: "errorprone options for java library",
 		in: `
 			include $(CLEAR_VARS)
 			LOCAL_ERROR_PRONE_FLAGS := -Xep:AsyncCallableReturnsNull:ERROR -Xep:AsyncFunctionReturnsNull:ERROR
-			include $(BUILD_JAVA_LIBRARY)
+			include $(BUILD_STATIC_JAVA_LIBRARY)
 		`,
 		expected: `
 			java_library {
@@ -570,12 +673,25 @@
 				],
 			}
 
-			java_library_static {
+			java_library {
 				srcs: ["test.java"],
 				static_libs: [],
 			}
 		`,
 	},
+	{
+		desc: "cc_library shared_libs",
+		in: `
+			include $(CLEAR_VARS)
+			LOCAL_SHARED_LIBRARIES := libfoo
+			include $(BUILD_SHARED_LIBRARY)
+		`,
+		expected: `
+			cc_library_shared {
+				shared_libs: ["libfoo"],
+			}
+		`,
+	},
 }
 
 func TestEndToEnd(t *testing.T) {
diff --git a/androidmk/parser/make_strings.go b/androidmk/parser/make_strings.go
index e6885a8..4b782a2 100644
--- a/androidmk/parser/make_strings.go
+++ b/androidmk/parser/make_strings.go
@@ -90,10 +90,10 @@
 	if len(ms.Strings) == 0 {
 		return ""
 	} else {
-		ret := ms.Strings[0]
+		ret := unescape(ms.Strings[0])
 		for i := range ms.Strings[1:] {
 			ret += ms.Variables[i].Value(scope)
-			ret += ms.Strings[i+1]
+			ret += unescape(ms.Strings[i+1])
 		}
 		return ret
 	}
@@ -125,6 +125,16 @@
 }
 
 func (ms *MakeString) SplitN(sep string, n int) []*MakeString {
+	return ms.splitNFunc(n, func(s string, n int) []string {
+		return splitAnyN(s, sep, n)
+	})
+}
+
+func (ms *MakeString) Words() []*MakeString {
+	return ms.splitNFunc(-1, splitWords)
+}
+
+func (ms *MakeString) splitNFunc(n int, splitFunc func(s string, n int) []string) []*MakeString {
 	ret := []*MakeString{}
 
 	curMs := SimpleMakeString("", ms.Pos())
@@ -133,7 +143,7 @@
 	var s string
 	for i, s = range ms.Strings {
 		if n != 0 {
-			split := splitAnyN(s, sep, n)
+			split := splitFunc(s, n)
 			if n != -1 {
 				if len(split) > n {
 					panic("oops!")
@@ -156,7 +166,9 @@
 		}
 	}
 
-	ret = append(ret, curMs)
+	if !curMs.Empty() {
+		ret = append(ret, curMs)
+	}
 	return ret
 }
 
@@ -206,3 +218,64 @@
 	ret = append(ret, s)
 	return ret
 }
+
+func splitWords(s string, n int) []string {
+	ret := []string{}
+	preserve := ""
+	for n == -1 || n > 1 {
+		index := strings.IndexAny(s, " \t")
+		if index == 0 && len(preserve) == 0 {
+			s = s[1:]
+		} else if index >= 0 {
+			escapeCount := 0
+			for i := index - 1; i >= 0; i-- {
+				if s[i] != '\\' {
+					break
+				}
+				escapeCount += 1
+			}
+
+			if escapeCount%2 == 1 {
+				preserve += s[0 : index+1]
+				s = s[index+1:]
+				continue
+			}
+
+			ret = append(ret, preserve+s[0:index])
+			s = s[index+1:]
+			preserve = ""
+			if n > 0 {
+				n--
+			}
+		} else {
+			break
+		}
+	}
+	if preserve != "" || s != "" || len(ret) == 0 {
+		ret = append(ret, preserve+s)
+	}
+	return ret
+}
+
+func unescape(s string) string {
+	ret := ""
+	for {
+		index := strings.IndexByte(s, '\\')
+		if index < 0 {
+			break
+		}
+
+		if index+1 == len(s) {
+			break
+		}
+
+		switch s[index+1] {
+		case ' ', '\\', '#', ':', '*', '[', '|', '\t', '\n', '\r':
+			ret += s[:index] + s[index+1:index+2]
+		default:
+			ret += s[:index+2]
+		}
+		s = s[index+2:]
+	}
+	return ret + s
+}
diff --git a/androidmk/parser/make_strings_test.go b/androidmk/parser/make_strings_test.go
index 8ad3d74..6995e89 100644
--- a/androidmk/parser/make_strings_test.go
+++ b/androidmk/parser/make_strings_test.go
@@ -99,6 +99,78 @@
 	}
 }
 
+var valueTestCases = []struct {
+	in       *MakeString
+	expected string
+}{
+	{
+		in:       SimpleMakeString("a b", NoPos),
+		expected: "a b",
+	},
+	{
+		in:       SimpleMakeString("a\\ \\\tb\\\\", NoPos),
+		expected: "a \tb\\",
+	},
+	{
+		in:       SimpleMakeString("a\\b\\", NoPos),
+		expected: "a\\b\\",
+	},
+}
+
+func TestMakeStringValue(t *testing.T) {
+	for _, test := range valueTestCases {
+		got := test.in.Value(nil)
+		if got != test.expected {
+			t.Errorf("\nwith: %q\nwant: %q\n got: %q", test.in.Dump(), test.expected, got)
+		}
+	}
+}
+
+var splitWordsTestCases = []struct {
+	in       *MakeString
+	expected []*MakeString
+}{
+	{
+		in:       SimpleMakeString("", NoPos),
+		expected: []*MakeString{},
+	},
+	{
+		in: SimpleMakeString(" a b\\ c d", NoPos),
+		expected: []*MakeString{
+			SimpleMakeString("a", NoPos),
+			SimpleMakeString("b\\ c", NoPos),
+			SimpleMakeString("d", NoPos),
+		},
+	},
+	{
+		in: SimpleMakeString("  a\tb\\\t\\ c d  ", NoPos),
+		expected: []*MakeString{
+			SimpleMakeString("a", NoPos),
+			SimpleMakeString("b\\\t\\ c", NoPos),
+			SimpleMakeString("d", NoPos),
+		},
+	},
+	{
+		in: SimpleMakeString(`a\\ b\\\ c d`, NoPos),
+		expected: []*MakeString{
+			SimpleMakeString(`a\\`, NoPos),
+			SimpleMakeString(`b\\\ c`, NoPos),
+			SimpleMakeString("d", NoPos),
+		},
+	},
+}
+
+func TestMakeStringWords(t *testing.T) {
+	for _, test := range splitWordsTestCases {
+		got := test.in.Words()
+		gotString := dumpArray(got)
+		expectedString := dumpArray(test.expected)
+		if gotString != expectedString {
+			t.Errorf("with:\n%q\nexpected:\n%s\ngot:\n%s", test.in.Dump(), expectedString, gotString)
+		}
+	}
+}
+
 func dumpArray(a []*MakeString) string {
 	ret := make([]string, len(a))
 
diff --git a/androidmk/parser/parser.go b/androidmk/parser/parser.go
index 89ee308..89c1af9 100644
--- a/androidmk/parser/parser.go
+++ b/androidmk/parser/parser.go
@@ -35,6 +35,10 @@
 	return fmt.Sprintf("%s: %s", e.Pos, e.Err)
 }
 
+const builtinDollar = "__builtin_dollar"
+
+var builtinDollarName = SimpleMakeString(builtinDollar, NoPos)
+
 func (p *parser) Parse() ([]Node, []error) {
 	defer func() {
 		if r := recover(); r != nil {
@@ -326,7 +330,11 @@
 		case '$':
 			var variable Variable
 			variable = p.parseVariable()
-			value.appendVariable(variable)
+			if variable.Name == builtinDollarName {
+				value.appendString("$")
+			} else {
+				value.appendVariable(variable)
+			}
 		case scanner.EOF:
 			break loop
 		case '(':
@@ -357,7 +365,8 @@
 	case '{':
 		return p.parseBracketedVariable('{', '}', pos)
 	case '$':
-		name = SimpleMakeString("__builtin_dollar", NoPos)
+		name = builtinDollarName
+		p.accept(p.tok)
 	case scanner.EOF:
 		p.errorf("expected variable name, found %s",
 			scanner.TokenString(p.tok))
@@ -457,6 +466,8 @@
 	case '=':
 		p.parseAssignment("=", target, prerequisites)
 		return nil, true
+	case scanner.EOF:
+		// do nothing
 	default:
 		p.errorf("unexpected token %s after rule prerequisites", scanner.TokenString(p.tok))
 	}
diff --git a/androidmk/parser/parser_test.go b/androidmk/parser/parser_test.go
new file mode 100644
index 0000000..f562c29
--- /dev/null
+++ b/androidmk/parser/parser_test.go
@@ -0,0 +1,61 @@
+// Copyright 2018 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 parser
+
+import (
+	"bytes"
+	"testing"
+)
+
+var parserTestCases = []struct {
+	name string
+	in   string
+	out  []Node
+}{
+	{
+		name: "Escaped $",
+		in:   `a$$ b: c`,
+		out: []Node{
+			&Rule{
+				Target:        SimpleMakeString("a$ b", NoPos),
+				Prerequisites: SimpleMakeString("c", NoPos),
+			},
+		},
+	},
+}
+
+func TestParse(t *testing.T) {
+	for _, test := range parserTestCases {
+		t.Run(test.name, func(t *testing.T) {
+			p := NewParser(test.name, bytes.NewBufferString(test.in))
+			got, errs := p.Parse()
+
+			if len(errs) != 0 {
+				t.Fatalf("Unexpected errors while parsing: %v", errs)
+			}
+
+			if len(got) != len(test.out) {
+				t.Fatalf("length mismatch, expected %d nodes, got %d", len(test.out), len(got))
+			}
+
+			for i := range got {
+				if got[i].Dump() != test.out[i].Dump() {
+					t.Errorf("incorrect node %d:\nexpected: %#v (%s)\n     got: %#v (%s)",
+						i, test.out[i], test.out[i].Dump(), got[i], got[i].Dump())
+				}
+			}
+		})
+	}
+}
diff --git a/androidmk/parser/scope.go b/androidmk/parser/scope.go
index 7a514fa..167e470 100644
--- a/androidmk/parser/scope.go
+++ b/androidmk/parser/scope.go
@@ -71,7 +71,7 @@
 
 func init() {
 	builtinScope := make(map[string]string)
-	builtinScope["__builtin_dollar"] = "$"
+	builtinScope[builtinDollar] = "$"
 }
 
 func (v Variable) EvalFunction(scope Scope) (string, bool) {
diff --git a/bpf/Android.bp b/bpf/Android.bp
new file mode 100644
index 0000000..7bd4d44
--- /dev/null
+++ b/bpf/Android.bp
@@ -0,0 +1,30 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// 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.
+//
+
+bootstrap_go_package {
+    name: "soong-bpf",
+    pkgPath: "android/soong/bpf",
+    deps: [
+        "blueprint",
+        "blueprint-proptools",
+        "soong-android",
+        "soong-cc-config",
+    ],
+    srcs: [
+        "bpf.go",
+    ],
+    pluginFor: ["soong_build"],
+}
diff --git a/bpf/bpf.go b/bpf/bpf.go
new file mode 100644
index 0000000..fa1f3ff
--- /dev/null
+++ b/bpf/bpf.go
@@ -0,0 +1,135 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// 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 bpf
+
+import (
+	"fmt"
+	"io"
+	"strings"
+
+	"android/soong/android"
+	_ "android/soong/cc/config"
+
+	"github.com/google/blueprint"
+)
+
+func init() {
+	android.RegisterModuleType("bpf", bpfFactory)
+	pctx.Import("android/soong/cc/config")
+}
+
+var (
+	pctx = android.NewPackageContext("android/soong/bpf")
+
+	cc = pctx.AndroidGomaStaticRule("cc",
+		blueprint.RuleParams{
+			Depfile:     "${out}.d",
+			Deps:        blueprint.DepsGCC,
+			Command:     "$ccCmd --target=bpf -c $cFlags -MD -MF ${out}.d -o $out $in",
+			CommandDeps: []string{"$ccCmd"},
+		},
+		"ccCmd", "cFlags")
+)
+
+type BpfProperties struct {
+	Srcs         []string
+	Cflags       []string
+	Include_dirs []string
+}
+
+type bpf struct {
+	android.ModuleBase
+
+	properties BpfProperties
+
+	objs android.Paths
+}
+
+func (bpf *bpf) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	cflags := []string{
+		"-nostdlibinc",
+		"-O2",
+		"-isystem bionic/libc/include",
+		"-isystem bionic/libc/kernel/uapi",
+		// The architecture doesn't matter here, but asm/types.h is included by linux/types.h.
+		"-isystem bionic/libc/kernel/uapi/asm-arm64",
+		"-isystem bionic/libc/kernel/android/uapi",
+		"-I " + ctx.ModuleDir(),
+	}
+
+	for _, dir := range android.PathsForSource(ctx, bpf.properties.Include_dirs) {
+		cflags = append(cflags, "-I "+dir.String())
+	}
+
+	cflags = append(cflags, bpf.properties.Cflags...)
+
+	srcs := ctx.ExpandSources(bpf.properties.Srcs, nil)
+
+	for _, src := range srcs {
+		obj := android.ObjPathWithExt(ctx, "", src, "o")
+
+		ctx.Build(pctx, android.BuildParams{
+			Rule:   cc,
+			Input:  src,
+			Output: obj,
+			Args: map[string]string{
+				"cFlags": strings.Join(cflags, " "),
+				"ccCmd":  "${config.ClangBin}/clang",
+			},
+		})
+
+		bpf.objs = append(bpf.objs, obj)
+	}
+}
+
+func (bpf *bpf) DepsMutator(ctx android.BottomUpMutatorContext) {
+	android.ExtractSourcesDeps(ctx, bpf.properties.Srcs)
+}
+
+func (bpf *bpf) AndroidMk() android.AndroidMkData {
+	return android.AndroidMkData{
+		Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
+			var names []string
+			fmt.Fprintln(w)
+			fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
+			fmt.Fprintln(w)
+			for _, obj := range bpf.objs {
+				objName := name + "_" + obj.Base()
+				names = append(names, objName)
+				fmt.Fprintln(w, "include $(CLEAR_VARS)")
+				fmt.Fprintln(w, "LOCAL_MODULE := ", objName)
+				fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", obj.String())
+				fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", obj.Base())
+				fmt.Fprintln(w, "LOCAL_MODULE_CLASS := ETC")
+				fmt.Fprintln(w, "LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/bpf")
+				fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
+				fmt.Fprintln(w)
+			}
+			fmt.Fprintln(w, "include $(CLEAR_VARS)")
+			fmt.Fprintln(w, "LOCAL_MODULE := ", name)
+			fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES :=", strings.Join(names, " "))
+			fmt.Fprintln(w, "include $(BUILD_PHONY_PACKAGE)")
+		},
+	}
+}
+
+func bpfFactory() android.Module {
+	module := &bpf{}
+
+	module.AddProperties(&module.properties)
+
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	return module
+}
diff --git a/bpfix/bpfix/bpfix.go b/bpfix/bpfix/bpfix.go
index e4d4e34..056a01b 100644
--- a/bpfix/bpfix/bpfix.go
+++ b/bpfix/bpfix/bpfix.go
@@ -22,6 +22,7 @@
 	"fmt"
 	"io"
 	"path/filepath"
+	"strings"
 
 	"github.com/google/blueprint/parser"
 )
@@ -44,10 +45,51 @@
 // A FixRequest specifies the details of which fixes to apply to an individual file
 // A FixRequest doesn't specify whether to do a dry run or where to write the results; that's in cmd/bpfix.go
 type FixRequest struct {
-	simplifyKnownRedundantVariables           bool
-	rewriteIncorrectAndroidmkPrebuilts        bool
-	rewriteIncorrectAndroidmkAndroidLibraries bool
-	mergeMatchingModuleProperties             bool
+	steps []fixStep
+}
+
+type fixStep struct {
+	name string
+	fix  func(f *Fixer) error
+}
+
+var fixSteps = []fixStep{
+	{
+		name: "simplifyKnownRedundantVariables",
+		fix:  runPatchListMod(simplifyKnownPropertiesDuplicatingEachOther),
+	},
+	{
+		name: "rewriteIncorrectAndroidmkPrebuilts",
+		fix:  rewriteIncorrectAndroidmkPrebuilts,
+	},
+	{
+		name: "rewriteIncorrectAndroidmkAndroidLibraries",
+		fix:  rewriteIncorrectAndroidmkAndroidLibraries,
+	},
+	{
+		name: "rewriteTestModuleTypes",
+		fix:  rewriteTestModuleTypes,
+	},
+	{
+		name: "rewriteAndroidmkJavaLibs",
+		fix:  rewriteAndroidmkJavaLibs,
+	},
+	{
+		name: "rewriteJavaStaticLibs",
+		fix:  rewriteJavaStaticLibs,
+	},
+	{
+		name: "mergeMatchingModuleProperties",
+		fix:  runPatchListMod(mergeMatchingModuleProperties),
+	},
+	{
+		name: "reorderCommonProperties",
+		fix:  runPatchListMod(reorderCommonProperties),
+	},
+	{
+		name: "removeTags",
+		fix:  runPatchListMod(removeTags),
+	},
 }
 
 func NewFixRequest() FixRequest {
@@ -55,11 +97,8 @@
 }
 
 func (r FixRequest) AddAll() (result FixRequest) {
-	result = r
-	result.simplifyKnownRedundantVariables = true
-	result.rewriteIncorrectAndroidmkPrebuilts = true
-	result.rewriteIncorrectAndroidmkAndroidLibraries = true
-	result.mergeMatchingModuleProperties = true
+	result.steps = append([]fixStep(nil), r.steps...)
+	result.steps = append(result.steps, fixSteps...)
 	return result
 }
 
@@ -143,28 +182,8 @@
 }
 
 func (f *Fixer) fixTreeOnce(config FixRequest) error {
-	if config.simplifyKnownRedundantVariables {
-		err := f.simplifyKnownPropertiesDuplicatingEachOther()
-		if err != nil {
-			return err
-		}
-	}
-	if config.rewriteIncorrectAndroidmkPrebuilts {
-		err := f.rewriteIncorrectAndroidmkPrebuilts()
-		if err != nil {
-			return err
-		}
-	}
-
-	if config.rewriteIncorrectAndroidmkAndroidLibraries {
-		err := f.rewriteIncorrectAndroidmkAndroidLibraries()
-		if err != nil {
-			return err
-		}
-	}
-
-	if config.mergeMatchingModuleProperties {
-		err := f.mergeMatchingModuleProperties()
+	for _, fix := range config.steps {
+		err := fix.fix(f)
 		if err != nil {
 			return err
 		}
@@ -172,12 +191,13 @@
 	return nil
 }
 
-func (f *Fixer) simplifyKnownPropertiesDuplicatingEachOther() error {
+func simplifyKnownPropertiesDuplicatingEachOther(mod *parser.Module, buf []byte, patchList *parser.PatchList) error {
 	// remove from local_include_dirs anything in export_include_dirs
-	return f.removeMatchingModuleListProperties("export_include_dirs", "local_include_dirs")
+	return removeMatchingModuleListProperties(mod, patchList,
+		"export_include_dirs", "local_include_dirs")
 }
 
-func (f *Fixer) rewriteIncorrectAndroidmkPrebuilts() error {
+func rewriteIncorrectAndroidmkPrebuilts(f *Fixer) error {
 	for _, def := range f.tree.Defs {
 		mod, ok := def.(*parser.Module)
 		if !ok {
@@ -213,19 +233,23 @@
 	return nil
 }
 
-func (f *Fixer) rewriteIncorrectAndroidmkAndroidLibraries() error {
+func rewriteIncorrectAndroidmkAndroidLibraries(f *Fixer) error {
 	for _, def := range f.tree.Defs {
 		mod, ok := def.(*parser.Module)
 		if !ok {
 			continue
 		}
 
+		if !strings.HasPrefix(mod.Type, "java_") && !strings.HasPrefix(mod.Type, "android_") {
+			continue
+		}
+
 		hasAndroidLibraries := hasNonEmptyLiteralListProperty(mod, "android_libs")
 		hasStaticAndroidLibraries := hasNonEmptyLiteralListProperty(mod, "android_static_libs")
 		hasResourceDirs := hasNonEmptyLiteralListProperty(mod, "resource_dirs")
 
 		if hasAndroidLibraries || hasStaticAndroidLibraries || hasResourceDirs {
-			if mod.Type == "java_library_static" {
+			if mod.Type == "java_library_static" || mod.Type == "java_library" {
 				mod.Type = "android_library"
 			}
 		}
@@ -244,42 +268,264 @@
 	return nil
 }
 
-func (f *Fixer) mergeMatchingModuleProperties() error {
-	// Make sure all the offsets are accurate
-	buf, err := f.reparse()
-	if err != nil {
-		return err
-	}
-
-	var patchlist parser.PatchList
+// rewriteTestModuleTypes looks for modules that are identifiable as tests but for which Make doesn't have a separate
+// module class, and moves them to the appropriate Soong module type.
+func rewriteTestModuleTypes(f *Fixer) error {
 	for _, def := range f.tree.Defs {
 		mod, ok := def.(*parser.Module)
 		if !ok {
 			continue
 		}
 
-		err := mergeMatchingProperties(&mod.Properties, buf, &patchlist)
+		if !strings.HasPrefix(mod.Type, "java_") && !strings.HasPrefix(mod.Type, "android_") {
+			continue
+		}
+
+		hasInstrumentationFor := hasNonEmptyLiteralStringProperty(mod, "instrumentation_for")
+		tags, _ := getLiteralListPropertyValue(mod, "tags")
+
+		var hasTestsTag bool
+		for _, tag := range tags {
+			if tag == "tests" {
+				hasTestsTag = true
+			}
+		}
+
+		isTest := hasInstrumentationFor || hasTestsTag
+
+		if isTest {
+			switch mod.Type {
+			case "android_app":
+				mod.Type = "android_test"
+			case "java_library", "java_library_installable":
+				mod.Type = "java_test"
+			case "java_library_host":
+				mod.Type = "java_test_host"
+			}
+		}
+	}
+
+	return nil
+}
+
+// rewriteJavaStaticLibs rewrites java_library_static into java_library
+func rewriteJavaStaticLibs(f *Fixer) error {
+	for _, def := range f.tree.Defs {
+		mod, ok := def.(*parser.Module)
+		if !ok {
+			continue
+		}
+
+		if mod.Type == "java_library_static" {
+			mod.Type = "java_library"
+		}
+	}
+
+	return nil
+}
+
+// rewriteAndroidmkJavaLibs rewrites java_library_installable into java_library plus installable: true
+func rewriteAndroidmkJavaLibs(f *Fixer) error {
+	for _, def := range f.tree.Defs {
+		mod, ok := def.(*parser.Module)
+		if !ok {
+			continue
+		}
+
+		if mod.Type != "java_library_installable" {
+			continue
+		}
+
+		mod.Type = "java_library"
+
+		_, hasInstallable := mod.GetProperty("installable")
+		if !hasInstallable {
+			prop := &parser.Property{
+				Name: "installable",
+				Value: &parser.Bool{
+					Value: true,
+				},
+			}
+			mod.Properties = append(mod.Properties, prop)
+		}
+	}
+
+	return nil
+}
+
+func runPatchListMod(modFunc func(mod *parser.Module, buf []byte, patchlist *parser.PatchList) error) func(*Fixer) error {
+	return func(f *Fixer) error {
+		// Make sure all the offsets are accurate
+		buf, err := f.reparse()
+		if err != nil {
+			return err
+		}
+
+		var patchlist parser.PatchList
+		for _, def := range f.tree.Defs {
+			mod, ok := def.(*parser.Module)
+			if !ok {
+				continue
+			}
+
+			err := modFunc(mod, buf, &patchlist)
+			if err != nil {
+				return err
+			}
+		}
+
+		newBuf := new(bytes.Buffer)
+		err = patchlist.Apply(bytes.NewReader(buf), newBuf)
+		if err != nil {
+			return err
+		}
+
+		// Save a copy of the buffer to print for errors below
+		bufCopy := append([]byte(nil), newBuf.Bytes()...)
+
+		newTree, err := parse(f.tree.Name, newBuf)
+		if err != nil {
+			return fmt.Errorf("Failed to parse: %v\nBuffer:\n%s", err, string(bufCopy))
+		}
+
+		f.tree = newTree
+
+		return nil
+	}
+}
+
+var commonPropertyPriorities = []string{
+	"name",
+	"defaults",
+	"device_supported",
+	"host_supported",
+	"installable",
+}
+
+func reorderCommonProperties(mod *parser.Module, buf []byte, patchlist *parser.PatchList) error {
+	if len(mod.Properties) == 0 {
+		return nil
+	}
+
+	pos := mod.LBracePos.Offset + 1
+	stage := ""
+
+	for _, name := range commonPropertyPriorities {
+		idx := propertyIndex(mod.Properties, name)
+		if idx == -1 {
+			continue
+		}
+		if idx == 0 {
+			err := patchlist.Add(pos, pos, stage)
+			if err != nil {
+				return err
+			}
+			stage = ""
+
+			pos = mod.Properties[0].End().Offset + 1
+			mod.Properties = mod.Properties[1:]
+			continue
+		}
+
+		prop := mod.Properties[idx]
+		mod.Properties = append(mod.Properties[:idx], mod.Properties[idx+1:]...)
+
+		stage += string(buf[prop.Pos().Offset : prop.End().Offset+1])
+
+		err := patchlist.Add(prop.Pos().Offset, prop.End().Offset+2, "")
 		if err != nil {
 			return err
 		}
 	}
 
-	newBuf := new(bytes.Buffer)
-	err = patchlist.Apply(bytes.NewReader(buf), newBuf)
-	if err != nil {
-		return err
+	if stage != "" {
+		err := patchlist.Add(pos, pos, stage)
+		if err != nil {
+			return err
+		}
 	}
 
-	newTree, err := parse(f.tree.Name, newBuf)
-	if err != nil {
-		return err
-	}
-
-	f.tree = newTree
-
 	return nil
 }
 
+func removeTags(mod *parser.Module, buf []byte, patchlist *parser.PatchList) error {
+	prop, ok := mod.GetProperty("tags")
+	if !ok {
+		return nil
+	}
+	list, ok := prop.Value.(*parser.List)
+	if !ok {
+		return nil
+	}
+
+	replaceStr := ""
+
+	for _, item := range list.Values {
+		str, ok := item.(*parser.String)
+		if !ok {
+			replaceStr += fmt.Sprintf("// ERROR: Unable to parse tag %q\n", item)
+			continue
+		}
+
+		switch str.Value {
+		case "optional":
+			continue
+		case "debug":
+			replaceStr += `// WARNING: Module tags are not supported in Soong.
+				// Add this module to PRODUCT_PACKAGES_DEBUG in your product file if you want to
+				// force installation for -userdebug and -eng builds.
+				`
+		case "eng":
+			replaceStr += `// WARNING: Module tags are not supported in Soong.
+				// Add this module to PRODUCT_PACKAGES_ENG in your product file if you want to
+				// force installation for -eng builds.
+				`
+		case "tests":
+			switch {
+			case strings.Contains(mod.Type, "cc_test"),
+				strings.Contains(mod.Type, "cc_library_static"),
+				strings.Contains(mod.Type, "java_test"),
+				mod.Type == "android_test":
+				continue
+			case strings.Contains(mod.Type, "cc_lib"):
+				replaceStr += `// WARNING: Module tags are not supported in Soong.
+					// To make a shared library only for tests, use the "cc_test_library" module
+					// type. If you don't use gtest, set "gtest: false".
+					`
+			case strings.Contains(mod.Type, "cc_bin"):
+				replaceStr += `// WARNING: Module tags are not supported in Soong.
+					// For native test binaries, use the "cc_test" module type. Some differences:
+					//  - If you don't use gtest, set "gtest: false"
+					//  - Binaries will be installed into /data/nativetest[64]/<name>/<name>
+					//  - Both 32 & 64 bit versions will be built (as appropriate)
+					`
+			case strings.Contains(mod.Type, "java_lib"):
+				replaceStr += `// WARNING: Module tags are not supported in Soong.
+					// For JUnit or similar tests, use the "java_test" module type. A dependency on
+					// Junit will be added by default, if it is using some other runner, set "junit: false".
+					`
+			case mod.Type == "android_app":
+				replaceStr += `// WARNING: Module tags are not supported in Soong.
+					// For JUnit or instrumentataion app tests, use the "android_test" module type.
+					`
+			default:
+				replaceStr += `// WARNING: Module tags are not supported in Soong.
+					// In most cases, tests are now identified by their module type:
+					// cc_test, java_test, python_test
+					`
+			}
+		default:
+			replaceStr += fmt.Sprintf("// WARNING: Unknown module tag %q\n", str.Value)
+		}
+	}
+
+	return patchlist.Add(prop.Pos().Offset, prop.End().Offset+2, replaceStr)
+}
+
+func mergeMatchingModuleProperties(mod *parser.Module, buf []byte, patchlist *parser.PatchList) error {
+	return mergeMatchingProperties(&mod.Properties, buf, patchlist)
+}
+
 func mergeMatchingProperties(properties *[]*parser.Property, buf []byte, patchlist *parser.PatchList) error {
 	seen := make(map[string]*parser.Property)
 	for i := 0; i < len(*properties); i++ {
@@ -354,7 +600,7 @@
 }
 
 // removes from <items> every item present in <removals>
-func filterExpressionList(items *parser.List, removals *parser.List) {
+func filterExpressionList(patchList *parser.PatchList, items *parser.List, removals *parser.List) {
 	writeIndex := 0
 	for _, item := range items.Values {
 		included := true
@@ -371,28 +617,39 @@
 		if included {
 			items.Values[writeIndex] = item
 			writeIndex++
+		} else {
+			patchList.Add(item.Pos().Offset, item.End().Offset+2, "")
 		}
 	}
 	items.Values = items.Values[:writeIndex]
 }
 
 // Remove each modules[i].Properties[<legacyName>][j] that matches a modules[i].Properties[<canonicalName>][k]
-func (f *Fixer) removeMatchingModuleListProperties(canonicalName string, legacyName string) error {
-	for _, def := range f.tree.Defs {
-		mod, ok := def.(*parser.Module)
-		if !ok {
-			continue
-		}
-		legacyList, ok := getLiteralListProperty(mod, legacyName)
-		if !ok {
-			continue
-		}
-		canonicalList, ok := getLiteralListProperty(mod, canonicalName)
-		if !ok {
-			continue
-		}
-		filterExpressionList(legacyList, canonicalList)
+func removeMatchingModuleListProperties(mod *parser.Module, patchList *parser.PatchList, canonicalName string, legacyName string) error {
+	legacyProp, ok := mod.GetProperty(legacyName)
+	if !ok {
+		return nil
 	}
+	legacyList, ok := legacyProp.Value.(*parser.List)
+	if !ok || len(legacyList.Values) == 0 {
+		return nil
+	}
+	canonicalList, ok := getLiteralListProperty(mod, canonicalName)
+	if !ok {
+		return nil
+	}
+
+	localPatches := parser.PatchList{}
+	filterExpressionList(&localPatches, legacyList, canonicalList)
+
+	if len(legacyList.Values) == 0 {
+		patchList.Add(legacyProp.Pos().Offset, legacyProp.End().Offset+2, "")
+	} else {
+		for _, p := range localPatches {
+			patchList.Add(p.Start, p.End, p.Replacement)
+		}
+	}
+
 	return nil
 }
 
@@ -401,6 +658,11 @@
 	return found && len(list.Values) > 0
 }
 
+func hasNonEmptyLiteralStringProperty(mod *parser.Module, name string) bool {
+	s, found := getLiteralStringPropertyValue(mod, name)
+	return found && len(s) > 0
+}
+
 func getLiteralListProperty(mod *parser.Module, name string) (list *parser.List, found bool) {
 	prop, ok := mod.GetProperty(name)
 	if !ok {
@@ -410,6 +672,49 @@
 	return list, ok
 }
 
+func getLiteralListPropertyValue(mod *parser.Module, name string) (list []string, found bool) {
+	listValue, ok := getLiteralListProperty(mod, name)
+	if !ok {
+		return nil, false
+	}
+	for _, v := range listValue.Values {
+		stringValue, ok := v.(*parser.String)
+		if !ok {
+			return nil, false
+		}
+		list = append(list, stringValue.Value)
+	}
+
+	return list, true
+}
+
+func getLiteralStringProperty(mod *parser.Module, name string) (s *parser.String, found bool) {
+	prop, ok := mod.GetProperty(name)
+	if !ok {
+		return nil, false
+	}
+	s, ok = prop.Value.(*parser.String)
+	return s, ok
+}
+
+func getLiteralStringPropertyValue(mod *parser.Module, name string) (s string, found bool) {
+	stringValue, ok := getLiteralStringProperty(mod, name)
+	if !ok {
+		return "", false
+	}
+
+	return stringValue.Value, true
+}
+
+func propertyIndex(props []*parser.Property, propertyName string) int {
+	for i, prop := range props {
+		if prop.Name == propertyName {
+			return i
+		}
+	}
+	return -1
+}
+
 func renameProperty(mod *parser.Module, from, to string) {
 	for _, prop := range mod.Properties {
 		if prop.Name == from {
diff --git a/bpfix/bpfix/bpfix_test.go b/bpfix/bpfix/bpfix_test.go
index 51708eb..16dfce0 100644
--- a/bpfix/bpfix/bpfix_test.go
+++ b/bpfix/bpfix/bpfix_test.go
@@ -66,27 +66,31 @@
 	fixer := NewFixer(tree)
 
 	// apply simplifications
-	err := fixer.simplifyKnownPropertiesDuplicatingEachOther()
+	err := runPatchListMod(simplifyKnownPropertiesDuplicatingEachOther)(fixer)
 	if len(errs) > 0 {
 		t.Fatal(err)
 	}
 
 	// lookup legacy property
 	mod := fixer.tree.Defs[0].(*parser.Module)
-	_, found := mod.GetProperty("local_include_dirs")
-	if !found {
-		t.Fatalf("failed to include key local_include_dirs in parse tree")
+
+	expectedResultString := fmt.Sprintf("%q", expectedResult)
+	if expectedResult == nil {
+		expectedResultString = "unset"
 	}
 
 	// check that the value for the legacy property was updated to the correct value
 	errorHeader := fmt.Sprintf("\nFailed to correctly simplify key 'local_include_dirs' in the presence of 'export_include_dirs.'\n"+
 		"original local_include_dirs: %q\n"+
 		"original export_include_dirs: %q\n"+
-		"expected result: %q\n"+
+		"expected result: %s\n"+
 		"actual result: ",
-		local_include_dirs, export_include_dirs, expectedResult)
-	result, ok := mod.GetProperty("local_include_dirs")
-	if !ok {
+		local_include_dirs, export_include_dirs, expectedResultString)
+	result, found := mod.GetProperty("local_include_dirs")
+	if !found {
+		if expectedResult == nil {
+			return
+		}
 		t.Fatal(errorHeader + "property not found")
 	}
 
@@ -95,6 +99,10 @@
 		t.Fatalf("%sproperty is not a list: %v", errorHeader, listResult)
 	}
 
+	if expectedResult == nil {
+		t.Fatalf("%sproperty exists: %v", errorHeader, listResult)
+	}
+
 	actualExpressions := listResult.Values
 	actualValues := make([]string, 0)
 	for _, expr := range actualExpressions {
@@ -109,7 +117,7 @@
 
 func TestSimplifyKnownVariablesDuplicatingEachOther(t *testing.T) {
 	// TODO use []Expression{} once buildTree above can support it (which is after b/38325146 is done)
-	implFilterListTest(t, []string{"include"}, []string{"include"}, []string{})
+	implFilterListTest(t, []string{"include"}, []string{"include"}, nil)
 	implFilterListTest(t, []string{"include1"}, []string{"include2"}, []string{"include1"})
 	implFilterListTest(t, []string{"include1", "include2", "include3", "include4"}, []string{"include2"},
 		[]string{"include1", "include3", "include4"})
@@ -117,6 +125,49 @@
 	implFilterListTest(t, []string{}, []string{}, []string{})
 }
 
+func runPass(t *testing.T, in, out string, innerTest func(*Fixer) error) {
+	expected, err := Reformat(out)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	in, err = Reformat(in)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	tree, errs := parser.Parse("<testcase>", bytes.NewBufferString(in), parser.NewScope(nil))
+	if errs != nil {
+		t.Fatal(errs)
+	}
+
+	fixer := NewFixer(tree)
+
+	got := ""
+	prev := "foo"
+	passes := 0
+	for got != prev && passes < 10 {
+		err := innerTest(fixer)
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		out, err := parser.Print(fixer.tree)
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		prev = got
+		got = string(out)
+		passes++
+	}
+
+	if got != expected {
+		t.Errorf("output didn't match:\ninput:\n%s\n\nexpected:\n%s\ngot:\n%s\n",
+			in, expected, got)
+	}
+}
+
 func TestMergeMatchingProperties(t *testing.T) {
 	tests := []struct {
 		name string
@@ -199,47 +250,308 @@
 
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
-			expected, err := Reformat(test.out)
-			if err != nil {
-				t.Error(err)
-			}
+			runPass(t, test.in, test.out, func(fixer *Fixer) error {
+				return runPatchListMod(mergeMatchingModuleProperties)(fixer)
+			})
+		})
+	}
+}
 
-			in, err := Reformat(test.in)
-			if err != nil {
-				t.Error(err)
-			}
-
-			tree, errs := parser.Parse("<testcase>", bytes.NewBufferString(in), parser.NewScope(nil))
-			if errs != nil {
-				t.Fatal(errs)
-			}
-
-			fixer := NewFixer(tree)
-
-			got := ""
-			prev := "foo"
-			passes := 0
-			for got != prev && passes < 10 {
-				err := fixer.mergeMatchingModuleProperties()
-				if err != nil {
-					t.Fatal(err)
+func TestReorderCommonProperties(t *testing.T) {
+	var tests = []struct {
+		name string
+		in   string
+		out  string
+	}{
+		{
+			name: "empty",
+			in:   `cc_library {}`,
+			out:  `cc_library {}`,
+		},
+		{
+			name: "only priority",
+			in: `
+				cc_library {
+					name: "foo",
 				}
-
-				out, err := parser.Print(fixer.tree)
-				if err != nil {
-					t.Fatal(err)
+			`,
+			out: `
+				cc_library {
+					name: "foo",
 				}
+			`,
+		},
+		{
+			name: "already in order",
+			in: `
+				cc_library {
+					name: "foo",
+					defaults: ["bar"],
+				}
+			`,
+			out: `
+				cc_library {
+					name: "foo",
+					defaults: ["bar"],
+				}
+			`,
+		},
+		{
+			name: "reorder only priority",
+			in: `
+				cc_library {
+					defaults: ["bar"],
+					name: "foo",
+				}
+			`,
+			out: `
+				cc_library {
+					name: "foo",
+					defaults: ["bar"],
+				}
+			`,
+		},
+		{
+			name: "reorder",
+			in: `
+				cc_library {
+					name: "foo",
+					srcs: ["a.c"],
+					host_supported: true,
+					defaults: ["bar"],
+					shared_libs: ["baz"],
+				}
+			`,
+			out: `
+				cc_library {
+					name: "foo",
+					defaults: ["bar"],
+					host_supported: true,
+					srcs: ["a.c"],
+					shared_libs: ["baz"],
+				}
+			`,
+		},
+	}
 
-				prev = got
-				got = string(out)
-				passes++
-			}
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			runPass(t, test.in, test.out, func(fixer *Fixer) error {
+				return runPatchListMod(reorderCommonProperties)(fixer)
+			})
+		})
+	}
+}
 
-			if got != expected {
-				t.Errorf("failed testcase '%s'\ninput:\n%s\n\nexpected:\n%s\ngot:\n%s\n",
-					test.name, in, expected, got)
-			}
+func TestRemoveMatchingModuleListProperties(t *testing.T) {
+	var tests = []struct {
+		name string
+		in   string
+		out  string
+	}{
+		{
+			name: "simple",
+			in: `
+				cc_library {
+					name: "foo",
+					foo: ["a"],
+					bar: ["a"],
+				}
+			`,
+			out: `
+				cc_library {
+					name: "foo",
+					bar: ["a"],
+				}
+			`,
+		},
+		{
+			name: "long",
+			in: `
+				cc_library {
+					name: "foo",
+					foo: [
+						"a",
+						"b",
+					],
+					bar: ["a"],
+				}
+			`,
+			out: `
+				cc_library {
+					name: "foo",
+					foo: [
+						"b",
+					],
+					bar: ["a"],
+				}
+			`,
+		},
+		{
+			name: "long fully removed",
+			in: `
+				cc_library {
+					name: "foo",
+					foo: [
+						"a",
+					],
+					bar: ["a"],
+				}
+			`,
+			out: `
+				cc_library {
+					name: "foo",
+					bar: ["a"],
+				}
+			`,
+		},
+		{
+			name: "comment",
+			in: `
+				cc_library {
+					name: "foo",
 
+					// comment
+					foo: ["a"],
+
+					bar: ["a"],
+				}
+			`,
+			out: `
+				cc_library {
+					name: "foo",
+
+					// comment
+
+					bar: ["a"],
+				}
+			`,
+		},
+		{
+			name: "inner comment",
+			in: `
+				cc_library {
+					name: "foo",
+					foo: [
+						// comment
+						"a",
+					],
+					bar: ["a"],
+				}
+			`,
+			out: `
+				cc_library {
+					name: "foo",
+					bar: ["a"],
+				}
+			`,
+		},
+		{
+			name: "eol comment",
+			in: `
+				cc_library {
+					name: "foo",
+					foo: ["a"], // comment
+					bar: ["a"],
+				}
+			`,
+			out: `
+				cc_library {
+					name: "foo",
+					// comment
+					bar: ["a"],
+				}
+			`,
+		},
+		{
+			name: "eol comment with blank lines",
+			in: `
+				cc_library {
+					name: "foo",
+
+					foo: ["a"], // comment
+
+					// bar
+					bar: ["a"],
+				}
+			`,
+			out: `
+				cc_library {
+					name: "foo",
+
+					// comment
+
+					// bar
+					bar: ["a"],
+				}
+			`,
+		},
+	}
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			runPass(t, test.in, test.out, func(fixer *Fixer) error {
+				return runPatchListMod(func(mod *parser.Module, buf []byte, patchList *parser.PatchList) error {
+					return removeMatchingModuleListProperties(mod, patchList, "bar", "foo")
+				})(fixer)
+			})
+		})
+	}
+}
+
+func TestReplaceJavaStaticLibs(t *testing.T) {
+	tests := []struct {
+		name string
+		in   string
+		out  string
+	}{
+		{
+			name: "static lib",
+			in: `
+				java_library_static {
+					name: "foo",
+				}
+			`,
+			out: `
+				java_library {
+					name: "foo",
+				}
+			`,
+		},
+		{
+			name: "java lib",
+			in: `
+				java_library {
+					name: "foo",
+				}
+			`,
+			out: `
+				java_library {
+					name: "foo",
+				}
+			`,
+		},
+		{
+			name: "java installable lib",
+			in: `
+				java_library {
+					name: "foo",
+					installable: true,
+				}
+			`,
+			out: `
+				java_library {
+					name: "foo",
+					installable: true,
+				}
+			`,
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			runPass(t, test.in, test.out, func(fixer *Fixer) error {
+				return rewriteJavaStaticLibs(fixer)
+			})
 		})
 	}
 }
diff --git a/bpfix/cmd/bpfix.go b/bpfix/cmd/bpfix.go
index 2fde383..ccdae16 100644
--- a/bpfix/cmd/bpfix.go
+++ b/bpfix/cmd/bpfix.go
@@ -65,7 +65,7 @@
 	if err != nil {
 		return err
 	}
-	r := bytes.NewBuffer(src)
+	r := bytes.NewBuffer(append([]byte(nil), src...))
 	file, errs := parser.Parse(filename, r, parser.NewScope(nil))
 	if len(errs) > 0 {
 		for _, err := range errs {
diff --git a/cc/androidmk.go b/cc/androidmk.go
index cdd4a5a..263f0f3 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -24,7 +24,8 @@
 )
 
 var (
-	vendorSuffix = ".vendor"
+	vendorSuffix   = ".vendor"
+	recoverySuffix = ".recovery"
 )
 
 type AndroidMkContext interface {
@@ -70,7 +71,7 @@
 					fmt.Fprintln(w, "LOCAL_SHARED_LIBRARIES := "+strings.Join(c.Properties.AndroidMkSharedLibs, " "))
 				}
 				if c.Target().Os == android.Android &&
-					String(c.Properties.Sdk_version) != "" && !c.useVndk() {
+					String(c.Properties.Sdk_version) != "" && !c.useVndk() && !c.inRecovery() {
 					fmt.Fprintln(w, "LOCAL_SDK_VERSION := "+String(c.Properties.Sdk_version))
 					fmt.Fprintln(w, "LOCAL_NDK_STL_VARIANT := none")
 				} else {
@@ -99,6 +100,8 @@
 		// .vendor suffix is added only when we will have two variants: core and vendor.
 		// The suffix is not added for vendor-only module.
 		ret.SubName += vendorSuffix
+	} else if c.inRecovery() && !c.onlyInRecovery() {
+		ret.SubName += recoverySuffix
 	}
 
 	return ret
@@ -345,7 +348,7 @@
 
 func (c *llndkStubDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
 	ret.Class = "SHARED_LIBRARIES"
-	ret.SubName = ".vendor"
+	ret.SubName = vendorSuffix
 
 	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
 		c.libraryDecorator.androidMkWriteExportedFlags(w)
diff --git a/cc/binary.go b/cc/binary.go
index 9e7b70b..82e1941 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -41,9 +41,6 @@
 	// extension (if any) appended
 	Symlinks []string `android:"arch_variant"`
 
-	// do not pass -pie
-	No_pie *bool `android:"arch_variant"`
-
 	DynamicLinker string `blueprint:"mutated"`
 
 	// Names of modules to be overridden. Listed modules can only be other binaries
@@ -216,9 +213,6 @@
 	if ctx.Host() && !binary.static() {
 		if !ctx.Config().IsEnvTrue("DISABLE_HOST_PIE") {
 			flags.LdFlags = append(flags.LdFlags, "-pie")
-			if ctx.Windows() {
-				flags.LdFlags = append(flags.LdFlags, "-Wl,-e_mainCRTStartup")
-			}
 		}
 	}
 
@@ -318,6 +312,10 @@
 	builderFlags := flagsToBuilderFlags(flags)
 
 	if binary.stripper.needsStrip(ctx) {
+		// b/80093681, GNU strip/objcopy bug.
+		// Use llvm-{strip,objcopy} when clang lld is used.
+		builderFlags.stripUseLlvmStrip =
+			flags.Clang && binary.baseLinker.useClangLld(ctx)
 		strippedOutputFile := outputFile
 		outputFile = android.PathForModuleOut(ctx, "unstripped", fileName)
 		binary.stripper.strip(ctx, outputFile, strippedOutputFile, builderFlags)
diff --git a/cc/builder.go b/cc/builder.go
index cb09d09..51d3195 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -117,7 +117,7 @@
 		blueprint.RuleParams{
 			Depfile:     "${out}.d",
 			Deps:        blueprint.DepsGCC,
-			Command:     "CROSS_COMPILE=$crossCompile XZ=$xzCmd $stripPath ${args} -i ${in} -o ${out} -d ${out}.d",
+			Command:     "CROSS_COMPILE=$crossCompile XZ=$xzCmd CLANG_BIN=${config.ClangBin} $stripPath ${args} -i ${in} -o ${out} -d ${out}.d",
 			CommandDeps: []string{"$stripPath", "$xzCmd"},
 		},
 		"args", "crossCompile")
@@ -200,8 +200,8 @@
 
 	sAbiDiff = pctx.AndroidRuleFunc("sAbiDiff",
 		func(ctx android.PackageRuleContext) blueprint.RuleParams {
-
-			commandStr := "($sAbiDiffer $allowFlags -lib $libName -arch $arch -check-all-apis -o ${out} -new $in -old $referenceDump)"
+			// TODO(b/78139997): Add -check-all-apis back
+			commandStr := "($sAbiDiffer $allowFlags -lib $libName -arch $arch -o ${out} -new $in -old $referenceDump)"
 			distAbiDiffDir := android.PathForDist(ctx, "abidiffs")
 			commandStr += "|| (echo ' ---- Please update abi references by running $$ANDROID_BUILD_TOP/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l ${libName} ----'"
 			if distAbiDiffDir.Valid() {
@@ -266,6 +266,7 @@
 	stripKeepSymbols       bool
 	stripKeepMiniDebugInfo bool
 	stripAddGnuDebuglink   bool
+	stripUseLlvmStrip      bool
 }
 
 type Objects struct {
@@ -725,11 +726,14 @@
 	baseName, exportedHeaderFlags string, isVndkExt bool) android.OptionalPath {
 
 	outputFile := android.PathForModuleOut(ctx, baseName+".abidiff")
-
+	libName := strings.TrimSuffix(baseName, filepath.Ext(baseName))
 	localAbiCheckAllowFlags := append([]string(nil), abiCheckAllowFlags...)
 	if exportedHeaderFlags == "" {
 		localAbiCheckAllowFlags = append(localAbiCheckAllowFlags, "-advice-only")
 	}
+	if inList(libName, llndkLibraries) {
+		localAbiCheckAllowFlags = append(localAbiCheckAllowFlags, "-consider-opaque-types-different")
+	}
 	if isVndkExt {
 		localAbiCheckAllowFlags = append(localAbiCheckAllowFlags, "-allow-extensions")
 	}
@@ -742,7 +746,7 @@
 		Implicit:    referenceDump,
 		Args: map[string]string{
 			"referenceDump": referenceDump.String(),
-			"libName":       baseName[0:(len(baseName) - len(filepath.Ext(baseName)))],
+			"libName":       libName,
 			"arch":          ctx.Arch().ArchType.Name,
 			"allowFlags":    strings.Join(localAbiCheckAllowFlags, " "),
 		},
@@ -822,6 +826,9 @@
 	if flags.stripKeepSymbols {
 		args += " --keep-symbols"
 	}
+	if flags.stripUseLlvmStrip {
+		args += " --use-llvm-strip"
+	}
 
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        strip,
diff --git a/cc/cc.go b/cc/cc.go
index 76e6645..89d45a9 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -34,7 +34,7 @@
 	android.RegisterModuleType("cc_defaults", defaultsFactory)
 
 	android.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.BottomUp("image", vendorMutator).Parallel()
+		ctx.BottomUp("image", imageMutator).Parallel()
 		ctx.BottomUp("link", linkageMutator).Parallel()
 		ctx.BottomUp("vndk", vndkMutator).Parallel()
 		ctx.BottomUp("ndk_api", ndkApiMutator).Parallel()
@@ -52,7 +52,7 @@
 		ctx.TopDown("tsan_deps", sanitizerDepsMutator(tsan))
 		ctx.BottomUp("tsan", sanitizerMutator(tsan)).Parallel()
 
-		ctx.TopDown("sanitize_runtime_deps", sanitizerRuntimeDepsMutator())
+		ctx.TopDown("sanitize_runtime_deps", sanitizerRuntimeDepsMutator)
 
 		ctx.BottomUp("coverage", coverageLinkingMutator).Parallel()
 		ctx.TopDown("vndk_deps", sabiDepsMutator)
@@ -175,6 +175,11 @@
 	// *.logtags files, to combine together in order to generate the /system/etc/event-log-tags
 	// file
 	Logtags []string
+
+	// Make this module available when building for recovery
+	Recovery_available *bool
+
+	InRecovery bool `blueprint:"mutated"`
 }
 
 type VendorProperties struct {
@@ -206,10 +211,6 @@
 	Double_loadable *bool
 }
 
-type UnusedProperties struct {
-	Tags []string
-}
-
 type ModuleContextIntf interface {
 	static() bool
 	staticBinary() bool
@@ -221,7 +222,8 @@
 	isVndk() bool
 	isVndkSp() bool
 	isVndkExt() bool
-	createVndkSourceAbiDump() bool
+	inRecovery() bool
+	shouldCreateVndkSourceAbiDump() bool
 	selectedStl() string
 	baseModuleName() string
 	getVndkExtendsModuleName() string
@@ -320,7 +322,6 @@
 
 	Properties       BaseProperties
 	VendorProperties VendorProperties
-	unused           UnusedProperties
 
 	// initialize before calling Init
 	hod      android.HostOrDeviceSupported
@@ -360,7 +361,7 @@
 }
 
 func (c *Module) Init() android.Module {
-	c.AddProperties(&c.Properties, &c.VendorProperties, &c.unused)
+	c.AddProperties(&c.Properties, &c.VendorProperties)
 	if c.compiler != nil {
 		c.AddProperties(c.compiler.compilerProps()...)
 	}
@@ -458,6 +459,14 @@
 	return c.isVndk() || Bool(c.VendorProperties.Vendor_available)
 }
 
+func (c *Module) inRecovery() bool {
+	return c.Properties.InRecovery || c.ModuleBase.InstallInRecovery()
+}
+
+func (c *Module) onlyInRecovery() bool {
+	return c.ModuleBase.InstallInRecovery()
+}
+
 type baseModuleContext struct {
 	android.BaseContext
 	moduleContextImpl
@@ -505,7 +514,7 @@
 }
 
 func (ctx *moduleContextImpl) useSdk() bool {
-	if ctx.ctx.Device() && !ctx.useVndk() {
+	if ctx.ctx.Device() && !ctx.useVndk() && !ctx.inRecovery() {
 		return String(ctx.mod.Properties.Sdk_version) != ""
 	}
 	return false
@@ -549,16 +558,33 @@
 	return ctx.mod.isVndkExt()
 }
 
-// Create source abi dumps if the module belongs to the list of VndkLibraries.
-func (ctx *moduleContextImpl) createVndkSourceAbiDump() bool {
-	skipAbiChecks := ctx.ctx.Config().IsEnvTrue("SKIP_ABI_CHECKS")
-	isUnsanitizedVariant := true
-	sanitize := ctx.mod.sanitize
-	if sanitize != nil {
-		isUnsanitizedVariant = sanitize.isUnsanitizedVariant()
+func (ctx *moduleContextImpl) inRecovery() bool {
+	return ctx.mod.inRecovery()
+}
+
+// Check whether ABI dumps should be created for this module.
+func (ctx *moduleContextImpl) shouldCreateVndkSourceAbiDump() bool {
+	if ctx.ctx.Config().IsEnvTrue("SKIP_ABI_CHECKS") {
+		return false
 	}
-	vendorAvailable := Bool(ctx.mod.VendorProperties.Vendor_available)
-	return !skipAbiChecks && isUnsanitizedVariant && ctx.ctx.Device() && ((ctx.useVndk() && ctx.isVndk() && vendorAvailable) || inList(ctx.baseModuleName(), llndkLibraries))
+	if sanitize := ctx.mod.sanitize; sanitize != nil {
+		if !sanitize.isVariantOnProductionDevice() {
+			return false
+		}
+	}
+	if !ctx.ctx.Device() {
+		// Host modules do not need ABI dumps.
+		return false
+	}
+	if inList(ctx.baseModuleName(), llndkLibraries) {
+		return true
+	}
+	if ctx.useVndk() && ctx.isVndk() {
+		// Return true if this is VNDK-core, VNDK-SP, or VNDK-Ext and this is not
+		// VNDK-private.
+		return Bool(ctx.mod.VendorProperties.Vendor_available) || ctx.isVndkExt()
+	}
+	return false
 }
 
 func (ctx *moduleContextImpl) selectedStl() string {
@@ -1074,6 +1100,10 @@
 		// Platform code can link to anything
 		return
 	}
+	if from.inRecovery() {
+		// Recovery code is not NDK
+		return
+	}
 	if _, ok := to.linker.(*toolchainLibraryDecorator); ok {
 		// These are always allowed
 		return
@@ -1185,8 +1215,6 @@
 		if ccDep == nil {
 			// handling for a few module types that aren't cc Module but that are also supported
 			switch depTag {
-			case android.DefaultsDepTag, android.SourceDepTag:
-				// Nothing to do
 			case genSourceDepTag:
 				if genRule, ok := dep.(genrule.SourceFileGenerator); ok {
 					depPaths.GeneratedSources = append(depPaths.GeneratedSources,
@@ -1224,8 +1252,6 @@
 				} else {
 					ctx.ModuleErrorf("module %q is not a genrule", depName)
 				}
-			default:
-				ctx.ModuleErrorf("depends on non-cc module %q", depName)
 			}
 			return
 		}
@@ -1365,6 +1391,8 @@
 				return libName + vendorSuffix
 			} else if (ctx.Platform() || ctx.ProductSpecific()) && isVendorPublicLib {
 				return libName + vendorPublicLibrarySuffix
+			} else if ccDep.inRecovery() && !ccDep.onlyInRecovery() {
+				return libName + recoverySuffix
 			} else {
 				return libName
 			}
@@ -1416,6 +1444,10 @@
 	return c.installer.inSanitizerDir()
 }
 
+func (c *Module) InstallInRecovery() bool {
+	return c.inRecovery()
+}
+
 func (c *Module) HostToolPath() android.OptionalPath {
 	if c.installer == nil {
 		return android.OptionalPath{}
@@ -1470,12 +1502,12 @@
 		&VendorProperties{},
 		&BaseCompilerProperties{},
 		&BaseLinkerProperties{},
+		&MoreBaseLinkerProperties{},
 		&LibraryProperties{},
 		&FlagExporterProperties{},
 		&BinaryLinkerProperties{},
 		&TestProperties{},
 		&TestBinaryProperties{},
-		&UnusedProperties{},
 		&StlProperties{},
 		&SanitizeProperties{},
 		&StripProperties{},
@@ -1502,6 +1534,8 @@
 	// vendorMode is the variant used for /vendor code that compiles
 	// against the VNDK.
 	vendorMode = "vendor"
+
+	recoveryMode = "recovery"
 )
 
 func squashVendorSrcs(m *Module) {
@@ -1514,22 +1548,59 @@
 	}
 }
 
-func vendorMutator(mctx android.BottomUpMutatorContext) {
+func squashRecoverySrcs(m *Module) {
+	if lib, ok := m.compiler.(*libraryDecorator); ok {
+		lib.baseCompiler.Properties.Srcs = append(lib.baseCompiler.Properties.Srcs,
+			lib.baseCompiler.Properties.Target.Recovery.Srcs...)
+
+		lib.baseCompiler.Properties.Exclude_srcs = append(lib.baseCompiler.Properties.Exclude_srcs,
+			lib.baseCompiler.Properties.Target.Recovery.Exclude_srcs...)
+	}
+}
+
+func imageMutator(mctx android.BottomUpMutatorContext) {
 	if mctx.Os() != android.Android {
 		return
 	}
 
 	if genrule, ok := mctx.Module().(*genrule.Module); ok {
-		if props, ok := genrule.Extra.(*VendorProperties); ok {
+		if props, ok := genrule.Extra.(*GenruleExtraProperties); ok {
+			var coreVariantNeeded bool = false
+			var vendorVariantNeeded bool = false
+			var recoveryVariantNeeded bool = false
 			if mctx.DeviceConfig().VndkVersion() == "" {
-				mctx.CreateVariations(coreMode)
+				coreVariantNeeded = true
 			} else if Bool(props.Vendor_available) {
-				mctx.CreateVariations(coreMode, vendorMode)
+				coreVariantNeeded = true
+				vendorVariantNeeded = true
 			} else if mctx.SocSpecific() || mctx.DeviceSpecific() {
-				mctx.CreateVariations(vendorMode)
+				vendorVariantNeeded = true
 			} else {
-				mctx.CreateVariations(coreMode)
+				coreVariantNeeded = true
 			}
+			if Bool(props.Recovery_available) {
+				recoveryVariantNeeded = true
+			}
+
+			if recoveryVariantNeeded {
+				primaryArch := mctx.Config().DevicePrimaryArchType()
+				moduleArch := genrule.Target().Arch.ArchType
+				if moduleArch != primaryArch {
+					recoveryVariantNeeded = false
+				}
+			}
+
+			var variants []string
+			if coreVariantNeeded {
+				variants = append(variants, coreMode)
+			}
+			if vendorVariantNeeded {
+				variants = append(variants, vendorMode)
+			}
+			if recoveryVariantNeeded {
+				variants = append(variants, recoveryMode)
+			}
+			mctx.CreateVariations(variants...)
 		}
 	}
 
@@ -1583,43 +1654,78 @@
 		}
 	}
 
+	var coreVariantNeeded bool = false
+	var vendorVariantNeeded bool = false
+	var recoveryVariantNeeded bool = false
+
 	if mctx.DeviceConfig().VndkVersion() == "" {
 		// If the device isn't compiling against the VNDK, we always
 		// use the core mode.
-		mctx.CreateVariations(coreMode)
+		coreVariantNeeded = true
 	} else if _, ok := m.linker.(*llndkStubDecorator); ok {
 		// LL-NDK stubs only exist in the vendor variant, since the
 		// real libraries will be used in the core variant.
-		mctx.CreateVariations(vendorMode)
+		vendorVariantNeeded = true
 	} else if _, ok := m.linker.(*llndkHeadersDecorator); ok {
 		// ... and LL-NDK headers as well
-		mod := mctx.CreateVariations(vendorMode)
-		vendor := mod[0].(*Module)
-		vendor.Properties.UseVndk = true
+		vendorVariantNeeded = true
 	} else if _, ok := m.linker.(*vndkPrebuiltLibraryDecorator); ok {
 		// Make vendor variants only for the versions in BOARD_VNDK_VERSION and
 		// PRODUCT_EXTRA_VNDK_VERSIONS.
-		mod := mctx.CreateVariations(vendorMode)
-		vendor := mod[0].(*Module)
-		vendor.Properties.UseVndk = true
+		vendorVariantNeeded = true
 	} else if m.hasVendorVariant() && !vendorSpecific {
 		// This will be available in both /system and /vendor
 		// or a /system directory that is available to vendor.
-		mod := mctx.CreateVariations(coreMode, vendorMode)
-		vendor := mod[1].(*Module)
-		vendor.Properties.UseVndk = true
-		squashVendorSrcs(vendor)
+		coreVariantNeeded = true
+		vendorVariantNeeded = true
 	} else if vendorSpecific && String(m.Properties.Sdk_version) == "" {
 		// This will be available in /vendor (or /odm) only
-		mod := mctx.CreateVariations(vendorMode)
-		vendor := mod[0].(*Module)
-		vendor.Properties.UseVndk = true
-		squashVendorSrcs(vendor)
+		vendorVariantNeeded = true
 	} else {
 		// This is either in /system (or similar: /data), or is a
 		// modules built with the NDK. Modules built with the NDK
 		// will be restricted using the existing link type checks.
-		mctx.CreateVariations(coreMode)
+		coreVariantNeeded = true
+	}
+
+	if Bool(m.Properties.Recovery_available) {
+		recoveryVariantNeeded = true
+	}
+
+	if m.ModuleBase.InstallInRecovery() {
+		recoveryVariantNeeded = true
+		coreVariantNeeded = false
+	}
+
+	if recoveryVariantNeeded {
+		primaryArch := mctx.Config().DevicePrimaryArchType()
+		moduleArch := m.Target().Arch.ArchType
+		if moduleArch != primaryArch {
+			recoveryVariantNeeded = false
+		}
+	}
+
+	var variants []string
+	if coreVariantNeeded {
+		variants = append(variants, coreMode)
+	}
+	if vendorVariantNeeded {
+		variants = append(variants, vendorMode)
+	}
+	if recoveryVariantNeeded {
+		variants = append(variants, recoveryMode)
+	}
+	mod := mctx.CreateVariations(variants...)
+	for i, v := range variants {
+		if v == vendorMode {
+			m := mod[i].(*Module)
+			m.Properties.UseVndk = true
+			squashVendorSrcs(m)
+		} else if v == recoveryMode {
+			m := mod[i].(*Module)
+			m.Properties.InRecovery = true
+			squashRecoverySrcs(m)
+		}
 	}
 }
 
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 4f26827..3d162e7 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -63,7 +63,7 @@
 	ctx.RegisterModuleType("cc_object", android.ModuleFactoryAdaptor(objectFactory))
 	ctx.RegisterModuleType("filegroup", android.ModuleFactoryAdaptor(android.FileGroupFactory))
 	ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.BottomUp("image", vendorMutator).Parallel()
+		ctx.BottomUp("image", imageMutator).Parallel()
 		ctx.BottomUp("link", linkageMutator).Parallel()
 		ctx.BottomUp("vndk", vndkMutator).Parallel()
 		ctx.BottomUp("begin", beginMutator).Parallel()
@@ -75,16 +75,19 @@
 		toolchain_library {
 			name: "libatomic",
 			vendor_available: true,
+			recovery_available: true,
 		}
 
 		toolchain_library {
 			name: "libcompiler_rt-extras",
 			vendor_available: true,
+			recovery_available: true,
 		}
 
 		toolchain_library {
 			name: "libgcc",
 			vendor_available: true,
+			recovery_available: true,
 		}
 
 		cc_library {
@@ -92,6 +95,7 @@
 			no_libgcc: true,
 			nocrt: true,
 			system_shared_libs: [],
+			recovery_available: true,
 		}
 		llndk_library {
 			name: "libc",
@@ -102,6 +106,7 @@
 			no_libgcc: true,
 			nocrt: true,
 			system_shared_libs: [],
+			recovery_available: true,
 		}
 		llndk_library {
 			name: "libm",
@@ -112,6 +117,7 @@
 			no_libgcc: true,
 			nocrt: true,
 			system_shared_libs: [],
+			recovery_available: true,
 		}
 		llndk_library {
 			name: "libdl",
@@ -124,6 +130,7 @@
 			system_shared_libs: [],
 			stl: "none",
 			vendor_available: true,
+			recovery_available: true,
 		}
 		cc_library {
 			name: "libc++",
@@ -132,6 +139,7 @@
 			system_shared_libs: [],
 			stl: "none",
 			vendor_available: true,
+			recovery_available: true,
 			vndk: {
 				enabled: true,
 				support_system_process: true,
@@ -144,14 +152,17 @@
 			system_shared_libs: [],
 			stl: "none",
 			vendor_available: true,
+			recovery_available: true,
 		}
 
 		cc_object {
 			name: "crtbegin_so",
+			recovery_available: true,
 		}
 
 		cc_object {
 			name: "crtend_so",
+			recovery_available: true,
 		}
 
 		cc_library {
@@ -1651,3 +1662,28 @@
 	}
 
 }
+
+func TestRecovery(t *testing.T) {
+	ctx := testCc(t, `
+		cc_library_shared {
+			name: "librecovery",
+			recovery: true,
+		}
+		cc_library_shared {
+			name: "librecovery32",
+			recovery: true,
+			compile_multilib:"32",
+		}
+	`)
+
+	variants := ctx.ModuleVariantsForTests("librecovery")
+	const arm64 = "android_arm64_armv8-a_recovery_shared"
+	if len(variants) != 1 || !android.InList(arm64, variants) {
+		t.Errorf("variants of librecovery must be \"%s\" only, but was %#v", arm64, variants)
+	}
+
+	variants = ctx.ModuleVariantsForTests("librecovery32")
+	if android.InList(arm64, variants) {
+		t.Errorf("multilib was set to 32 for librecovery32, but its variants has %s.", arm64)
+	}
+}
diff --git a/cc/cmakelists.go b/cc/cmakelists.go
index c25578e..a2f46cd 100644
--- a/cc/cmakelists.go
+++ b/cc/cmakelists.go
@@ -62,10 +62,14 @@
 
 	outputDebugInfo = (getEnvVariable(envVariableGenerateDebugInfo, ctx) == envVariableTrue)
 
+	// Track which projects have already had CMakeLists.txt generated to keep the first
+	// variant for each project.
+	seenProjects := map[string]bool{}
+
 	ctx.VisitAllModules(func(module android.Module) {
 		if ccModule, ok := module.(*Module); ok {
 			if compiledModule, ok := ccModule.compiler.(CompiledInterface); ok {
-				generateCLionProject(compiledModule, ctx, ccModule)
+				generateCLionProject(compiledModule, ctx, ccModule, seenProjects)
 			}
 		}
 	})
@@ -114,14 +118,22 @@
 	return nil
 }
 
-func generateCLionProject(compiledModule CompiledInterface, ctx android.SingletonContext, ccModule *Module) {
+func generateCLionProject(compiledModule CompiledInterface, ctx android.SingletonContext, ccModule *Module,
+	seenProjects map[string]bool) {
 	srcs := compiledModule.Srcs()
 	if len(srcs) == 0 {
 		return
 	}
 
-	// Ensure the directory hosting the cmakelists.txt exists
+	// Only write CMakeLists.txt for the first variant of each architecture of each module
 	clionproject_location := getCMakeListsForModule(ccModule, ctx)
+	if seenProjects[clionproject_location] {
+		return
+	}
+
+	seenProjects[clionproject_location] = true
+
+	// Ensure the directory hosting the cmakelists.txt exists
 	projectDir := path.Dir(clionproject_location)
 	os.MkdirAll(projectDir, os.ModePerm)
 
diff --git a/cc/compiler.go b/cc/compiler.go
index 2ba19f1..8d034c9 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -142,6 +142,19 @@
 			// variant of the C/C++ module.
 			Cflags []string
 		}
+		Recovery struct {
+			// list of source files that should only be used in the
+			// recovery variant of the C/C++ module.
+			Srcs []string
+
+			// list of source files that should not be used to
+			// build the recovery variant of the C/C++ module.
+			Exclude_srcs []string
+
+			// List of additional cflags that should be used to build the recovery
+			// variant of the C/C++ module.
+			Cflags []string
+		}
 	}
 
 	Proto struct {
@@ -245,6 +258,8 @@
 	CheckBadCompilerFlags(ctx, "cppflags", compiler.Properties.Cppflags)
 	CheckBadCompilerFlags(ctx, "conlyflags", compiler.Properties.Conlyflags)
 	CheckBadCompilerFlags(ctx, "asflags", compiler.Properties.Asflags)
+	CheckBadCompilerFlags(ctx, "vendor.cflags", compiler.Properties.Target.Vendor.Cflags)
+	CheckBadCompilerFlags(ctx, "recovery.cflags", compiler.Properties.Target.Recovery.Cflags)
 
 	esc := proptools.NinjaAndShellEscape
 
@@ -317,6 +332,10 @@
 			"-D__ANDROID_API__="+version, "-D__ANDROID_VNDK__")
 	}
 
+	if ctx.inRecovery() {
+		flags.GlobalFlags = append(flags.GlobalFlags, "-D__ANDROID_RECOVERY__")
+	}
+
 	instructionSet := String(compiler.Properties.Instruction_set)
 	if flags.RequiredInstructionSet != "" {
 		instructionSet = flags.RequiredInstructionSet
@@ -377,6 +396,12 @@
 			fmt.Sprintf("${config.%sGlobalCflags}", hod))
 	}
 
+	if flags.Clang {
+		if strings.HasPrefix(android.PathForModuleSrc(ctx).String(), "external/") {
+			flags.GlobalFlags = append([]string{"${config.ClangExternalCflags}"}, flags.GlobalFlags...)
+		}
+	}
+
 	if ctx.Device() {
 		if Bool(compiler.Properties.Rtti) {
 			flags.CppFlags = append(flags.CppFlags, "-frtti")
@@ -441,6 +466,10 @@
 		flags.CFlags = append(flags.CFlags, esc(compiler.Properties.Target.Vendor.Cflags)...)
 	}
 
+	if ctx.inRecovery() {
+		flags.CFlags = append(flags.CFlags, esc(compiler.Properties.Target.Recovery.Cflags)...)
+	}
+
 	// We can enforce some rules more strictly in the code we own. strict
 	// indicates if this is code that we can be stricter with. If we have
 	// rules that we want to apply to *our* code (but maybe can't for
diff --git a/cc/config/arm64_device.go b/cc/config/arm64_device.go
index 74c936f..172784a 100644
--- a/cc/config/arm64_device.go
+++ b/cc/config/arm64_device.go
@@ -27,10 +27,18 @@
 		"-Werror=implicit-function-declaration",
 	}
 
+	arm64ArchVariantCflags = map[string][]string{
+		"armv8-a": []string{
+			"-march=armv8-a",
+		},
+		"armv8-2a": []string{
+			"-march=armv8.2a",
+		},
+	}
+
 	arm64Ldflags = []string{
 		"-Wl,-m,aarch64_elf64_le_vec",
 		"-Wl,--hash-style=gnu",
-		"-Wl,--fix-cortex-a53-843419",
 		"-fuse-ld=gold",
 		"-Wl,--icf=safe",
 	}
@@ -44,6 +52,14 @@
 		"cortex-a53": []string{
 			"-mcpu=cortex-a53",
 		},
+		"cortex-a55": []string{
+			"-mcpu=cortex-a55",
+		},
+		"cortex-a75": []string{
+			// Use the cortex-a55 since it is similar to the little
+			// core (cortex-a55) and is sensitive to ordering.
+			"-mcpu=cortex-a55",
+		},
 		"kryo": []string{
 			// Use the cortex-a57 cpu since some compilers
 			// don't support a Kryo specific target yet.
@@ -67,8 +83,11 @@
 func init() {
 	android.RegisterArchVariants(android.Arm64,
 		"armv8_a",
+		"armv8_2a",
 		"cortex-a53",
+		"cortex-a55",
 		"cortex-a73",
+		"cortex-a75",
 		"kryo",
 		"exynos-m1",
 		"exynos-m2",
@@ -93,11 +112,19 @@
 	pctx.StaticVariable("Arm64ClangLldflags", strings.Join(ClangFilterUnknownCflags(arm64Lldflags), " "))
 	pctx.StaticVariable("Arm64ClangCppflags", strings.Join(ClangFilterUnknownCflags(arm64Cppflags), " "))
 
+	pctx.StaticVariable("Arm64ClangArmv8ACflags", strings.Join(arm64ArchVariantCflags["armv8-a"], " "))
+	pctx.StaticVariable("Arm64ClangArmv82ACflags", strings.Join(arm64ArchVariantCflags["armv8-2a"], " "))
+
 	pctx.StaticVariable("Arm64CortexA53Cflags",
 		strings.Join(arm64CpuVariantCflags["cortex-a53"], " "))
 	pctx.StaticVariable("Arm64ClangCortexA53Cflags",
 		strings.Join(arm64ClangCpuVariantCflags["cortex-a53"], " "))
 
+	pctx.StaticVariable("Arm64CortexA55Cflags",
+		strings.Join(arm64CpuVariantCflags["cortex-a55"], " "))
+	pctx.StaticVariable("Arm64ClangCortexA55Cflags",
+		strings.Join(arm64ClangCpuVariantCflags["cortex-a55"], " "))
+
 	pctx.StaticVariable("Arm64KryoCflags",
 		strings.Join(arm64CpuVariantCflags["kryo"], " "))
 	pctx.StaticVariable("Arm64ClangKryoCflags",
@@ -118,16 +145,25 @@
 	arm64CpuVariantCflagsVar = map[string]string{
 		"":           "",
 		"cortex-a53": "${config.Arm64CortexA53Cflags}",
+		"cortex-a55": "${config.Arm64CortexA55Cflags}",
 		"cortex-a73": "${config.Arm64CortexA53Cflags}",
+		"cortex-a75": "${config.Arm64CortexA55Cflags}",
 		"kryo":       "${config.Arm64KryoCflags}",
 		"exynos-m1":  "${config.Arm64ExynosM1Cflags}",
 		"exynos-m2":  "${config.Arm64ExynosM2Cflags}",
 	}
 
+	arm64ClangArchVariantCflagsVar = map[string]string{
+		"armv8-a":  "${config.Arm64ClangArmv8ACflags}",
+		"armv8-2a": "${config.Arm64ClangArmv82ACflags}",
+	}
+
 	arm64ClangCpuVariantCflagsVar = map[string]string{
 		"":           "",
 		"cortex-a53": "${config.Arm64ClangCortexA53Cflags}",
+		"cortex-a55": "${config.Arm64ClangCortexA55Cflags}",
 		"cortex-a73": "${config.Arm64ClangCortexA53Cflags}",
+		"cortex-a75": "${config.Arm64ClangCortexA55Cflags}",
 		"kryo":       "${config.Arm64ClangKryoCflags}",
 		"exynos-m1":  "${config.Arm64ClangExynosM1Cflags}",
 		"exynos-m2":  "${config.Arm64ClangExynosM2Cflags}",
@@ -137,6 +173,8 @@
 type toolchainArm64 struct {
 	toolchain64Bit
 
+	ldflags              string
+	lldflags             string
 	toolchainCflags      string
 	toolchainClangCflags string
 }
@@ -170,7 +208,7 @@
 }
 
 func (t *toolchainArm64) Ldflags() string {
-	return "${config.Arm64Ldflags}"
+	return t.ldflags
 }
 
 func (t *toolchainArm64) IncludeFlags() string {
@@ -190,11 +228,11 @@
 }
 
 func (t *toolchainArm64) ClangLdflags() string {
-	return "${config.Arm64Ldflags}"
+	return t.ldflags
 }
 
 func (t *toolchainArm64) ClangLldflags() string {
-	return "${config.Arm64Lldflags}"
+	return t.lldflags
 }
 
 func (t *toolchainArm64) ToolchainClangCflags() string {
@@ -206,13 +244,38 @@
 }
 
 func arm64ToolchainFactory(arch android.Arch) Toolchain {
-	if arch.ArchVariant != "armv8-a" {
+	switch arch.ArchVariant {
+	case "armv8-a":
+	case "armv8-2a":
+		// Nothing extra for armv8-a/armv8-2a
+	default:
 		panic(fmt.Sprintf("Unknown ARM architecture version: %q", arch.ArchVariant))
 	}
 
+	toolchainClangCflags := []string{arm64ClangArchVariantCflagsVar[arch.ArchVariant]}
+	toolchainClangCflags = append(toolchainClangCflags,
+		variantOrDefault(arm64ClangCpuVariantCflagsVar, arch.CpuVariant))
+
+	var extraLdflags string
+	switch arch.CpuVariant {
+	case "cortex-a53", "cortex-a73", "kryo", "exynos-m1", "exynos-m2",
+		// This variant might not need the workaround but leave it
+		// in the list since it has had the workaround on before.
+		"denver64":
+		extraLdflags = "-Wl,--fix-cortex-a53-843419"
+	}
+
 	return &toolchainArm64{
+		ldflags: strings.Join([]string{
+			"${config.Arm64Ldflags}",
+			extraLdflags,
+		}, " "),
+		lldflags: strings.Join([]string{
+			"${config.Arm64Lldflags}",
+			extraLdflags,
+		}, " "),
 		toolchainCflags:      variantOrDefault(arm64CpuVariantCflagsVar, arch.CpuVariant),
-		toolchainClangCflags: variantOrDefault(arm64ClangCpuVariantCflagsVar, arch.CpuVariant),
+		toolchainClangCflags: strings.Join(toolchainClangCflags, " "),
 	}
 }
 
diff --git a/cc/config/arm_device.go b/cc/config/arm_device.go
index 02bd9eb..4719fb7 100644
--- a/cc/config/arm_device.go
+++ b/cc/config/arm_device.go
@@ -99,6 +99,24 @@
 			// better solution comes around. See Bug 27340895
 			"-D__ARM_FEATURE_LPAE=1",
 		},
+		"cortex-a55": []string{
+			"-mcpu=cortex-a55",
+			"-mfpu=neon-fp-armv8",
+			// Fake an ARM compiler flag as these processors support LPAE which GCC/clang
+			// don't advertise.
+			// TODO This is a hack and we need to add it for each processor that supports LPAE until some
+			// better solution comes around. See Bug 27340895
+			"-D__ARM_FEATURE_LPAE=1",
+		},
+		"cortex-a75": []string{
+			"-mcpu=cortex-a55",
+			"-mfpu=neon-fp-armv8",
+			// Fake an ARM compiler flag as these processors support LPAE which GCC/clang
+			// don't advertise.
+			// TODO This is a hack and we need to add it for each processor that supports LPAE until some
+			// better solution comes around. See Bug 27340895
+			"-D__ARM_FEATURE_LPAE=1",
+		},
 		"krait": []string{
 			"-mcpu=cortex-a15",
 			"-mfpu=neon-vfpv4",
@@ -143,7 +161,9 @@
 		"cortex-a15",
 		"cortex-a53",
 		"cortex-a53-a57",
+		"cortex-a55",
 		"cortex-a73",
+		"cortex-a75",
 		"krait",
 		"kryo",
 		"exynos-m1",
@@ -192,6 +212,7 @@
 	pctx.StaticVariable("ArmCortexA8Cflags", strings.Join(armCpuVariantCflags["cortex-a8"], " "))
 	pctx.StaticVariable("ArmCortexA15Cflags", strings.Join(armCpuVariantCflags["cortex-a15"], " "))
 	pctx.StaticVariable("ArmCortexA53Cflags", strings.Join(armCpuVariantCflags["cortex-a53"], " "))
+	pctx.StaticVariable("ArmCortexA55Cflags", strings.Join(armCpuVariantCflags["cortex-a55"], " "))
 	pctx.StaticVariable("ArmKraitCflags", strings.Join(armCpuVariantCflags["krait"], " "))
 	pctx.StaticVariable("ArmKryoCflags", strings.Join(armCpuVariantCflags["kryo"], " "))
 
@@ -225,6 +246,8 @@
 		strings.Join(armClangCpuVariantCflags["cortex-a15"], " "))
 	pctx.StaticVariable("ArmClangCortexA53Cflags",
 		strings.Join(armClangCpuVariantCflags["cortex-a53"], " "))
+	pctx.StaticVariable("ArmClangCortexA55Cflags",
+		strings.Join(armClangCpuVariantCflags["cortex-a55"], " "))
 	pctx.StaticVariable("ArmClangKraitCflags",
 		strings.Join(armClangCpuVariantCflags["krait"], " "))
 	pctx.StaticVariable("ArmClangKryoCflags",
@@ -245,7 +268,9 @@
 		"cortex-a15":     "${config.ArmCortexA15Cflags}",
 		"cortex-a53":     "${config.ArmCortexA53Cflags}",
 		"cortex-a53.a57": "${config.ArmCortexA53Cflags}",
+		"cortex-a55":     "${config.ArmCortexA55Cflags}",
 		"cortex-a73":     "${config.ArmCortexA53Cflags}",
+		"cortex-a75":     "${config.ArmCortexA55Cflags}",
 		"krait":          "${config.ArmKraitCflags}",
 		"kryo":           "${config.ArmKryoCflags}",
 		"exynos-m1":      "${config.ArmCortexA53Cflags}",
@@ -266,7 +291,9 @@
 		"cortex-a15":     "${config.ArmClangCortexA15Cflags}",
 		"cortex-a53":     "${config.ArmClangCortexA53Cflags}",
 		"cortex-a53.a57": "${config.ArmClangCortexA53Cflags}",
+		"cortex-a55":     "${config.ArmClangCortexA55Cflags}",
 		"cortex-a73":     "${config.ArmClangCortexA53Cflags}",
+		"cortex-a75":     "${config.ArmClangCortexA55Cflags}",
 		"krait":          "${config.ArmClangKraitCflags}",
 		"kryo":           "${config.ArmClangKryoCflags}",
 		"exynos-m1":      "${config.ArmClangCortexA53Cflags}",
diff --git a/cc/config/clang.go b/cc/config/clang.go
index ba1cd3c..36afc68 100644
--- a/cc/config/clang.go
+++ b/cc/config/clang.go
@@ -173,15 +173,17 @@
 		// this new warning are fixed.
 		"-Wno-null-pointer-arithmetic",
 
-		// http://b/72330874 Disable -Wenum-compare until the instances detected by this new
-		// warning are fixed.
-		"-Wno-enum-compare",
-		"-Wno-enum-compare-switch",
-
 		// Disable c++98-specific warning since Android is not concerned with C++98
 		// compatibility.
 		"-Wno-c++98-compat-extra-semi",
 	}, " "))
+
+	// Extra cflags for projects under external/ directory to disable warnings that are infeasible
+	// to fix in all the external projects and their upstream repos.
+	pctx.StaticVariable("ClangExtraExternalCflags", strings.Join([]string{
+		"-Wno-enum-compare",
+		"-Wno-enum-compare-switch",
+	}, " "))
 }
 
 func ClangFilterUnknownCflags(cflags []string) []string {
diff --git a/cc/config/global.go b/cc/config/global.go
index 06f6f9a..dee7640 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -89,6 +89,9 @@
 
 	deviceGlobalLldflags = append(ClangFilterUnknownLldflags(deviceGlobalLdflags),
 		[]string{
+			// TODO(b/109657296): needs --no-rosegment until Android
+			// stack unwinder can handle the read-only segment.
+			"-Wl,--no-rosegment",
 			"-Wl,--pack-dyn-relocs=android",
 			"-fuse-ld=lld",
 		}...)
@@ -170,6 +173,8 @@
 	pctx.StaticVariable("CommonClangGlobalCppflags",
 		strings.Join(append(ClangFilterUnknownCflags(commonGlobalCppflags), "${ClangExtraCppflags}"), " "))
 
+	pctx.StaticVariable("ClangExternalCflags", "${ClangExtraExternalCflags}")
+
 	// Everything in these lists is a crime against abstraction and dependency tracking.
 	// Do not add anything to this list.
 	pctx.PrefixedExistentPathsForSourcesVariable("CommonGlobalIncludes", "-I",
diff --git a/cc/config/tidy.go b/cc/config/tidy.go
index a20d556..5d53a8c 100644
--- a/cc/config/tidy.go
+++ b/cc/config/tidy.go
@@ -19,6 +19,13 @@
 	"strings"
 )
 
+// clang-tidy doesn't recognize every flag that clang does. This is unlikely to
+// be a complete list, but we can populate this with the ones we know to avoid
+// issues with clang-diagnostic-unused-command-line-argument.
+var ClangTidyUnknownCflags = sorted([]string{
+	"-Wa,%",
+})
+
 func init() {
 	// Most Android source files are not clang-tidy clean yet.
 	// Global tidy checks include only google*, performance*,
diff --git a/cc/config/toolchain.go b/cc/config/toolchain.go
index ca863a7..ea8397d 100644
--- a/cc/config/toolchain.go
+++ b/cc/config/toolchain.go
@@ -243,6 +243,10 @@
 	return SanitizerRuntimeLibrary(t, "profile")
 }
 
+func ScudoRuntimeLibrary(t Toolchain) string {
+	return SanitizerRuntimeLibrary(t, "scudo")
+}
+
 func ToolPath(t Toolchain) string {
 	if p := t.ToolPath(); p != "" {
 		return p
diff --git a/cc/config/x86_windows_host.go b/cc/config/x86_windows_host.go
index 6fbff9f..4cb8fa4 100644
--- a/cc/config/x86_windows_host.go
+++ b/cc/config/x86_windows_host.go
@@ -45,7 +45,6 @@
 
 	windowsIncludeFlags = []string{
 		"-isystem ${WindowsGccRoot}/${WindowsGccTriple}/include",
-		"-isystem ${WindowsGccRoot}/lib/gcc/${WindowsGccTriple}/4.8.3/include",
 	}
 
 	windowsClangCppflags = []string{
@@ -79,22 +78,34 @@
 		"-m32",
 		"-Wl,--large-address-aware",
 		"-L${WindowsGccRoot}/${WindowsGccTriple}/lib32",
+		"-static-libgcc",
 	}
 	windowsX86ClangLdflags = append(ClangFilterUnknownCflags(windowsX86Ldflags), []string{
+		"-B${WindowsGccRoot}/${WindowsGccTriple}/bin",
 		"-B${WindowsGccRoot}/lib/gcc/${WindowsGccTriple}/4.8.3/32",
 		"-L${WindowsGccRoot}/lib/gcc/${WindowsGccTriple}/4.8.3/32",
 		"-B${WindowsGccRoot}/${WindowsGccTriple}/lib32",
+		"-pthread",
+		// Bug: http://b/109759970 - WAR until issue with ld.bfd's
+		// inability to handle Clang-generated section names is fixed.
+		"-Wl,--allow-multiple-definition",
 	}...)
 	windowsX86ClangLldflags = ClangFilterUnknownLldflags(windowsX86ClangLdflags)
 
 	windowsX8664Ldflags = []string{
 		"-m64",
 		"-L${WindowsGccRoot}/${WindowsGccTriple}/lib64",
+		"-static-libgcc",
 	}
 	windowsX8664ClangLdflags = append(ClangFilterUnknownCflags(windowsX8664Ldflags), []string{
+		"-B${WindowsGccRoot}/${WindowsGccTriple}/bin",
 		"-B${WindowsGccRoot}/lib/gcc/${WindowsGccTriple}/4.8.3",
 		"-L${WindowsGccRoot}/lib/gcc/${WindowsGccTriple}/4.8.3",
 		"-B${WindowsGccRoot}/${WindowsGccTriple}/lib64",
+		"-pthread",
+		// Bug: http://b/109759970 - WAR until issue with ld.bfd's
+		// inability to handle Clang-generated section names is fixed.
+		"-Wl,--allow-multiple-definition",
 	}...)
 	windowsX8664ClangLldflags = ClangFilterUnknownLldflags(windowsX8664ClangLdflags)
 
@@ -220,7 +231,7 @@
 }
 
 func (t *toolchainWindows) ClangSupported() bool {
-	return false
+	return true
 }
 
 func (t *toolchainWindowsX86) ClangTriple() string {
diff --git a/cc/gen.go b/cc/gen.go
index f22a783..487f662 100644
--- a/cc/gen.go
+++ b/cc/gen.go
@@ -25,7 +25,7 @@
 )
 
 func init() {
-	pctx.SourcePathVariable("lexCmd", "prebuilts/misc/${config.HostPrebuiltTag}/flex/flex-2.5.39")
+	pctx.SourcePathVariable("lexCmd", "prebuilts/build-tools/${config.HostPrebuiltTag}/bin/flex")
 	pctx.SourcePathVariable("yaccCmd", "prebuilts/build-tools/${config.HostPrebuiltTag}/bin/bison")
 	pctx.SourcePathVariable("yaccDataDir", "prebuilts/build-tools/common/bison")
 
@@ -48,7 +48,7 @@
 
 	aidl = pctx.AndroidStaticRule("aidl",
 		blueprint.RuleParams{
-			Command:     "$aidlCmd -d${out}.d -ninja $aidlFlags $in $outDir $out",
+			Command:     "$aidlCmd -d${out}.d --ninja $aidlFlags $in $outDir $out",
 			CommandDeps: []string{"$aidlCmd"},
 			Depfile:     "${out}.d",
 			Deps:        blueprint.DepsGCC,
diff --git a/cc/genrule.go b/cc/genrule.go
index 51c0d16..a672992 100644
--- a/cc/genrule.go
+++ b/cc/genrule.go
@@ -23,13 +23,18 @@
 	android.RegisterModuleType("cc_genrule", genRuleFactory)
 }
 
+type GenruleExtraProperties struct {
+	Vendor_available   *bool
+	Recovery_available *bool
+}
+
 // cc_genrule is a genrule that can depend on other cc_* objects.
 // The cmd may be run multiple times, once for each of the different arch/etc
 // variations.
 func genRuleFactory() android.Module {
 	module := genrule.NewGenRule()
 
-	module.Extra = &VendorProperties{}
+	module.Extra = &GenruleExtraProperties{}
 	module.AddProperties(module.Extra)
 
 	android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibBoth)
diff --git a/cc/libbuildversion/Android.bp b/cc/libbuildversion/Android.bp
index fd563e6..825b920 100644
--- a/cc/libbuildversion/Android.bp
+++ b/cc/libbuildversion/Android.bp
@@ -1,6 +1,7 @@
 cc_library_static {
     name: "libbuildversion",
     host_supported: true,
+    recovery_available: true,
     srcs: ["libbuildversion.cpp"],
     export_include_dirs: ["include"],
     cflags: ["-fvisibility=hidden"],
diff --git a/cc/library.go b/cc/library.go
index b31fee2..e92cf9d 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -362,7 +362,7 @@
 		}
 		return Objects{}
 	}
-	if ctx.createVndkSourceAbiDump() || library.sabi.Properties.CreateSAbiDumps {
+	if ctx.shouldCreateVndkSourceAbiDump() || library.sabi.Properties.CreateSAbiDumps {
 		exportIncludeDirs := library.flagExporter.exportedIncludes(ctx)
 		var SourceAbiFlags []string
 		for _, dir := range exportIncludeDirs.Strings() {
@@ -477,6 +477,11 @@
 		deps.SharedLibs = removeListFromList(deps.SharedLibs, library.baseLinker.Properties.Target.Vendor.Exclude_shared_libs)
 		deps.StaticLibs = removeListFromList(deps.StaticLibs, library.baseLinker.Properties.Target.Vendor.Exclude_static_libs)
 	}
+	if ctx.inRecovery() {
+		deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, library.baseLinker.Properties.Target.Recovery.Exclude_static_libs)
+		deps.SharedLibs = removeListFromList(deps.SharedLibs, library.baseLinker.Properties.Target.Recovery.Exclude_shared_libs)
+		deps.StaticLibs = removeListFromList(deps.StaticLibs, library.baseLinker.Properties.Target.Recovery.Exclude_static_libs)
+	}
 
 	android.ExtractSourceDeps(ctx, library.Properties.Version_script)
 	android.ExtractSourceDeps(ctx, library.Properties.Unexported_symbols_list)
@@ -588,6 +593,10 @@
 	}
 
 	if library.stripper.needsStrip(ctx) {
+		// b/80093681, GNU strip/objcopy bug.
+		// Use llvm-{strip,objcopy} when clang lld is used.
+		builderFlags.stripUseLlvmStrip =
+			flags.Clang && library.baseLinker.useClangLld(ctx)
 		strippedOutputFile := outputFile
 		outputFile = android.PathForModuleOut(ctx, "unstripped", fileName)
 		library.stripper.strip(ctx, outputFile, strippedOutputFile, builderFlags)
@@ -623,14 +632,12 @@
 }
 
 func (library *libraryDecorator) linkSAbiDumpFiles(ctx ModuleContext, objs Objects, fileName string, soFile android.Path) {
-	//Also take into account object re-use.
-	if len(objs.sAbiDumpFiles) > 0 && ctx.createVndkSourceAbiDump() {
+	if len(objs.sAbiDumpFiles) > 0 && ctx.shouldCreateVndkSourceAbiDump() {
 		vndkVersion := ctx.DeviceConfig().PlatformVndkVersion()
 		if ver := ctx.DeviceConfig().VndkVersion(); ver != "" && ver != "current" {
 			vndkVersion = ver
 		}
 
-		refSourceDumpFile := android.PathForVndkRefAbiDump(ctx, vndkVersion, fileName, vndkVsNdk(ctx), true)
 		exportIncludeDirs := library.flagExporter.exportedIncludes(ctx)
 		var SourceAbiFlags []string
 		for _, dir := range exportIncludeDirs.Strings() {
@@ -641,6 +648,8 @@
 		}
 		exportedHeaderFlags := strings.Join(SourceAbiFlags, " ")
 		library.sAbiOutputFile = TransformDumpToLinkedDump(ctx, objs.sAbiDumpFiles, soFile, fileName, exportedHeaderFlags)
+
+		refSourceDumpFile := android.PathForVndkRefAbiDump(ctx, vndkVersion, fileName, vndkVsNdk(ctx), true)
 		if refSourceDumpFile.Valid() {
 			unzippedRefDump := UnzipRefDump(ctx, refSourceDumpFile.Path(), fileName)
 			library.sAbiDiff = SourceAbiDiff(ctx, library.sAbiOutputFile.Path(),
@@ -745,7 +754,7 @@
 	}
 
 	if Bool(library.Properties.Static_ndk_lib) && library.static() &&
-		!ctx.useVndk() && ctx.Device() &&
+		!ctx.useVndk() && !ctx.inRecovery() && ctx.Device() &&
 		library.sanitize.isUnsanitizedVariant() {
 		installPath := getNdkSysrootBase(ctx).Join(
 			ctx, "usr/lib", config.NDKTriple(ctx.toolchain()), file.Base())
diff --git a/cc/linker.go b/cc/linker.go
index 71da09e..6bbf015 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -107,12 +107,28 @@
 			// variant of the C/C++ module.
 			Exclude_runtime_libs []string
 		}
+		Recovery struct {
+			// list of shared libs that should not be used to build
+			// the recovery variant of the C/C++ module.
+			Exclude_shared_libs []string
+
+			// list of static libs that should not be used to build
+			// the recovery variant of the C/C++ module.
+			Exclude_static_libs []string
+		}
 	}
 
 	// make android::build:GetBuildNumber() available containing the build ID.
 	Use_version_lib *bool `android:"arch_variant"`
 }
 
+// TODO(http://b/80437643): BaseLinkerProperties is getting too big,
+// more than 2^16 bytes. New properties are defined in MoreBaseLinkerProperties.
+type MoreBaseLinkerProperties struct {
+	// Generate compact dynamic relocation table, default true.
+	Pack_relocations *bool `android:"arch_variant"`
+}
+
 func NewBaseLinker() *baseLinker {
 	return &baseLinker{}
 }
@@ -120,6 +136,7 @@
 // baseLinker provides support for shared_libs, static_libs, and whole_static_libs properties
 type baseLinker struct {
 	Properties        BaseLinkerProperties
+	MoreProperties    MoreBaseLinkerProperties
 	dynamicProperties struct {
 		RunPaths []string `blueprint:"mutated"`
 	}
@@ -138,7 +155,7 @@
 }
 
 func (linker *baseLinker) linkerProps() []interface{} {
-	return []interface{}{&linker.Properties, &linker.dynamicProperties}
+	return []interface{}{&linker.Properties, &linker.MoreProperties, &linker.dynamicProperties}
 }
 
 func (linker *baseLinker) linkerDeps(ctx BaseModuleContext, deps Deps) Deps {
@@ -166,6 +183,14 @@
 		deps.RuntimeLibs = removeListFromList(deps.RuntimeLibs, linker.Properties.Target.Vendor.Exclude_runtime_libs)
 	}
 
+	if ctx.inRecovery() {
+		deps.SharedLibs = removeListFromList(deps.SharedLibs, linker.Properties.Target.Recovery.Exclude_shared_libs)
+		deps.ReexportSharedLibHeaders = removeListFromList(deps.ReexportSharedLibHeaders, linker.Properties.Target.Recovery.Exclude_shared_libs)
+		deps.StaticLibs = removeListFromList(deps.StaticLibs, linker.Properties.Target.Recovery.Exclude_static_libs)
+		deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, linker.Properties.Target.Recovery.Exclude_static_libs)
+		deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Target.Recovery.Exclude_static_libs)
+	}
+
 	if ctx.ModuleName() != "libcompiler_rt-extras" {
 		deps.LateStaticLibs = append(deps.LateStaticLibs, "libcompiler_rt-extras")
 	}
@@ -221,6 +246,10 @@
 	if ctx.Darwin() {
 		return false
 	}
+	// http://b/110800681 - lld cannot link Android's Windows modules yet.
+	if ctx.Windows() {
+		return false
+	}
 	if linker.Properties.Use_clang_lld != nil {
 		return Bool(linker.Properties.Use_clang_lld)
 	}
@@ -239,6 +268,9 @@
 
 	if flags.Clang && linker.useClangLld(ctx) {
 		flags.LdFlags = append(flags.LdFlags, fmt.Sprintf("${config.%sGlobalLldflags}", hod))
+		if !BoolDefault(linker.MoreProperties.Pack_relocations, true) {
+			flags.LdFlags = append(flags.LdFlags, "-Wl,--pack-dyn-relocs=none")
+		}
 	} else {
 		flags.LdFlags = append(flags.LdFlags, fmt.Sprintf("${config.%sGlobalLdflags}", hod))
 	}
diff --git a/cc/makevars.go b/cc/makevars.go
index bb81dcb..88d4639 100644
--- a/cc/makevars.go
+++ b/cc/makevars.go
@@ -72,7 +72,10 @@
 	ctx.Strict("CLANG_CXX", "${config.ClangBin}/clang++")
 	ctx.Strict("LLVM_AS", "${config.ClangBin}/llvm-as")
 	ctx.Strict("LLVM_LINK", "${config.ClangBin}/llvm-link")
+	ctx.Strict("LLVM_OBJCOPY", "${config.ClangBin}/llvm-objcopy")
+	ctx.Strict("LLVM_STRIP", "${config.ClangBin}/llvm-strip")
 	ctx.Strict("PATH_TO_CLANG_TIDY", "${config.ClangBin}/clang-tidy")
+	ctx.Strict("CLANG_TIDY_UNKNOWN_CFLAGS", strings.Join(config.ClangTidyUnknownCflags, " "))
 	ctx.StrictSorted("CLANG_CONFIG_UNKNOWN_CFLAGS", strings.Join(config.ClangUnknownCflags, " "))
 
 	ctx.Strict("RS_LLVM_PREBUILTS_VERSION", "${config.RSClangVersion}")
@@ -83,6 +86,7 @@
 	ctx.Strict("RS_LLVM_AS", "${config.RSLLVMPrebuiltsPath}/llvm-as")
 	ctx.Strict("RS_LLVM_LINK", "${config.RSLLVMPrebuiltsPath}/llvm-link")
 
+	ctx.Strict("CLANG_EXTERNAL_CFLAGS", "${config.ClangExternalCflags}")
 	ctx.Strict("GLOBAL_CFLAGS_NO_OVERRIDE", "${config.NoOverrideGlobalCflags}")
 	ctx.Strict("GLOBAL_CLANG_CFLAGS_NO_OVERRIDE", "${config.ClangExtraNoOverrideCflags}")
 	ctx.Strict("GLOBAL_CPPFLAGS_NO_OVERRIDE", "")
@@ -299,6 +303,7 @@
 			ctx.Strict(secondPrefix+"UBSAN_RUNTIME_LIBRARY", strings.TrimSuffix(config.UndefinedBehaviorSanitizerRuntimeLibrary(toolchain), ".so"))
 			ctx.Strict(secondPrefix+"UBSAN_MINIMAL_RUNTIME_LIBRARY", strings.TrimSuffix(config.UndefinedBehaviorSanitizerMinimalRuntimeLibrary(toolchain), ".a"))
 			ctx.Strict(secondPrefix+"TSAN_RUNTIME_LIBRARY", strings.TrimSuffix(config.ThreadSanitizerRuntimeLibrary(toolchain), ".so"))
+			ctx.Strict(secondPrefix+"SCUDO_RUNTIME_LIBRARY", strings.TrimSuffix(config.ScudoRuntimeLibrary(toolchain), ".so"))
 		}
 
 		// This is used by external/gentoo/...
@@ -315,10 +320,14 @@
 
 	if target.Os == android.Darwin {
 		ctx.Strict(makePrefix+"AR", "${config.MacArPath}")
+		ctx.Strict(makePrefix+"NM", "${config.MacToolPath}/nm")
+		ctx.Strict(makePrefix+"OTOOL", "${config.MacToolPath}/otool")
+		ctx.Strict(makePrefix+"STRIP", "${config.MacStripPath}")
 	} else {
 		ctx.Strict(makePrefix+"AR", "${config.ClangBin}/llvm-ar")
 		ctx.Strict(makePrefix+"READELF", gccCmd(toolchain, "readelf"))
 		ctx.Strict(makePrefix+"NM", gccCmd(toolchain, "nm"))
+		ctx.Strict(makePrefix+"STRIP", gccCmd(toolchain, "strip"))
 	}
 
 	if target.Os == android.Windows {
@@ -328,7 +337,6 @@
 	if target.Os.Class == android.Device {
 		ctx.Strict(makePrefix+"OBJCOPY", gccCmd(toolchain, "objcopy"))
 		ctx.Strict(makePrefix+"LD", gccCmd(toolchain, "ld"))
-		ctx.Strict(makePrefix+"STRIP", gccCmd(toolchain, "strip"))
 		ctx.Strict(makePrefix+"GCC_VERSION", toolchain.GccVersion())
 		ctx.Strict(makePrefix+"NDK_GCC_VERSION", toolchain.GccVersion())
 		ctx.Strict(makePrefix+"NDK_TRIPLE", config.NDKTriple(toolchain))
diff --git a/cc/ndk_headers.go b/cc/ndk_headers.go
index 9fabc97..1cd4829 100644
--- a/cc/ndk_headers.go
+++ b/cc/ndk_headers.go
@@ -26,7 +26,7 @@
 )
 
 var (
-	preprocessBionicHeaders = pctx.AndroidStaticRule("preprocessBionicHeaders",
+	versionBionicHeaders = pctx.AndroidStaticRule("versionBionicHeaders",
 		blueprint.RuleParams{
 			// The `&& touch $out` isn't really necessary, but Blueprint won't
 			// let us have only implicit outputs.
@@ -34,6 +34,13 @@
 			CommandDeps: []string{"$versionerCmd"},
 		},
 		"depsPath", "srcDir", "outDir")
+
+	preprocessNdkHeader = pctx.AndroidStaticRule("preprocessNdkHeader",
+		blueprint.RuleParams{
+			Command:     "$preprocessor -o $out $in",
+			CommandDeps: []string{"$preprocessor"},
+		},
+		"preprocessor")
 )
 
 func init() {
@@ -45,7 +52,7 @@
 	return getNdkSysrootBase(ctx).Join(ctx, "usr/include")
 }
 
-type headerProperies struct {
+type headerProperties struct {
 	// Base directory of the headers being installed. As an example:
 	//
 	// ndk_headers {
@@ -65,6 +72,9 @@
 	// List of headers to install. Glob compatible. Common case is "include/**/*.h".
 	Srcs []string
 
+	// Source paths that should be excluded from the srcs glob.
+	Exclude_srcs []string
+
 	// Path to the NOTICE file associated with the headers.
 	License *string
 }
@@ -72,7 +82,7 @@
 type headerModule struct {
 	android.ModuleBase
 
-	properties headerProperies
+	properties headerProperties
 
 	installPaths android.Paths
 	licensePath  android.ModuleSrcPath
@@ -128,7 +138,7 @@
 		return
 	}
 
-	srcFiles := ctx.ExpandSources(m.properties.Srcs, nil)
+	srcFiles := ctx.ExpandSources(m.properties.Srcs, m.properties.Exclude_srcs)
 	for _, header := range srcFiles {
 		installDir := getHeaderInstallDir(ctx, header, String(m.properties.From),
 			String(m.properties.To))
@@ -154,10 +164,10 @@
 	return module
 }
 
-type preprocessedHeaderProperies struct {
+type versionedHeaderProperties struct {
 	// Base directory of the headers being installed. As an example:
 	//
-	// preprocessed_ndk_headers {
+	// versioned_ndk_headers {
 	//     name: "foo",
 	//     from: "include",
 	//     to: "",
@@ -181,19 +191,19 @@
 // module does not have the srcs property, and operates on a full directory (the `from` property).
 //
 // Note that this is really only built to handle bionic/libc/include.
-type preprocessedHeaderModule struct {
+type versionedHeaderModule struct {
 	android.ModuleBase
 
-	properties preprocessedHeaderProperies
+	properties versionedHeaderProperties
 
 	installPaths android.Paths
 	licensePath  android.ModuleSrcPath
 }
 
-func (m *preprocessedHeaderModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+func (m *versionedHeaderModule) DepsMutator(ctx android.BottomUpMutatorContext) {
 }
 
-func (m *preprocessedHeaderModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+func (m *versionedHeaderModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	if String(m.properties.License) == "" {
 		ctx.PropertyErrorf("license", "field is required")
 	}
@@ -248,7 +258,7 @@
 
 	timestampFile := android.PathForModuleOut(ctx, "versioner.timestamp")
 	ctx.Build(pctx, android.BuildParams{
-		Rule:            preprocessBionicHeaders,
+		Rule:            versionBionicHeaders,
 		Description:     "versioner preprocess " + srcDir.Rel(),
 		Output:          timestampFile,
 		Implicits:       append(srcFiles, depsGlob...),
@@ -263,8 +273,92 @@
 	return timestampFile
 }
 
+func versionedNdkHeadersFactory() android.Module {
+	module := &versionedHeaderModule{}
+
+	module.AddProperties(&module.properties)
+
+	// Host module rather than device module because device module install steps
+	// do not get run when embedded in make. We're not any of the existing
+	// module types that can be exposed via the Android.mk exporter, so just use
+	// a host module.
+	android.InitAndroidArchModule(module, android.HostSupportedNoCross, android.MultilibFirst)
+
+	return module
+}
+
+// preprocessed_ndk_header {
+//     name: "foo",
+//     preprocessor: "foo.sh",
+//     srcs: [...],
+//     to: "android",
+// }
+//
+// Will invoke the preprocessor as:
+//     $preprocessor -o $SYSROOT/usr/include/android/needs_preproc.h $src
+// For each src in srcs.
+type preprocessedHeadersProperties struct {
+	// The preprocessor to run. Must be a program inside the source directory
+	// with no dependencies.
+	Preprocessor *string
+
+	// Source path to the files to be preprocessed.
+	Srcs []string
+
+	// Source paths that should be excluded from the srcs glob.
+	Exclude_srcs []string
+
+	// Install path within the sysroot. This is relative to usr/include.
+	To *string
+
+	// Path to the NOTICE file associated with the headers.
+	License *string
+}
+
+type preprocessedHeadersModule struct {
+	android.ModuleBase
+
+	properties preprocessedHeadersProperties
+
+	installPaths android.Paths
+	licensePath  android.ModuleSrcPath
+}
+
+func (m *preprocessedHeadersModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+}
+
+func (m *preprocessedHeadersModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	if String(m.properties.License) == "" {
+		ctx.PropertyErrorf("license", "field is required")
+	}
+
+	preprocessor := android.PathForModuleSrc(ctx, String(m.properties.Preprocessor))
+	m.licensePath = android.PathForModuleSrc(ctx, String(m.properties.License))
+
+	srcFiles := ctx.ExpandSources(m.properties.Srcs, m.properties.Exclude_srcs)
+	installDir := getCurrentIncludePath(ctx).Join(ctx, String(m.properties.To))
+	for _, src := range srcFiles {
+		installPath := installDir.Join(ctx, src.Base())
+		m.installPaths = append(m.installPaths, installPath)
+
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        preprocessNdkHeader,
+			Description: "preprocess " + src.Rel(),
+			Input:       src,
+			Output:      installPath,
+			Args: map[string]string{
+				"preprocessor": preprocessor.String(),
+			},
+		})
+	}
+
+	if len(m.installPaths) == 0 {
+		ctx.ModuleErrorf("srcs %q matched zero files", m.properties.Srcs)
+	}
+}
+
 func preprocessedNdkHeadersFactory() android.Module {
-	module := &preprocessedHeaderModule{}
+	module := &preprocessedHeadersModule{}
 
 	module.AddProperties(&module.properties)
 
diff --git a/cc/ndk_sysroot.go b/cc/ndk_sysroot.go
index c7ba588..144fc09 100644
--- a/cc/ndk_sysroot.go
+++ b/cc/ndk_sysroot.go
@@ -59,6 +59,7 @@
 func init() {
 	android.RegisterModuleType("ndk_headers", ndkHeadersFactory)
 	android.RegisterModuleType("ndk_library", ndkLibraryFactory)
+	android.RegisterModuleType("versioned_ndk_headers", versionedNdkHeadersFactory)
 	android.RegisterModuleType("preprocessed_ndk_headers", preprocessedNdkHeadersFactory)
 	android.RegisterSingletonType("ndk", NdkSingleton)
 
@@ -107,7 +108,12 @@
 			licensePaths = append(licensePaths, m.licensePath)
 		}
 
-		if m, ok := module.(*preprocessedHeaderModule); ok {
+		if m, ok := module.(*versionedHeaderModule); ok {
+			installPaths = append(installPaths, m.installPaths...)
+			licensePaths = append(licensePaths, m.licensePath)
+		}
+
+		if m, ok := module.(*preprocessedHeadersModule); ok {
 			installPaths = append(installPaths, m.installPaths...)
 			licensePaths = append(licensePaths, m.licensePath)
 		}
diff --git a/cc/pgo.go b/cc/pgo.go
index d39e429..a341ab9 100644
--- a/cc/pgo.go
+++ b/cc/pgo.go
@@ -19,6 +19,8 @@
 	"path/filepath"
 	"strings"
 
+	"github.com/google/blueprint/proptools"
+
 	"android/soong/android"
 	"android/soong/cc/config"
 )
@@ -160,13 +162,8 @@
 		return flags
 	}
 
-	// Skip -fprofile-use if 'enable_profile_use' property is set
-	if props.Pgo.Enable_profile_use != nil && *props.Pgo.Enable_profile_use == false {
-		return flags
-	}
-
-	// If the profile file is found, add flags to use the profile
-	if profileFile := props.getPgoProfileFile(ctx); profileFile.Valid() {
+	if props.PgoCompile {
+		profileFile := props.getPgoProfileFile(ctx)
 		profileFilePath := profileFile.Path()
 		profileUseFlags := props.profileUseFlags(ctx, profileFilePath.String())
 
@@ -257,7 +254,8 @@
 		}
 	}
 
-	if !ctx.Config().IsEnvTrue("ANDROID_PGO_NO_PROFILE_USE") {
+	if !ctx.Config().IsEnvTrue("ANDROID_PGO_NO_PROFILE_USE") &&
+		proptools.BoolDefault(pgo.Properties.Pgo.Enable_profile_use, true) {
 		if profileFile := pgo.Properties.getPgoProfileFile(ctx); profileFile.Valid() {
 			pgo.Properties.PgoCompile = true
 		}
diff --git a/cc/proto.go b/cc/proto.go
index 22e50ab..6e6f95e 100644
--- a/cc/proto.go
+++ b/cc/proto.go
@@ -25,13 +25,17 @@
 
 func init() {
 	pctx.HostBinToolVariable("protocCmd", "aprotoc")
+	pctx.HostBinToolVariable("depFixCmd", "dep_fixer")
 }
 
 var (
 	proto = pctx.AndroidStaticRule("protoc",
 		blueprint.RuleParams{
-			Command:     "$protocCmd --cpp_out=$protoOutParams:$outDir -I $protoBase $protoFlags $in",
-			CommandDeps: []string{"$protocCmd"},
+			Command: "$protocCmd --cpp_out=$protoOutParams:$outDir --dependency_out=$out.d -I $protoBase $protoFlags $in && " +
+				`$depFixCmd $out.d`,
+			CommandDeps: []string{"$protocCmd", "$depFixCmd"},
+			Depfile:     "${out}.d",
+			Deps:        blueprint.DepsGCC,
 		}, "protoFlags", "protoOutParams", "protoBase", "outDir")
 )
 
@@ -53,10 +57,11 @@
 	}
 
 	ctx.Build(pctx, android.BuildParams{
-		Rule:        proto,
-		Description: "protoc " + protoFile.Rel(),
-		Outputs:     android.WritablePaths{ccFile, headerFile},
-		Input:       protoFile,
+		Rule:           proto,
+		Description:    "protoc " + protoFile.Rel(),
+		Output:         ccFile,
+		ImplicitOutput: headerFile,
+		Input:          protoFile,
 		Args: map[string]string{
 			"outDir":         android.ProtoDir(ctx).String(),
 			"protoFlags":     protoFlags,
diff --git a/cc/relocation_packer.go b/cc/relocation_packer.go
index f6a1fe4..8989b29 100644
--- a/cc/relocation_packer.go
+++ b/cc/relocation_packer.go
@@ -33,6 +33,7 @@
 	})
 
 type RelocationPackerProperties struct {
+	// Generate compact dynamic relocation table, default true.
 	Pack_relocations *bool `android:"arch_variant"`
 
 	// This will be true even if we're embedded in Make, in which case
diff --git a/cc/sabi.go b/cc/sabi.go
index f5a7c77..42b2f35 100644
--- a/cc/sabi.go
+++ b/cc/sabi.go
@@ -74,8 +74,13 @@
 
 	// RSClang does not support recent mcpu option likes exynos-m2.
 	// So we need overriding mcpu option when we want to use it.
-	if ctx.Arch().CpuVariant == "exynos-m2" {
-		flags.ToolingCFlags = append(flags.ToolingCFlags, "-mcpu=cortex-a53")
+	mappedArch := map[string]string{
+		"exynos-m2":  "cortex-a53",
+		"cortex-a55": "cortex-a53",
+		"cortex-a75": "cortex-a57",
+	}
+	if arch, ok := mappedArch[ctx.Arch().CpuVariant]; ok {
+		flags.ToolingCFlags = append(flags.ToolingCFlags, "-mcpu="+arch)
 	}
 
 	return flags
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 859d876..4c8a611 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -21,6 +21,8 @@
 	"strings"
 	"sync"
 
+	"github.com/google/blueprint"
+
 	"android/soong/android"
 	"android/soong/cc/config"
 )
@@ -94,6 +96,7 @@
 		Safestack        *bool    `android:"arch_variant"`
 		Cfi              *bool    `android:"arch_variant"`
 		Integer_overflow *bool    `android:"arch_variant"`
+		Scudo            *bool    `android:"arch_variant"`
 
 		// Sanitizers to run in the diagnostic mode (as opposed to the release mode).
 		// Replaces abort() on error with a human-readable error message.
@@ -152,7 +155,9 @@
 
 	if ctx.clang() {
 		if ctx.Host() {
-			globalSanitizers = ctx.Config().SanitizeHost()
+			if !ctx.Windows() {
+				globalSanitizers = ctx.Config().SanitizeHost()
+			}
 		} else {
 			arches := ctx.Config().SanitizeDeviceArch()
 			if len(arches) == 0 || inList(ctx.Arch().ArchType.Name, arches) {
@@ -207,6 +212,10 @@
 			}
 		}
 
+		if found, globalSanitizers = removeFromList("scudo", globalSanitizers); found && s.Scudo == nil {
+			s.Scudo = boolPtr(true)
+		}
+
 		if len(globalSanitizers) > 0 {
 			ctx.ModuleErrorf("unknown global sanitizer option %s", globalSanitizers[0])
 		}
@@ -281,10 +290,16 @@
 	}
 
 	if ctx.Os() != android.Windows && (Bool(s.All_undefined) || Bool(s.Undefined) || Bool(s.Address) || Bool(s.Thread) ||
-		Bool(s.Coverage) || Bool(s.Safestack) || Bool(s.Cfi) || Bool(s.Integer_overflow) || len(s.Misc_undefined) > 0) {
+		Bool(s.Coverage) || Bool(s.Safestack) || Bool(s.Cfi) || Bool(s.Integer_overflow) || len(s.Misc_undefined) > 0 ||
+		Bool(s.Scudo)) {
 		sanitize.Properties.SanitizerEnabled = true
 	}
 
+	// Disable Scudo if ASan or TSan is enabled.
+	if Bool(s.Address) || Bool(s.Thread) {
+		s.Scudo = nil
+	}
+
 	if Bool(s.Coverage) {
 		if !Bool(s.Address) {
 			ctx.ModuleErrorf(`Use of "coverage" also requires "address"`)
@@ -307,10 +322,12 @@
 }
 
 func (sanitize *sanitize) flags(ctx ModuleContext, flags Flags) Flags {
-	minimalRuntimePath := "${config.ClangAsanLibDir}/" + config.UndefinedBehaviorSanitizerMinimalRuntimeLibrary(ctx.toolchain()) + ".a"
+	minimalRuntimeLib := config.UndefinedBehaviorSanitizerMinimalRuntimeLibrary(ctx.toolchain()) + ".a"
+	minimalRuntimePath := "${config.ClangAsanLibDir}/" + minimalRuntimeLib
 
 	if ctx.Device() && sanitize.Properties.MinimalRuntimeDep {
 		flags.LdFlags = append(flags.LdFlags, minimalRuntimePath)
+		flags.LdFlags = append(flags.LdFlags, "-Wl,--exclude-libs,"+minimalRuntimeLib)
 	}
 	if !sanitize.Properties.SanitizerEnabled && !sanitize.Properties.UbsanRuntimeDep {
 		return flags
@@ -432,6 +449,10 @@
 		}
 	}
 
+	if Bool(sanitize.Properties.Sanitize.Scudo) {
+		sanitizers = append(sanitizers, "scudo")
+	}
+
 	if len(sanitizers) > 0 {
 		sanitizeArg := "-fsanitize=" + strings.Join(sanitizers, ",")
 
@@ -448,6 +469,7 @@
 			if enableMinimalRuntime(sanitize) {
 				flags.CFlags = append(flags.CFlags, strings.Join(minimalRuntimeFlags, " "))
 				flags.libFlags = append([]string{minimalRuntimePath}, flags.libFlags...)
+				flags.LdFlags = append(flags.LdFlags, "-Wl,--exclude-libs,"+minimalRuntimeLib)
 			}
 		}
 	}
@@ -468,6 +490,8 @@
 		runtimeLibrary = config.AddressSanitizerRuntimeLibrary(ctx.toolchain())
 	} else if Bool(sanitize.Properties.Sanitize.Thread) {
 		runtimeLibrary = config.ThreadSanitizerRuntimeLibrary(ctx.toolchain())
+	} else if Bool(sanitize.Properties.Sanitize.Scudo) {
+		runtimeLibrary = config.ScudoRuntimeLibrary(ctx.toolchain())
 	} else if len(diagSanitizers) > 0 || sanitize.Properties.UbsanRuntimeDep {
 		runtimeLibrary = config.UndefinedBehaviorSanitizerRuntimeLibrary(ctx.toolchain())
 	}
@@ -539,6 +563,11 @@
 		!sanitize.isSanitizerEnabled(cfi)
 }
 
+func (sanitize *sanitize) isVariantOnProductionDevice() bool {
+	return !sanitize.isSanitizerEnabled(asan) &&
+		!sanitize.isSanitizerEnabled(tsan)
+}
+
 func (sanitize *sanitize) SetSanitizer(t sanitizerType, b bool) {
 	switch t {
 	case asan:
@@ -552,7 +581,6 @@
 		sanitize.Properties.Sanitize.Integer_overflow = boolPtr(b)
 	case cfi:
 		sanitize.Properties.Sanitize.Cfi = boolPtr(b)
-		sanitize.Properties.Sanitize.Diag.Cfi = boolPtr(b)
 	default:
 		panic(fmt.Errorf("unknown sanitizerType %d", t))
 	}
@@ -586,43 +614,54 @@
 	return sanitizerVal != nil && *sanitizerVal == true
 }
 
+func isSanitizableDependencyTag(tag blueprint.DependencyTag) bool {
+	t, ok := tag.(dependencyTag)
+	return ok && t.library || t == reuseObjTag
+}
+
 // Propagate asan requirements down from binaries
 func sanitizerDepsMutator(t sanitizerType) func(android.TopDownMutatorContext) {
 	return func(mctx android.TopDownMutatorContext) {
 		if c, ok := mctx.Module().(*Module); ok && c.sanitize.isSanitizerEnabled(t) {
-			mctx.VisitDepsDepthFirst(func(module android.Module) {
-				if d, ok := module.(*Module); ok && d.sanitize != nil &&
+			mctx.WalkDeps(func(child, parent android.Module) bool {
+				if !isSanitizableDependencyTag(mctx.OtherModuleDependencyTag(child)) {
+					return false
+				}
+				if d, ok := child.(*Module); ok && d.sanitize != nil &&
 					!Bool(d.sanitize.Properties.Sanitize.Never) &&
 					!d.sanitize.isSanitizerExplicitlyDisabled(t) {
 					if (t == cfi && d.static()) || t != cfi {
 						d.sanitize.Properties.SanitizeDep = true
 					}
 				}
+				return true
 			})
 		}
 	}
 }
 
 // Propagate the ubsan minimal runtime dependency when there are integer overflow sanitized static dependencies.
-func sanitizerRuntimeDepsMutator() func(android.TopDownMutatorContext) {
-	return func(mctx android.TopDownMutatorContext) {
-		if c, ok := mctx.Module().(*Module); ok && c.sanitize != nil {
-			mctx.VisitDepsDepthFirst(func(module android.Module) {
-				if d, ok := module.(*Module); ok && d.static() && d.sanitize != nil {
+func sanitizerRuntimeDepsMutator(mctx android.TopDownMutatorContext) {
+	if c, ok := mctx.Module().(*Module); ok && c.sanitize != nil {
+		mctx.WalkDeps(func(child, parent android.Module) bool {
+			if !isSanitizableDependencyTag(mctx.OtherModuleDependencyTag(child)) {
+				return false
+			}
+			if d, ok := child.(*Module); ok && d.static() && d.sanitize != nil {
 
-					if enableMinimalRuntime(d.sanitize) {
-						// If a static dependency is built with the minimal runtime,
-						// make sure we include the ubsan minimal runtime.
-						c.sanitize.Properties.MinimalRuntimeDep = true
-					} else if Bool(d.sanitize.Properties.Sanitize.Diag.Integer_overflow) ||
-						len(d.sanitize.Properties.Sanitize.Diag.Misc_undefined) > 0 {
-						// If a static dependency runs with full ubsan diagnostics,
-						// make sure we include the ubsan runtime.
-						c.sanitize.Properties.UbsanRuntimeDep = true
-					}
+				if enableMinimalRuntime(d.sanitize) {
+					// If a static dependency is built with the minimal runtime,
+					// make sure we include the ubsan minimal runtime.
+					c.sanitize.Properties.MinimalRuntimeDep = true
+				} else if Bool(d.sanitize.Properties.Sanitize.Diag.Integer_overflow) ||
+					len(d.sanitize.Properties.Sanitize.Diag.Misc_undefined) > 0 {
+					// If a static dependency runs with full ubsan diagnostics,
+					// make sure we include the ubsan runtime.
+					c.sanitize.Properties.UbsanRuntimeDep = true
 				}
-			})
-		}
+			}
+			return true
+		})
 	}
 }
 
@@ -698,6 +737,7 @@
 
 func enableMinimalRuntime(sanitize *sanitize) bool {
 	if !Bool(sanitize.Properties.Sanitize.Address) &&
+		!Bool(sanitize.Properties.Sanitize.Scudo) &&
 		(Bool(sanitize.Properties.Sanitize.Integer_overflow) ||
 			len(sanitize.Properties.Sanitize.Misc_undefined) > 0) &&
 		!(Bool(sanitize.Properties.Sanitize.Diag.Integer_overflow) ||
diff --git a/cc/test.go b/cc/test.go
index fef6367..5d0ef20 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -168,13 +168,17 @@
 }
 
 func (test *testDecorator) linkerInit(ctx BaseModuleContext, linker *baseLinker) {
-	// add ../../lib[64] to rpath so that out/host/linux-x86/nativetest/<test dir>/<test> can
+	// 1. Add ../../lib[64] to rpath so that out/host/linux-x86/nativetest/<test dir>/<test> can
 	// find out/host/linux-x86/lib[64]/library.so
-	runpath := "../../lib"
-	if ctx.toolchain().Is64Bit() {
-		runpath += "64"
+	// 2. Add ../../../lib[64] to rpath so that out/host/linux-x86/testcases/<test dir>/<CPU>/<test> can
+	// also find out/host/linux-x86/lib[64]/library.so
+	runpaths := []string{"../../lib", "../../../lib"}
+	for _, runpath := range runpaths {
+		if ctx.toolchain().Is64Bit() {
+			runpath += "64"
+		}
+		linker.dynamicProperties.RunPaths = append(linker.dynamicProperties.RunPaths, runpath)
 	}
-	linker.dynamicProperties.RunPaths = append(linker.dynamicProperties.RunPaths, runpath)
 
 	// add "" to rpath so that test binaries can find libraries in their own test directory
 	linker.dynamicProperties.RunPaths = append(linker.dynamicProperties.RunPaths, "")
diff --git a/cc/tidy.go b/cc/tidy.go
index 8ca94ef..491cc22 100644
--- a/cc/tidy.go
+++ b/cc/tidy.go
@@ -83,9 +83,21 @@
 		flags.TidyFlags = append(flags.TidyFlags, "-extra-arg-before=-fno-caret-diagnostics")
 	}
 
-	// We might be using the static analyzer through clang tidy.
-	// https://bugs.llvm.org/show_bug.cgi?id=32914
-	flags.TidyFlags = append(flags.TidyFlags, "-extra-arg-before=-D__clang_analyzer__")
+	extraArgFlags := []string{
+		// We might be using the static analyzer through clang tidy.
+		// https://bugs.llvm.org/show_bug.cgi?id=32914
+		"-D__clang_analyzer__",
+
+		// A recent change in clang-tidy (r328258) enabled destructor inlining, which
+		// appears to cause a number of false positives. Until that's resolved, this turns
+		// off the effects of r328258.
+		// https://bugs.llvm.org/show_bug.cgi?id=37459
+		"-Xclang", "-analyzer-config", "-Xclang", "c++-temp-dtor-inlining=false",
+	}
+
+	for _, f := range extraArgFlags {
+		flags.TidyFlags = append(flags.TidyFlags, "-extra-arg-before="+f)
+	}
 
 	tidyChecks := "-checks="
 	if checks := ctx.Config().TidyChecks(); len(checks) > 0 {
diff --git a/cc/vndk_prebuilt.go b/cc/vndk_prebuilt.go
index 9c9545d..849bb3f 100644
--- a/cc/vndk_prebuilt.go
+++ b/cc/vndk_prebuilt.go
@@ -21,7 +21,8 @@
 )
 
 var (
-	vndkSuffix = ".vndk."
+	vndkSuffix     = ".vndk."
+	binder32Suffix = ".binder32"
 )
 
 // Creates vndk prebuilts that include the VNDK version.
@@ -53,6 +54,10 @@
 	// Target arch name of the snapshot (e.g. 'arm64' for variant 'aosp_arm64_ab')
 	Target_arch *string
 
+	// If the prebuilt snapshot lib is built with 32 bit binder, this must be set to true.
+	// The lib with 64 bit binder does not need to set this property.
+	Binder32bit *bool
+
 	// Prebuilt files for each arch.
 	Srcs []string `android:"arch_variant"`
 }
@@ -67,10 +72,14 @@
 }
 
 func (p *vndkPrebuiltLibraryDecorator) NameSuffix() string {
+	suffix := p.version()
 	if p.arch() != "" {
-		return vndkSuffix + p.version() + "." + p.arch()
+		suffix += "." + p.arch()
 	}
-	return vndkSuffix + p.version()
+	if Bool(p.properties.Binder32bit) {
+		suffix += binder32Suffix
+	}
+	return vndkSuffix + suffix
 }
 
 func (p *vndkPrebuiltLibraryDecorator) version() string {
@@ -81,6 +90,13 @@
 	return String(p.properties.Target_arch)
 }
 
+func (p *vndkPrebuiltLibraryDecorator) binderBit() string {
+	if Bool(p.properties.Binder32bit) {
+		return "32"
+	}
+	return "64"
+}
+
 func (p *vndkPrebuiltLibraryDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
 	p.libraryDecorator.libName = strings.TrimSuffix(ctx.ModuleName(), p.NameSuffix())
 	return p.libraryDecorator.linkerFlags(ctx, flags)
@@ -114,6 +130,9 @@
 	if len(arches) == 0 || arches[0].ArchType.String() != p.arch() {
 		return
 	}
+	if ctx.DeviceConfig().BinderBitness() != p.binderBit() {
+		return
+	}
 	if p.shared() {
 		if ctx.isVndkSp() {
 			p.baseInstaller.subDir = "vndk-sp-" + p.version()
diff --git a/ui/build/util_linux.go b/cmd/dep_fixer/Android.bp
similarity index 71%
copy from ui/build/util_linux.go
copy to cmd/dep_fixer/Android.bp
index 0a4e1d2..d2d1113 100644
--- a/ui/build/util_linux.go
+++ b/cmd/dep_fixer/Android.bp
@@ -1,4 +1,4 @@
-// Copyright 2017 Google Inc. All rights reserved.
+// Copyright 2018 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.
@@ -12,10 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package build
-
-import (
-	"syscall"
-)
-
-const ioctlGetTermios = syscall.TCGETS
+blueprint_go_binary {
+    name: "dep_fixer",
+    deps: ["androidmk-parser"],
+    srcs: [
+        "main.go",
+        "deps.go",
+    ],
+    testSrcs: ["deps_test.go"],
+}
diff --git a/cmd/dep_fixer/deps.go b/cmd/dep_fixer/deps.go
new file mode 100644
index 0000000..64c97f5
--- /dev/null
+++ b/cmd/dep_fixer/deps.go
@@ -0,0 +1,95 @@
+// Copyright 2018 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 main
+
+import (
+	"bytes"
+	"fmt"
+	"io"
+	"strings"
+
+	"android/soong/androidmk/parser"
+)
+
+type Deps struct {
+	Output string
+	Inputs []string
+}
+
+func Parse(filename string, r io.Reader) (*Deps, error) {
+	p := parser.NewParser(filename, r)
+	nodes, errs := p.Parse()
+
+	if len(errs) == 1 {
+		return nil, errs[0]
+	} else if len(errs) > 1 {
+		return nil, fmt.Errorf("many errors: %v", errs)
+	}
+
+	pos := func(node parser.Node) string {
+		return p.Unpack(node.Pos()).String() + ": "
+	}
+
+	ret := &Deps{}
+
+	for _, node := range nodes {
+		switch x := node.(type) {
+		case *parser.Comment:
+			// Do nothing
+		case *parser.Rule:
+			if x.Recipe != "" {
+				return nil, fmt.Errorf("%sunexpected recipe in rule: %v", pos(node), x)
+			}
+
+			if !x.Target.Const() {
+				return nil, fmt.Errorf("%sunsupported variable expansion: %v", pos(node), x.Target.Dump())
+			}
+			outputs := x.Target.Words()
+			if len(outputs) == 0 {
+				return nil, fmt.Errorf("%smissing output: %v", pos(node), x)
+			}
+			ret.Output = outputs[0].Value(nil)
+
+			if !x.Prerequisites.Const() {
+				return nil, fmt.Errorf("%sunsupported variable expansion: %v", pos(node), x.Prerequisites.Dump())
+			}
+			for _, input := range x.Prerequisites.Words() {
+				ret.Inputs = append(ret.Inputs, input.Value(nil))
+			}
+		default:
+			return nil, fmt.Errorf("%sunexpected line: %#v", pos(node), node)
+		}
+	}
+
+	return ret, nil
+}
+
+func (d *Deps) Print() []byte {
+	// We don't really have to escape every \, but it's simpler,
+	// and ninja will handle it.
+	replacer := strings.NewReplacer(" ", "\\ ",
+		":", "\\:",
+		"#", "\\#",
+		"$", "$$",
+		"\\", "\\\\")
+
+	b := &bytes.Buffer{}
+	fmt.Fprintf(b, "%s:", replacer.Replace(d.Output))
+	for _, input := range d.Inputs {
+		fmt.Fprintf(b, " %s", replacer.Replace(input))
+	}
+	fmt.Fprintln(b)
+	return b.Bytes()
+}
diff --git a/cmd/dep_fixer/deps_test.go b/cmd/dep_fixer/deps_test.go
new file mode 100644
index 0000000..0a779b7
--- /dev/null
+++ b/cmd/dep_fixer/deps_test.go
@@ -0,0 +1,389 @@
+// Copyright 2018 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 main
+
+import (
+	"bytes"
+	"io"
+	"io/ioutil"
+	"os"
+	"testing"
+)
+
+func TestParse(t *testing.T) {
+	testCases := []struct {
+		name   string
+		input  string
+		output Deps
+		err    error
+	}{
+		// These come from the ninja test suite
+		{
+			name:  "Basic",
+			input: "build/ninja.o: ninja.cc ninja.h eval_env.h manifest_parser.h",
+			output: Deps{
+				Output: "build/ninja.o",
+				Inputs: []string{
+					"ninja.cc",
+					"ninja.h",
+					"eval_env.h",
+					"manifest_parser.h",
+				},
+			},
+		},
+		{
+			name: "EarlyNewlineAndWhitespace",
+			input: ` \
+  out: in`,
+			output: Deps{
+				Output: "out",
+				Inputs: []string{"in"},
+			},
+		},
+		{
+			name: "Continuation",
+			input: `foo.o: \
+  bar.h baz.h
+`,
+			output: Deps{
+				Output: "foo.o",
+				Inputs: []string{"bar.h", "baz.h"},
+			},
+		},
+		{
+			name:  "CarriageReturnContinuation",
+			input: "foo.o: \\\r\n  bar.h baz.h\r\n",
+			output: Deps{
+				Output: "foo.o",
+				Inputs: []string{"bar.h", "baz.h"},
+			},
+		},
+		{
+			name: "BackSlashes",
+			input: `Project\Dir\Build\Release8\Foo\Foo.res : \
+  Dir\Library\Foo.rc \
+  Dir\Library\Version\Bar.h \
+  Dir\Library\Foo.ico \
+  Project\Thing\Bar.tlb \
+`,
+			output: Deps{
+				Output: `Project\Dir\Build\Release8\Foo\Foo.res`,
+				Inputs: []string{
+					`Dir\Library\Foo.rc`,
+					`Dir\Library\Version\Bar.h`,
+					`Dir\Library\Foo.ico`,
+					`Project\Thing\Bar.tlb`,
+				},
+			},
+		},
+		{
+			name:  "Spaces",
+			input: `a\ bc\ def:   a\ b c d`,
+			output: Deps{
+				Output: `a bc def`,
+				Inputs: []string{"a b", "c", "d"},
+			},
+		},
+		{
+			name:  "Escapes",
+			input: `\!\@\#$$\%\^\&\\:`,
+			output: Deps{
+				Output: `\!\@#$\%\^\&\`,
+			},
+		},
+		{
+			name: "SpecialChars",
+			// Ninja includes a number of '=', but our parser can't handle that,
+			// since it sees the equals and switches over to assuming it's an
+			// assignment.
+			//
+			// We don't have any files in our tree that contain an '=' character,
+			// and Kati can't handle parsing this either, so for now I'm just
+			// going to remove all the '=' characters below.
+			//
+			// It looks like make will only do this for the first
+			// dependency, but not later dependencies.
+			input: `C\:/Program\ Files\ (x86)/Microsoft\ crtdefs.h: \
+ en@quot.header~ t+t-x!1 \
+ openldap/slapd.d/cnconfig/cnschema/cn{0}core.ldif \
+ Fu` + "\303\244ball",
+			output: Deps{
+				Output: "C:/Program Files (x86)/Microsoft crtdefs.h",
+				Inputs: []string{
+					"en@quot.header~",
+					"t+t-x!1",
+					"openldap/slapd.d/cnconfig/cnschema/cn{0}core.ldif",
+					"Fu\303\244ball",
+				},
+			},
+		},
+		// Ninja's UnifyMultipleOutputs and RejectMultipleDifferentOutputs tests have been omitted,
+		// since we don't want the same behavior.
+
+		// Our own tests
+		{
+			name: "Multiple outputs",
+			input: `a b: c
+a: d
+b: e`,
+			output: Deps{
+				Output: "b",
+				Inputs: []string{
+					"c",
+					"d",
+					"e",
+				},
+			},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			out, err := Parse("test.d", bytes.NewBufferString(tc.input))
+			if err != tc.err {
+				t.Fatalf("Unexpected error: %v (expected %v)", err, tc.err)
+			}
+
+			if out.Output != tc.output.Output {
+				t.Errorf("output file doesn't match:\n"+
+					" str: %#v\n"+
+					"want: %#v\n"+
+					" got: %#v", tc.input, tc.output.Output, out.Output)
+			}
+
+			matches := true
+			if len(out.Inputs) != len(tc.output.Inputs) {
+				matches = false
+			} else {
+				for i := range out.Inputs {
+					if out.Inputs[i] != tc.output.Inputs[i] {
+						matches = false
+					}
+				}
+			}
+			if !matches {
+				t.Errorf("input files don't match:\n"+
+					" str: %#v\n"+
+					"want: %#v\n"+
+					" got: %#v", tc.input, tc.output.Inputs, out.Inputs)
+			}
+		})
+	}
+}
+
+func BenchmarkParsing(b *testing.B) {
+	// Write it out to a file to most closely match ninja's perftest
+	tmpfile, err := ioutil.TempFile("", "depfile")
+	if err != nil {
+		b.Fatal("Failed to create temp file:", err)
+	}
+	defer os.Remove(tmpfile.Name())
+	_, err = io.WriteString(tmpfile, `out/soong/.intermediates/external/ninja/ninja/linux_glibc_x86_64/obj/external/ninja/src/ninja.o: \
+  external/ninja/src/ninja.cc external/libcxx/include/errno.h \
+  external/libcxx/include/__config \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/features.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/predefs.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/sys/cdefs.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/wordsize.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/gnu/stubs.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/gnu/stubs-64.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/errno.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/errno.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/linux/errno.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/asm/errno.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/asm-generic/errno.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/asm-generic/errno-base.h \
+  external/libcxx/include/limits.h \
+  prebuilts/clang/host/linux-x86/clang-4639204/lib64/clang/6.0.1/include/limits.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/limits.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/posix1_lim.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/local_lim.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/linux/limits.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/posix2_lim.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/xopen_lim.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/stdio_lim.h \
+  external/libcxx/include/stdio.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/stdio.h \
+  external/libcxx/include/stddef.h \
+  prebuilts/clang/host/linux-x86/clang-4639204/lib64/clang/6.0.1/include/stddef.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/types.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/typesizes.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/libio.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/_G_config.h \
+  external/libcxx/include/wchar.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/wchar.h \
+  prebuilts/clang/host/linux-x86/clang-4639204/lib64/clang/6.0.1/include/stdarg.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/sys_errlist.h \
+  external/libcxx/include/stdlib.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/stdlib.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/waitflags.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/waitstatus.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/endian.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/endian.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/byteswap.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/xlocale.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/sys/types.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/time.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/sys/select.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/select.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/sigset.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/time.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/select2.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/sys/sysmacros.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/alloca.h \
+  external/libcxx/include/string.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/string.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/getopt.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/unistd.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/posix_opt.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/environments.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/confname.h \
+  external/ninja/src/browse.h external/ninja/src/build.h \
+  external/libcxx/include/cstdio external/libcxx/include/map \
+  external/libcxx/include/__tree external/libcxx/include/iterator \
+  external/libcxx/include/iosfwd \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/wchar.h \
+  external/libcxx/include/__functional_base \
+  external/libcxx/include/type_traits external/libcxx/include/cstddef \
+  prebuilts/clang/host/linux-x86/clang-4639204/lib64/clang/6.0.1/include/__stddef_max_align_t.h \
+  external/libcxx/include/__nullptr external/libcxx/include/typeinfo \
+  external/libcxx/include/exception external/libcxx/include/cstdlib \
+  external/libcxx/include/cstdint external/libcxx/include/stdint.h \
+  prebuilts/clang/host/linux-x86/clang-4639204/lib64/clang/6.0.1/include/stdint.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/stdint.h \
+  external/libcxx/include/new external/libcxx/include/utility \
+  external/libcxx/include/__tuple \
+  external/libcxx/include/initializer_list \
+  external/libcxx/include/cstring external/libcxx/include/__debug \
+  external/libcxx/include/memory external/libcxx/include/limits \
+  external/libcxx/include/__undef_macros external/libcxx/include/tuple \
+  external/libcxx/include/stdexcept external/libcxx/include/cassert \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/assert.h \
+  external/libcxx/include/atomic external/libcxx/include/algorithm \
+  external/libcxx/include/functional external/libcxx/include/queue \
+  external/libcxx/include/deque external/libcxx/include/__split_buffer \
+  external/libcxx/include/vector external/libcxx/include/__bit_reference \
+  external/libcxx/include/climits external/libcxx/include/set \
+  external/libcxx/include/string external/libcxx/include/string_view \
+  external/libcxx/include/__string external/libcxx/include/cwchar \
+  external/libcxx/include/cwctype external/libcxx/include/cctype \
+  external/libcxx/include/ctype.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/ctype.h \
+  external/libcxx/include/wctype.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/wctype.h \
+  external/ninja/src/graph.h external/ninja/src/eval_env.h \
+  external/ninja/src/string_piece.h external/ninja/src/timestamp.h \
+  external/ninja/src/util.h external/ninja/src/exit_status.h \
+  external/ninja/src/line_printer.h external/ninja/src/metrics.h \
+  external/ninja/src/build_log.h external/ninja/src/hash_map.h \
+  external/libcxx/include/unordered_map \
+  external/libcxx/include/__hash_table external/libcxx/include/cmath \
+  external/libcxx/include/math.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/math.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/huge_val.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/huge_valf.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/huge_vall.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/inf.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/nan.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/mathdef.h \
+  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/mathcalls.h \
+  external/ninja/src/deps_log.h external/ninja/src/clean.h \
+  external/ninja/src/debug_flags.h external/ninja/src/disk_interface.h \
+  external/ninja/src/graphviz.h external/ninja/src/manifest_parser.h \
+  external/ninja/src/lexer.h external/ninja/src/state.h \
+  external/ninja/src/version.h`)
+	tmpfile.Close()
+	if err != nil {
+		b.Fatal("Failed to write dep file:", err)
+	}
+	b.ResetTimer()
+
+	for n := 0; n < b.N; n++ {
+		depfile, err := ioutil.ReadFile(tmpfile.Name())
+		if err != nil {
+			b.Fatal("Failed to read dep file:", err)
+		}
+
+		_, err = Parse(tmpfile.Name(), bytes.NewBuffer(depfile))
+		if err != nil {
+			b.Fatal("Failed to parse:", err)
+		}
+	}
+}
+
+func TestDepPrint(t *testing.T) {
+	testCases := []struct {
+		name   string
+		input  Deps
+		output string
+	}{
+		{
+			name: "Empty",
+			input: Deps{
+				Output: "a",
+			},
+			output: "a:",
+		},
+		{
+			name: "Basic",
+			input: Deps{
+				Output: "a",
+				Inputs: []string{"b", "c"},
+			},
+			output: "a: b c",
+		},
+		{
+			name: "Escapes",
+			input: Deps{
+				Output: `\!\@#$\%\^\&\`,
+			},
+			output: `\\!\\@\#$$\\%\\^\\&\\:`,
+		},
+		{
+			name: "Spaces",
+			input: Deps{
+				Output: "a b",
+				Inputs: []string{"c d", "e f "},
+			},
+			output: `a\ b: c\ d e\ f\ `,
+		},
+		{
+			name: "SpecialChars",
+			input: Deps{
+				Output: "C:/Program Files (x86)/Microsoft crtdefs.h",
+				Inputs: []string{
+					"en@quot.header~",
+					"t+t-x!1",
+					"openldap/slapd.d/cnconfig/cnschema/cn{0}core.ldif",
+					"Fu\303\244ball",
+				},
+			},
+			output: `C\:/Program\ Files\ (x86)/Microsoft\ crtdefs.h: en@quot.header~ t+t-x!1 openldap/slapd.d/cnconfig/cnschema/cn{0}core.ldif Fu` + "\303\244ball",
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			out := tc.input.Print()
+			outStr := string(out)
+			want := tc.output + "\n"
+
+			if outStr != want {
+				t.Errorf("output doesn't match:\nwant:%q\n got:%q", want, outStr)
+			}
+		})
+	}
+}
diff --git a/cmd/dep_fixer/main.go b/cmd/dep_fixer/main.go
new file mode 100644
index 0000000..0647fb2
--- /dev/null
+++ b/cmd/dep_fixer/main.go
@@ -0,0 +1,67 @@
+// Copyright 2018 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.
+
+// This tool reads "make"-like dependency files, and outputs a canonical version
+// that can be used by ninja. Ninja doesn't support multiple output files (even
+// though it doesn't care what the output file is, or whether it matches what is
+// expected).
+package main
+
+import (
+	"bytes"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"log"
+	"os"
+)
+
+func main() {
+	flag.Usage = func() {
+		fmt.Fprintf(os.Stderr, "Usage: %s <depfile.d>", os.Args[0])
+		flag.PrintDefaults()
+	}
+	output := flag.String("o", "", "Optional output file (defaults to rewriting source if necessary)")
+	flag.Parse()
+
+	if flag.NArg() != 1 {
+		log.Fatal("Expected a single file as an argument")
+	}
+
+	old, err := ioutil.ReadFile(flag.Arg(0))
+	if err != nil {
+		log.Fatalf("Error opening %q: %v", flag.Arg(0), err)
+	}
+
+	deps, err := Parse(flag.Arg(0), bytes.NewBuffer(append([]byte(nil), old...)))
+	if err != nil {
+		log.Fatalf("Failed to parse: %v", err)
+	}
+
+	new := deps.Print()
+
+	if *output == "" || *output == flag.Arg(0) {
+		if !bytes.Equal(old, new) {
+			err := ioutil.WriteFile(flag.Arg(0), new, 0666)
+			if err != nil {
+				log.Fatalf("Failed to write: %v", err)
+			}
+		}
+	} else {
+		err := ioutil.WriteFile(*output, new, 0666)
+		if err != nil {
+			log.Fatalf("Failed to write to %q: %v", *output, err)
+		}
+	}
+}
diff --git a/cmd/merge_zips/Android.bp b/cmd/merge_zips/Android.bp
index ace079d..ab658fd 100644
--- a/cmd/merge_zips/Android.bp
+++ b/cmd/merge_zips/Android.bp
@@ -16,10 +16,14 @@
     name: "merge_zips",
     deps: [
       "android-archive-zip",
+      "blueprint-pathtools",
       "soong-jar",
     ],
     srcs: [
         "merge_zips.go",
     ],
+    testSrcs: [
+        "merge_zips_test.go",
+    ],
 }
 
diff --git a/cmd/merge_zips/merge_zips.go b/cmd/merge_zips/merge_zips.go
index 1057655..95ff70b 100644
--- a/cmd/merge_zips/merge_zips.go
+++ b/cmd/merge_zips/merge_zips.go
@@ -24,7 +24,8 @@
 	"os"
 	"path/filepath"
 	"sort"
-	"strings"
+
+	"github.com/google/blueprint/pathtools"
 
 	"android/soong/jar"
 	"android/soong/third_party/zip"
@@ -69,8 +70,8 @@
 )
 
 func init() {
-	flag.Var(&stripDirs, "stripDir", "the prefix of file path to be excluded from the output zip")
-	flag.Var(&stripFiles, "stripFile", "filenames to be excluded from the output zip, accepts wildcards")
+	flag.Var(&stripDirs, "stripDir", "directories to be excluded from the output zip, accepts wildcards")
+	flag.Var(&stripFiles, "stripFile", "files to be excluded from the output zip, accepts wildcards")
 	flag.Var(&zipsToNotStrip, "zipToNotStrip", "the input zip file which is not applicable for stripping")
 }
 
@@ -114,7 +115,7 @@
 			log.Fatal(err)
 		}
 		defer reader.Close()
-		namedReader := namedZipReader{path: input, reader: reader}
+		namedReader := namedZipReader{path: input, reader: &reader.Reader}
 		readers = append(readers, namedReader)
 	}
 
@@ -132,7 +133,7 @@
 
 	// do merge
 	err = mergeZips(readers, writer, *manifest, *entrypoint, *pyMain, *sortEntries, *emulateJar, *emulatePar,
-		*stripDirEntries, *ignoreDuplicates)
+		*stripDirEntries, *ignoreDuplicates, []string(stripFiles), []string(stripDirs), map[string]bool(zipsToNotStrip))
 	if err != nil {
 		log.Fatal(err)
 	}
@@ -141,7 +142,7 @@
 // a namedZipReader reads a .zip file and can say which file it's reading
 type namedZipReader struct {
 	path   string
-	reader *zip.ReadCloser
+	reader *zip.Reader
 }
 
 // a zipEntryPath refers to a file contained in a zip
@@ -224,7 +225,8 @@
 }
 
 func mergeZips(readers []namedZipReader, writer *zip.Writer, manifest, entrypoint, pyMain string,
-	sortEntries, emulateJar, emulatePar, stripDirEntries, ignoreDuplicates bool) error {
+	sortEntries, emulateJar, emulatePar, stripDirEntries, ignoreDuplicates bool,
+	stripFiles, stripDirs []string, zipsToNotStrip map[string]bool) error {
 
 	sourceByDest := make(map[string]zipSource, 0)
 	orderedMappings := []fileMapping{}
@@ -338,8 +340,12 @@
 	for _, namedReader := range readers {
 		_, skipStripThisZip := zipsToNotStrip[namedReader.path]
 		for _, file := range namedReader.reader.File {
-			if !skipStripThisZip && shouldStripFile(emulateJar, file.Name) {
-				continue
+			if !skipStripThisZip {
+				if skip, err := shouldStripEntry(emulateJar, stripFiles, stripDirs, file.Name); err != nil {
+					return err
+				} else if skip {
+					continue
+				}
 			}
 
 			if stripDirEntries && file.FileInfo().IsDir() {
@@ -419,26 +425,41 @@
 	return ret
 }
 
-func shouldStripFile(emulateJar bool, name string) bool {
+func shouldStripEntry(emulateJar bool, stripFiles, stripDirs []string, name string) (bool, error) {
 	for _, dir := range stripDirs {
-		if strings.HasPrefix(name, dir+"/") {
-			if emulateJar {
-				if name != jar.MetaDir && name != jar.ManifestFile {
-					return true
+		dir = filepath.Clean(dir)
+		patterns := []string{
+			dir + "/",      // the directory itself
+			dir + "/**/*",  // files recursively in the directory
+			dir + "/**/*/", // directories recursively in the directory
+		}
+
+		for _, pattern := range patterns {
+			match, err := pathtools.Match(pattern, name)
+			if err != nil {
+				return false, fmt.Errorf("%s: %s", err.Error(), pattern)
+			} else if match {
+				if emulateJar {
+					// When merging jar files, don't strip META-INF/MANIFEST.MF even if stripping META-INF is
+					// requested.
+					// TODO(ccross): which files does this affect?
+					if name != jar.MetaDir && name != jar.ManifestFile {
+						return true, nil
+					}
 				}
-			} else {
-				return true
+				return true, nil
 			}
 		}
 	}
+
 	for _, pattern := range stripFiles {
-		if match, err := filepath.Match(pattern, filepath.Base(name)); err != nil {
-			panic(fmt.Errorf("%s: %s", err.Error(), pattern))
+		if match, err := pathtools.Match(pattern, name); err != nil {
+			return false, fmt.Errorf("%s: %s", err.Error(), pattern)
 		} else if match {
-			return true
+			return true, nil
 		}
 	}
-	return false
+	return false, nil
 }
 
 func jarSort(files []fileMapping) {
diff --git a/cmd/merge_zips/merge_zips_test.go b/cmd/merge_zips/merge_zips_test.go
new file mode 100644
index 0000000..f91111f
--- /dev/null
+++ b/cmd/merge_zips/merge_zips_test.go
@@ -0,0 +1,298 @@
+// Copyright 2018 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 main
+
+import (
+	"bytes"
+	"fmt"
+	"os"
+	"strconv"
+	"strings"
+	"testing"
+
+	"android/soong/jar"
+	"android/soong/third_party/zip"
+)
+
+type testZipEntry struct {
+	name string
+	mode os.FileMode
+	data []byte
+}
+
+var (
+	A     = testZipEntry{"A", 0755, []byte("foo")}
+	a     = testZipEntry{"a", 0755, []byte("foo")}
+	a2    = testZipEntry{"a", 0755, []byte("FOO2")}
+	a3    = testZipEntry{"a", 0755, []byte("Foo3")}
+	bDir  = testZipEntry{"b/", os.ModeDir | 0755, nil}
+	bbDir = testZipEntry{"b/b/", os.ModeDir | 0755, nil}
+	bbb   = testZipEntry{"b/b/b", 0755, nil}
+	ba    = testZipEntry{"b/a", 0755, []byte("foob")}
+	bc    = testZipEntry{"b/c", 0755, []byte("bar")}
+	bd    = testZipEntry{"b/d", 0700, []byte("baz")}
+	be    = testZipEntry{"b/e", 0700, []byte("")}
+
+	metainfDir     = testZipEntry{jar.MetaDir, os.ModeDir | 0755, nil}
+	manifestFile   = testZipEntry{jar.ManifestFile, 0755, []byte("manifest")}
+	manifestFile2  = testZipEntry{jar.ManifestFile, 0755, []byte("manifest2")}
+	moduleInfoFile = testZipEntry{jar.ModuleInfoClass, 0755, []byte("module-info")}
+)
+
+func TestMergeZips(t *testing.T) {
+	testCases := []struct {
+		name             string
+		in               [][]testZipEntry
+		stripFiles       []string
+		stripDirs        []string
+		jar              bool
+		sort             bool
+		ignoreDuplicates bool
+		stripDirEntries  bool
+		zipsToNotStrip   map[string]bool
+
+		out []testZipEntry
+		err string
+	}{
+		{
+			name: "duplicates error",
+			in: [][]testZipEntry{
+				{a},
+				{a2},
+				{a3},
+			},
+			out: []testZipEntry{a},
+			err: "duplicate",
+		},
+		{
+			name: "duplicates take first",
+			in: [][]testZipEntry{
+				{a},
+				{a2},
+				{a3},
+			},
+			out: []testZipEntry{a},
+
+			ignoreDuplicates: true,
+		},
+		{
+			name: "sort",
+			in: [][]testZipEntry{
+				{be, bc, bDir, bbDir, bbb, A, metainfDir, manifestFile},
+			},
+			out: []testZipEntry{A, metainfDir, manifestFile, bDir, bbDir, bbb, bc, be},
+
+			sort: true,
+		},
+		{
+			name: "jar sort",
+			in: [][]testZipEntry{
+				{be, bc, bDir, A, metainfDir, manifestFile},
+			},
+			out: []testZipEntry{metainfDir, manifestFile, A, bDir, bc, be},
+
+			jar: true,
+		},
+		{
+			name: "jar merge",
+			in: [][]testZipEntry{
+				{metainfDir, manifestFile, bDir, be},
+				{metainfDir, manifestFile2, bDir, bc},
+				{metainfDir, manifestFile2, A},
+			},
+			out: []testZipEntry{metainfDir, manifestFile, A, bDir, bc, be},
+
+			jar: true,
+		},
+		{
+			name: "merge",
+			in: [][]testZipEntry{
+				{bDir, be},
+				{bDir, bc},
+				{A},
+			},
+			out: []testZipEntry{bDir, be, bc, A},
+		},
+		{
+			name: "strip dir entries",
+			in: [][]testZipEntry{
+				{a, bDir, bbDir, bbb, bc, bd, be},
+			},
+			out: []testZipEntry{a, bbb, bc, bd, be},
+
+			stripDirEntries: true,
+		},
+		{
+			name: "strip files",
+			in: [][]testZipEntry{
+				{a, bDir, bbDir, bbb, bc, bd, be},
+			},
+			out: []testZipEntry{a, bDir, bbDir, bbb, bc},
+
+			stripFiles: []string{"b/d", "b/e"},
+		},
+		{
+			// merge_zips used to treat -stripFile a as stripping any file named a, it now only strips a in the
+			// root of the zip.
+			name: "strip file name",
+			in: [][]testZipEntry{
+				{a, bDir, ba},
+			},
+			out: []testZipEntry{bDir, ba},
+
+			stripFiles: []string{"a"},
+		},
+		{
+			name: "strip files glob",
+			in: [][]testZipEntry{
+				{a, bDir, ba},
+			},
+			out: []testZipEntry{bDir},
+
+			stripFiles: []string{"**/a"},
+		},
+		{
+			name: "strip dirs",
+			in: [][]testZipEntry{
+				{a, bDir, bbDir, bbb, bc, bd, be},
+			},
+			out: []testZipEntry{a},
+
+			stripDirs: []string{"b"},
+		},
+		{
+			name: "strip dirs glob",
+			in: [][]testZipEntry{
+				{a, bDir, bbDir, bbb, bc, bd, be},
+			},
+			out: []testZipEntry{a, bDir, bc, bd, be},
+
+			stripDirs: []string{"b/*"},
+		},
+		{
+			name: "zips to not strip",
+			in: [][]testZipEntry{
+				{a, bDir, bc},
+				{bDir, bd},
+				{bDir, be},
+			},
+			out: []testZipEntry{a, bDir, bd},
+
+			stripDirs: []string{"b"},
+			zipsToNotStrip: map[string]bool{
+				"in1": true,
+			},
+		},
+	}
+
+	for _, test := range testCases {
+		t.Run(test.name, func(t *testing.T) {
+			var readers []namedZipReader
+			for i, in := range test.in {
+				r := testZipEntriesToZipReader(in)
+				readers = append(readers, namedZipReader{
+					path:   "in" + strconv.Itoa(i),
+					reader: r,
+				})
+			}
+
+			want := testZipEntriesToBuf(test.out)
+
+			out := &bytes.Buffer{}
+			writer := zip.NewWriter(out)
+
+			err := mergeZips(readers, writer, "", "", "",
+				test.sort, test.jar, false, test.stripDirEntries, test.ignoreDuplicates,
+				test.stripFiles, test.stripDirs, test.zipsToNotStrip)
+
+			closeErr := writer.Close()
+			if closeErr != nil {
+				t.Fatal(err)
+			}
+
+			if test.err != "" {
+				if err == nil {
+					t.Fatal("missing err, expected: ", test.err)
+				} else if !strings.Contains(strings.ToLower(err.Error()), strings.ToLower(test.err)) {
+					t.Fatal("incorrect err, want:", test.err, "got:", err)
+				}
+				return
+			}
+
+			if !bytes.Equal(want, out.Bytes()) {
+				t.Error("incorrect zip output")
+				t.Errorf("want:\n%s", dumpZip(want))
+				t.Errorf("got:\n%s", dumpZip(out.Bytes()))
+			}
+		})
+	}
+}
+
+func testZipEntriesToBuf(entries []testZipEntry) []byte {
+	b := &bytes.Buffer{}
+	zw := zip.NewWriter(b)
+
+	for _, e := range entries {
+		fh := zip.FileHeader{
+			Name: e.name,
+		}
+		fh.SetMode(e.mode)
+
+		w, err := zw.CreateHeader(&fh)
+		if err != nil {
+			panic(err)
+		}
+
+		_, err = w.Write(e.data)
+		if err != nil {
+			panic(err)
+		}
+	}
+
+	err := zw.Close()
+	if err != nil {
+		panic(err)
+	}
+
+	return b.Bytes()
+}
+
+func testZipEntriesToZipReader(entries []testZipEntry) *zip.Reader {
+	b := testZipEntriesToBuf(entries)
+	r := bytes.NewReader(b)
+
+	zr, err := zip.NewReader(r, int64(len(b)))
+	if err != nil {
+		panic(err)
+	}
+
+	return zr
+}
+
+func dumpZip(buf []byte) string {
+	r := bytes.NewReader(buf)
+	zr, err := zip.NewReader(r, int64(len(buf)))
+	if err != nil {
+		panic(err)
+	}
+
+	var ret string
+
+	for _, f := range zr.File {
+		ret += fmt.Sprintf("%v: %v %v %08x\n", f.Name, f.Mode(), f.UncompressedSize64, f.CRC32)
+	}
+
+	return ret
+}
diff --git a/cmd/multiproduct_kati/Android.bp b/cmd/multiproduct_kati/Android.bp
index 04a5802..13b3679 100644
--- a/cmd/multiproduct_kati/Android.bp
+++ b/cmd/multiproduct_kati/Android.bp
@@ -17,6 +17,7 @@
     deps: [
         "soong-ui-build",
         "soong-ui-logger",
+        "soong-ui-terminal",
         "soong-ui-tracer",
         "soong-zip",
     ],
diff --git a/cmd/multiproduct_kati/main.go b/cmd/multiproduct_kati/main.go
index 06c5626..237d384 100644
--- a/cmd/multiproduct_kati/main.go
+++ b/cmd/multiproduct_kati/main.go
@@ -29,6 +29,8 @@
 
 	"android/soong/ui/build"
 	"android/soong/ui/logger"
+	"android/soong/ui/status"
+	"android/soong/ui/terminal"
 	"android/soong/ui/tracer"
 	"android/soong/zip"
 )
@@ -66,98 +68,34 @@
 	ctx     build.Context
 	config  build.Config
 	logFile string
+	action  *status.Action
 }
 
-type Status struct {
-	cur    int
-	total  int
-	failed int
-
-	ctx           build.Context
-	haveBlankLine bool
-	smartTerminal bool
-
-	lock sync.Mutex
-}
-
-func NewStatus(ctx build.Context) *Status {
-	return &Status{
-		ctx:           ctx,
-		haveBlankLine: true,
-		smartTerminal: ctx.IsTerminal(),
-	}
-}
-
-func (s *Status) SetTotal(total int) {
-	s.total = total
-}
-
-func (s *Status) Fail(product string, err error, logFile string) {
-	s.Finish(product)
-
-	s.lock.Lock()
-	defer s.lock.Unlock()
-
-	if s.smartTerminal && !s.haveBlankLine {
-		fmt.Fprintln(s.ctx.Stdout())
-		s.haveBlankLine = true
+func errMsgFromLog(filename string) string {
+	if filename == "" {
+		return ""
 	}
 
-	s.failed++
-	fmt.Fprintln(s.ctx.Stderr(), "FAILED:", product)
-	s.ctx.Verboseln("FAILED:", product)
-
-	if logFile != "" {
-		data, err := ioutil.ReadFile(logFile)
-		if err == nil {
-			lines := strings.Split(strings.TrimSpace(string(data)), "\n")
-			if len(lines) > errorLeadingLines+errorTrailingLines+1 {
-				lines[errorLeadingLines] = fmt.Sprintf("... skipping %d lines ...",
-					len(lines)-errorLeadingLines-errorTrailingLines)
-
-				lines = append(lines[:errorLeadingLines+1],
-					lines[len(lines)-errorTrailingLines:]...)
-			}
-			for _, line := range lines {
-				fmt.Fprintln(s.ctx.Stderr(), "> ", line)
-				s.ctx.Verboseln(line)
-			}
-		}
+	data, err := ioutil.ReadFile(filename)
+	if err != nil {
+		return ""
 	}
 
-	s.ctx.Print(err)
-}
+	lines := strings.Split(strings.TrimSpace(string(data)), "\n")
+	if len(lines) > errorLeadingLines+errorTrailingLines+1 {
+		lines[errorLeadingLines] = fmt.Sprintf("... skipping %d lines ...",
+			len(lines)-errorLeadingLines-errorTrailingLines)
 
-func (s *Status) Finish(product string) {
-	s.lock.Lock()
-	defer s.lock.Unlock()
-
-	s.cur++
-	line := fmt.Sprintf("[%d/%d] %s", s.cur, s.total, product)
-
-	if s.smartTerminal {
-		if max, ok := s.ctx.TermWidth(); ok {
-			if len(line) > max {
-				line = line[:max]
-			}
-		}
-
-		fmt.Fprint(s.ctx.Stdout(), "\r", line, "\x1b[K")
-		s.haveBlankLine = false
-	} else {
-		s.ctx.Println(line)
+		lines = append(lines[:errorLeadingLines+1],
+			lines[len(lines)-errorTrailingLines:]...)
 	}
-}
-
-func (s *Status) Finished() int {
-	s.lock.Lock()
-	defer s.lock.Unlock()
-
-	if !s.haveBlankLine {
-		fmt.Fprintln(s.ctx.Stdout())
-		s.haveBlankLine = true
+	var buf strings.Builder
+	for _, line := range lines {
+		buf.WriteString("> ")
+		buf.WriteString(line)
+		buf.WriteString("\n")
 	}
-	return s.failed
+	return buf.String()
 }
 
 // TODO(b/70370883): This tool uses a lot of open files -- over the default
@@ -194,6 +132,9 @@
 }
 
 func main() {
+	writer := terminal.NewWriter(terminal.StdioImpl{})
+	defer writer.Finish()
+
 	log := logger.New(os.Stderr)
 	defer log.Cleanup()
 
@@ -205,20 +146,24 @@
 	trace := tracer.New(log)
 	defer trace.Close()
 
+	stat := &status.Status{}
+	defer stat.Finish()
+	stat.AddOutput(terminal.NewStatusOutput(writer, ""))
+
 	build.SetupSignals(log, cancel, func() {
 		trace.Close()
 		log.Cleanup()
+		stat.Finish()
 	})
 
 	buildCtx := build.Context{&build.ContextImpl{
-		Context:        ctx,
-		Logger:         log,
-		Tracer:         trace,
-		StdioInterface: build.StdioImpl{},
+		Context: ctx,
+		Logger:  log,
+		Tracer:  trace,
+		Writer:  writer,
+		Status:  stat,
 	}}
 
-	status := NewStatus(buildCtx)
-
 	config := build.NewConfig(buildCtx)
 	if *outDir == "" {
 		name := "multiproduct-" + time.Now().Format("20060102150405")
@@ -255,6 +200,11 @@
 
 	setMaxFiles(log)
 
+	finder := build.NewSourceFinder(buildCtx, config)
+	defer finder.Shutdown()
+
+	build.FindSources(buildCtx, config, finder)
+
 	vars, err := build.DumpMakeVars(buildCtx, config, nil, []string{"all_named_products"})
 	if err != nil {
 		log.Fatal(err)
@@ -298,14 +248,12 @@
 
 	log.Verbose("Got product list: ", products)
 
-	status.SetTotal(len(products))
+	s := buildCtx.Status.StartTool()
+	s.SetTotalActions(len(products))
 
 	var wg sync.WaitGroup
 	productConfigs := make(chan Product, len(products))
 
-	finder := build.NewSourceFinder(buildCtx, config)
-	defer finder.Shutdown()
-
 	// Run the product config for every product in parallel
 	for _, product := range products {
 		wg.Add(1)
@@ -313,8 +261,18 @@
 			var stdLog string
 
 			defer wg.Done()
+
+			action := &status.Action{
+				Description: product,
+				Outputs:     []string{product},
+			}
+			s.StartAction(action)
 			defer logger.Recover(func(err error) {
-				status.Fail(product, err, stdLog)
+				s.FinishAction(status.ActionResult{
+					Action: action,
+					Error:  err,
+					Output: errMsgFromLog(stdLog),
+				})
 			})
 
 			productOutDir := filepath.Join(config.OutDir(), product)
@@ -337,12 +295,14 @@
 			productLog.SetOutput(filepath.Join(productLogDir, "soong.log"))
 
 			productCtx := build.Context{&build.ContextImpl{
-				Context:        ctx,
-				Logger:         productLog,
-				Tracer:         trace,
-				StdioInterface: build.NewCustomStdio(nil, f, f),
-				Thread:         trace.NewThread(product),
+				Context: ctx,
+				Logger:  productLog,
+				Tracer:  trace,
+				Writer:  terminal.NewWriter(terminal.NewCustomStdio(nil, f, f)),
+				Thread:  trace.NewThread(product),
+				Status:  &status.Status{},
 			}}
+			productCtx.Status.AddOutput(terminal.NewStatusOutput(productCtx.Writer, ""))
 
 			productConfig := build.NewConfig(productCtx)
 			productConfig.Environment().Set("OUT_DIR", productOutDir)
@@ -350,7 +310,7 @@
 			productConfig.Lunch(productCtx, product, *buildVariant)
 
 			build.Build(productCtx, productConfig, build.BuildProductConfig)
-			productConfigs <- Product{productCtx, productConfig, stdLog}
+			productConfigs <- Product{productCtx, productConfig, stdLog, action}
 		}(product)
 	}
 	go func() {
@@ -367,7 +327,11 @@
 			for product := range productConfigs {
 				func() {
 					defer logger.Recover(func(err error) {
-						status.Fail(product.config.TargetProduct(), err, product.logFile)
+						s.FinishAction(status.ActionResult{
+							Action: product.action,
+							Error:  err,
+							Output: errMsgFromLog(product.logFile),
+						})
 					})
 
 					defer func() {
@@ -398,7 +362,9 @@
 						}
 					}
 					build.Build(product.ctx, product.config, buildWhat)
-					status.Finish(product.config.TargetProduct())
+					s.FinishAction(status.ActionResult{
+						Action: product.action,
+					})
 				}()
 			}
 		}()
@@ -419,7 +385,5 @@
 		}
 	}
 
-	if count := status.Finished(); count > 0 {
-		log.Fatalln(count, "products failed")
-	}
+	s.Finish()
 }
diff --git a/ui/build/util_linux.go b/cmd/path_interposer/Android.bp
similarity index 73%
copy from ui/build/util_linux.go
copy to cmd/path_interposer/Android.bp
index 0a4e1d2..41a219f 100644
--- a/ui/build/util_linux.go
+++ b/cmd/path_interposer/Android.bp
@@ -1,4 +1,4 @@
-// Copyright 2017 Google Inc. All rights reserved.
+// Copyright 2018 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.
@@ -12,10 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package build
-
-import (
-	"syscall"
-)
-
-const ioctlGetTermios = syscall.TCGETS
+blueprint_go_binary {
+    name: "path_interposer",
+    deps: ["soong-ui-build-paths"],
+    srcs: ["main.go"],
+    testSrcs: ["main_test.go"],
+}
diff --git a/cmd/path_interposer/main.go b/cmd/path_interposer/main.go
new file mode 100644
index 0000000..cd28b96
--- /dev/null
+++ b/cmd/path_interposer/main.go
@@ -0,0 +1,247 @@
+// Copyright 2018 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 main
+
+import (
+	"bytes"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"strconv"
+	"syscall"
+
+	"android/soong/ui/build/paths"
+)
+
+func main() {
+	interposer, err := os.Executable()
+	if err != nil {
+		fmt.Fprintln(os.Stderr, "Unable to locate interposer executable:", err)
+		os.Exit(1)
+	}
+
+	if fi, err := os.Lstat(interposer); err == nil {
+		if fi.Mode()&os.ModeSymlink != 0 {
+			link, err := os.Readlink(interposer)
+			if err != nil {
+				fmt.Fprintln(os.Stderr, "Unable to read link to interposer executable:", err)
+				os.Exit(1)
+			}
+			if filepath.IsAbs(link) {
+				interposer = link
+			} else {
+				interposer = filepath.Join(filepath.Dir(interposer), link)
+			}
+		}
+	} else {
+		fmt.Fprintln(os.Stderr, "Unable to stat interposer executable:", err)
+		os.Exit(1)
+	}
+
+	disableError := false
+	if e, ok := os.LookupEnv("TEMPORARY_DISABLE_PATH_RESTRICTIONS"); ok {
+		disableError = e == "1" || e == "y" || e == "yes" || e == "on" || e == "true"
+	}
+
+	exitCode, err := Main(os.Stdout, os.Stderr, interposer, os.Args, mainOpts{
+		disableError: disableError,
+
+		sendLog:       paths.SendLog,
+		config:        paths.GetConfig,
+		lookupParents: lookupParents,
+	})
+	if err != nil {
+		fmt.Fprintln(os.Stderr, err.Error())
+	}
+	os.Exit(exitCode)
+}
+
+var usage = fmt.Errorf(`To use the PATH interposer:
+ * Write the original PATH variable to <interposer>_origpath
+ * Set up a directory of symlinks to the PATH interposer, and use that in PATH
+
+If a tool isn't in the allowed list, a log will be posted to the unix domain
+socket at <interposer>_log.`)
+
+type mainOpts struct {
+	disableError bool
+
+	sendLog       func(logSocket string, entry *paths.LogEntry, done chan interface{})
+	config        func(name string) paths.PathConfig
+	lookupParents func() []paths.LogProcess
+}
+
+func Main(stdout, stderr io.Writer, interposer string, args []string, opts mainOpts) (int, error) {
+	base := filepath.Base(args[0])
+
+	origPathFile := interposer + "_origpath"
+	if base == filepath.Base(interposer) {
+		return 1, usage
+	}
+
+	origPath, err := ioutil.ReadFile(origPathFile)
+	if err != nil {
+		if os.IsNotExist(err) {
+			return 1, usage
+		} else {
+			return 1, fmt.Errorf("Failed to read original PATH: %v", err)
+		}
+	}
+
+	cmd := &exec.Cmd{
+		Args: args,
+		Env:  os.Environ(),
+
+		Stdin:  os.Stdin,
+		Stdout: stdout,
+		Stderr: stderr,
+	}
+
+	if err := os.Setenv("PATH", string(origPath)); err != nil {
+		return 1, fmt.Errorf("Failed to set PATH env: %v", err)
+	}
+
+	if config := opts.config(base); config.Log || config.Error {
+		var procs []paths.LogProcess
+		if opts.lookupParents != nil {
+			procs = opts.lookupParents()
+		}
+
+		if opts.sendLog != nil {
+			waitForLog := make(chan interface{})
+			opts.sendLog(interposer+"_log", &paths.LogEntry{
+				Basename: base,
+				Args:     args,
+				Parents:  procs,
+			}, waitForLog)
+			defer func() { <-waitForLog }()
+		}
+		if config.Error && !opts.disableError {
+			return 1, fmt.Errorf("%q is not allowed to be used. See https://android.googlesource.com/platform/build/+/master/Changes.md#PATH_Tools for more information.", base)
+		}
+	}
+
+	cmd.Path, err = exec.LookPath(base)
+	if err != nil {
+		return 1, err
+	}
+
+	if err = cmd.Run(); err != nil {
+		if exitErr, ok := err.(*exec.ExitError); ok {
+			if status, ok := exitErr.Sys().(syscall.WaitStatus); ok {
+				if status.Exited() {
+					return status.ExitStatus(), nil
+				} else if status.Signaled() {
+					exitCode := 128 + int(status.Signal())
+					return exitCode, nil
+				} else {
+					return 1, exitErr
+				}
+			} else {
+				return 1, nil
+			}
+		}
+	}
+
+	return 0, nil
+}
+
+type procEntry struct {
+	Pid     int
+	Ppid    int
+	Command string
+}
+
+func readProcs() map[int]procEntry {
+	cmd := exec.Command("ps", "-o", "pid,ppid,command")
+	data, err := cmd.Output()
+	if err != nil {
+		return nil
+	}
+
+	return parseProcs(data)
+}
+
+func parseProcs(data []byte) map[int]procEntry {
+	lines := bytes.Split(data, []byte("\n"))
+	if len(lines) < 2 {
+		return nil
+	}
+	// Remove the header
+	lines = lines[1:]
+
+	ret := make(map[int]procEntry, len(lines))
+	for _, line := range lines {
+		fields := bytes.SplitN(line, []byte(" "), 2)
+		if len(fields) != 2 {
+			continue
+		}
+
+		pid, err := strconv.Atoi(string(fields[0]))
+		if err != nil {
+			continue
+		}
+
+		line = bytes.TrimLeft(fields[1], " ")
+
+		fields = bytes.SplitN(line, []byte(" "), 2)
+		if len(fields) != 2 {
+			continue
+		}
+
+		ppid, err := strconv.Atoi(string(fields[0]))
+		if err != nil {
+			continue
+		}
+
+		ret[pid] = procEntry{
+			Pid:     pid,
+			Ppid:    ppid,
+			Command: string(bytes.TrimLeft(fields[1], " ")),
+		}
+	}
+
+	return ret
+}
+
+func lookupParents() []paths.LogProcess {
+	procs := readProcs()
+	if procs == nil {
+		return nil
+	}
+
+	list := []paths.LogProcess{}
+	pid := os.Getpid()
+	for {
+		entry, ok := procs[pid]
+		if !ok {
+			break
+		}
+
+		list = append([]paths.LogProcess{
+			{
+				Pid:     pid,
+				Command: entry.Command,
+			},
+		}, list...)
+
+		pid = entry.Ppid
+	}
+
+	return list
+}
diff --git a/cmd/path_interposer/main_test.go b/cmd/path_interposer/main_test.go
new file mode 100644
index 0000000..c89d623
--- /dev/null
+++ b/cmd/path_interposer/main_test.go
@@ -0,0 +1,196 @@
+// Copyright 2018 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 main
+
+import (
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"testing"
+
+	"android/soong/ui/build/paths"
+)
+
+var tmpDir string
+var origPATH string
+
+func TestMain(m *testing.M) {
+	os.Exit(func() int {
+		var err error
+		tmpDir, err = ioutil.TempDir("", "interposer_test")
+		if err != nil {
+			panic(err)
+		}
+		defer os.RemoveAll(tmpDir)
+
+		origPATH = os.Getenv("PATH")
+		err = os.Setenv("PATH", "")
+		if err != nil {
+			panic(err)
+		}
+
+		return m.Run()
+	}())
+}
+
+func setup(t *testing.T) string {
+	f, err := ioutil.TempFile(tmpDir, "interposer")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer f.Close()
+
+	err = ioutil.WriteFile(f.Name()+"_origpath", []byte(origPATH), 0666)
+	if err != nil {
+		t.Fatal(err)
+	}
+	return f.Name()
+}
+
+func TestInterposer(t *testing.T) {
+	interposer := setup(t)
+
+	logConfig := func(name string) paths.PathConfig {
+		if name == "true" {
+			return paths.PathConfig{
+				Log:   false,
+				Error: false,
+			}
+		} else if name == "path_interposer_test_not_allowed" {
+			return paths.PathConfig{
+				Log:   false,
+				Error: true,
+			}
+		}
+		return paths.PathConfig{
+			Log:   true,
+			Error: false,
+		}
+	}
+
+	testCases := []struct {
+		name string
+		args []string
+
+		exitCode int
+		err      error
+		logEntry string
+	}{
+		{
+			name: "direct call",
+			args: []string{interposer},
+
+			exitCode: 1,
+			err:      usage,
+		},
+		{
+			name: "relative call",
+			args: []string{filepath.Base(interposer)},
+
+			exitCode: 1,
+			err:      usage,
+		},
+		{
+			name: "true",
+			args: []string{"/my/path/true"},
+		},
+		{
+			name: "relative true",
+			args: []string{"true"},
+		},
+		{
+			name: "exit code",
+			args: []string{"bash", "-c", "exit 42"},
+
+			exitCode: 42,
+			logEntry: "bash",
+		},
+		{
+			name: "signal",
+			args: []string{"bash", "-c", "kill -9 $$"},
+
+			exitCode: 137,
+			logEntry: "bash",
+		},
+		{
+			name: "does not exist",
+			args: []string{"path_interposer_test_does_not_exist"},
+
+			exitCode: 1,
+			err:      fmt.Errorf(`exec: "path_interposer_test_does_not_exist": executable file not found in $PATH`),
+			logEntry: "path_interposer_test_does_not_exist",
+		},
+		{
+			name: "not allowed",
+			args: []string{"path_interposer_test_not_allowed"},
+
+			exitCode: 1,
+			err:      fmt.Errorf(`"path_interposer_test_not_allowed" is not allowed to be used. See https://android.googlesource.com/platform/build/+/master/Changes.md#PATH_Tools for more information.`),
+			logEntry: "path_interposer_test_not_allowed",
+		},
+	}
+
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			logged := false
+			logFunc := func(logSocket string, entry *paths.LogEntry, done chan interface{}) {
+				defer close(done)
+
+				logged = true
+				if entry.Basename != testCase.logEntry {
+					t.Errorf("unexpected log entry:\nwant: %q\n got: %q", testCase.logEntry, entry.Basename)
+				}
+			}
+
+			exitCode, err := Main(ioutil.Discard, ioutil.Discard, interposer, testCase.args, mainOpts{
+				sendLog: logFunc,
+				config:  logConfig,
+			})
+
+			errstr := func(err error) string {
+				if err == nil {
+					return ""
+				}
+				return err.Error()
+			}
+			if errstr(testCase.err) != errstr(err) {
+				t.Errorf("unexpected error:\nwant: %v\n got: %v", testCase.err, err)
+			}
+			if testCase.exitCode != exitCode {
+				t.Errorf("expected exit code %d, got %d", testCase.exitCode, exitCode)
+			}
+			if !logged && testCase.logEntry != "" {
+				t.Errorf("no log entry, but expected %q", testCase.logEntry)
+			}
+		})
+	}
+}
+
+func TestMissingPath(t *testing.T) {
+	interposer := setup(t)
+	err := os.Remove(interposer + "_origpath")
+	if err != nil {
+		t.Fatal("Failed to remove:", err)
+	}
+
+	exitCode, err := Main(ioutil.Discard, ioutil.Discard, interposer, []string{"true"}, mainOpts{})
+	if err != usage {
+		t.Errorf("Unexpected error:\n got: %v\nwant: %v", err, usage)
+	}
+	if exitCode != 1 {
+		t.Errorf("expected exit code %d, got %d", 1, exitCode)
+	}
+}
diff --git a/cmd/pom2bp/pom2bp.go b/cmd/pom2bp/pom2bp.go
index 078a07d..9ce6b50 100644
--- a/cmd/pom2bp/pom2bp.go
+++ b/cmd/pom2bp/pom2bp.go
@@ -15,6 +15,7 @@
 package main
 
 import (
+	"archive/zip"
 	"bufio"
 	"bytes"
 	"encoding/xml"
@@ -138,9 +139,10 @@
 type Pom struct {
 	XMLName xml.Name `xml:"http://maven.apache.org/POM/4.0.0 project"`
 
-	PomFile      string `xml:"-"`
-	ArtifactFile string `xml:"-"`
-	BpTarget     string `xml:"-"`
+	PomFile       string `xml:"-"`
+	ArtifactFile  string `xml:"-"`
+	BpTarget      string `xml:"-"`
+	MinSdkVersion string `xml:"-"`
 
 	GroupId    string `xml:"groupId"`
 	ArtifactId string `xml:"artifactId"`
@@ -215,11 +217,61 @@
 	}
 }
 
+// ExtractMinSdkVersion extracts the minSdkVersion from the AndroidManifest.xml file inside an aar file, or sets it
+// to "current" if it is not present.
+func (p *Pom) ExtractMinSdkVersion() error {
+	aar, err := zip.OpenReader(p.ArtifactFile)
+	if err != nil {
+		return err
+	}
+	defer aar.Close()
+
+	var manifest *zip.File
+	for _, f := range aar.File {
+		if f.Name == "AndroidManifest.xml" {
+			manifest = f
+			break
+		}
+	}
+
+	if manifest == nil {
+		return fmt.Errorf("failed to find AndroidManifest.xml in %s", p.ArtifactFile)
+	}
+
+	r, err := manifest.Open()
+	if err != nil {
+		return err
+	}
+	defer r.Close()
+
+	decoder := xml.NewDecoder(r)
+
+	manifestData := struct {
+		XMLName  xml.Name `xml:"manifest"`
+		Uses_sdk struct {
+			MinSdkVersion string `xml:"http://schemas.android.com/apk/res/android minSdkVersion,attr"`
+		} `xml:"uses-sdk"`
+	}{}
+
+	err = decoder.Decode(&manifestData)
+	if err != nil {
+		return err
+	}
+
+	p.MinSdkVersion = manifestData.Uses_sdk.MinSdkVersion
+	if p.MinSdkVersion == "" {
+		p.MinSdkVersion = "current"
+	}
+
+	return nil
+}
+
 var bpTemplate = template.Must(template.New("bp").Parse(`
 {{if .IsAar}}android_library_import{{else}}java_import{{end}} {
     name: "{{.BpName}}-nodeps",
     {{if .IsAar}}aars{{else}}jars{{end}}: ["{{.ArtifactFile}}"],
     sdk_version: "{{.SdkVersion}}",{{if .IsAar}}
+    min_sdk_version: "{{.MinSdkVersion}}",
     static_libs: [{{range .BpAarDeps}}
         "{{.}}",{{end}}{{range .BpExtraDeps}}
         "{{.}}",{{end}}
@@ -229,6 +281,7 @@
 {{if .IsAar}}android_library{{else}}java_library_static{{end}} {
     name: "{{.BpName}}",
     sdk_version: "{{.SdkVersion}}",{{if .IsAar}}
+    min_sdk_version: "{{.MinSdkVersion}}",
     manifest: "manifests/{{.BpName}}/AndroidManifest.xml",{{end}}
     static_libs: [
         "{{.BpName}}-nodeps",{{range .BpJarDeps}}
@@ -302,7 +355,7 @@
 
 	// Append all current command line args except -regen <file> to the ones from the file
 	for i := 1; i < len(os.Args); i++ {
-		if os.Args[i] == "-regen" {
+		if os.Args[i] == "-regen" || os.Args[i] == "--regen" {
 			i++
 		} else {
 			args = append(args, os.Args[i])
@@ -468,6 +521,13 @@
 	}
 
 	for _, pom := range poms {
+		if pom.IsAar() {
+			err := pom.ExtractMinSdkVersion()
+			if err != nil {
+				fmt.Fprintf(os.Stderr, "Error reading manifest for %s: %s", pom.ArtifactFile, err)
+				os.Exit(1)
+			}
+		}
 		pom.FixDeps(modules)
 	}
 
diff --git a/cmd/soong_ui/Android.bp b/cmd/soong_ui/Android.bp
index f09e42e..4e57bef 100644
--- a/cmd/soong_ui/Android.bp
+++ b/cmd/soong_ui/Android.bp
@@ -17,6 +17,7 @@
     deps: [
         "soong-ui-build",
         "soong-ui-logger",
+        "soong-ui-terminal",
         "soong-ui-tracer",
     ],
     srcs: [
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index 2ca7ebf..47682ff 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -26,6 +26,8 @@
 
 	"android/soong/ui/build"
 	"android/soong/ui/logger"
+	"android/soong/ui/status"
+	"android/soong/ui/terminal"
 	"android/soong/ui/tracer"
 )
 
@@ -44,7 +46,18 @@
 }
 
 func main() {
-	log := logger.New(os.Stderr)
+	var stdio terminal.StdioInterface
+	stdio = terminal.StdioImpl{}
+
+	// dumpvar uses stdout, everything else should be in stderr
+	if os.Args[1] == "--dumpvar-mode" || os.Args[1] == "--dumpvars-mode" {
+		stdio = terminal.NewCustomStdio(os.Stdin, os.Stderr, os.Stderr)
+	}
+
+	writer := terminal.NewWriter(stdio)
+	defer writer.Finish()
+
+	log := logger.New(writer)
 	defer log.Cleanup()
 
 	if len(os.Args) < 2 || !(inList("--make-mode", os.Args) ||
@@ -60,16 +73,23 @@
 	trace := tracer.New(log)
 	defer trace.Close()
 
+	stat := &status.Status{}
+	defer stat.Finish()
+	stat.AddOutput(terminal.NewStatusOutput(writer, os.Getenv("NINJA_STATUS")))
+	stat.AddOutput(trace.StatusTracer())
+
 	build.SetupSignals(log, cancel, func() {
 		trace.Close()
 		log.Cleanup()
+		stat.Finish()
 	})
 
 	buildCtx := build.Context{&build.ContextImpl{
-		Context:        ctx,
-		Logger:         log,
-		Tracer:         trace,
-		StdioInterface: build.StdioImpl{},
+		Context: ctx,
+		Logger:  log,
+		Tracer:  trace,
+		Writer:  writer,
+		Status:  stat,
 	}}
 	var config build.Config
 	if os.Args[1] == "--dumpvars-mode" || os.Args[1] == "--dumpvar-mode" {
@@ -78,19 +98,19 @@
 		config = build.NewConfig(buildCtx, os.Args[1:]...)
 	}
 
-	log.SetVerbose(config.IsVerbose())
 	build.SetupOutDir(buildCtx, config)
 
+	logsDir := config.OutDir()
 	if config.Dist() {
-		logsDir := filepath.Join(config.DistDir(), "logs")
-		os.MkdirAll(logsDir, 0777)
-		log.SetOutput(filepath.Join(logsDir, "soong.log"))
-		trace.SetOutput(filepath.Join(logsDir, "build.trace"))
-	} else {
-		log.SetOutput(filepath.Join(config.OutDir(), "soong.log"))
-		trace.SetOutput(filepath.Join(config.OutDir(), "build.trace"))
+		logsDir = filepath.Join(config.DistDir(), "logs")
 	}
 
+	os.MkdirAll(logsDir, 0777)
+	log.SetOutput(filepath.Join(logsDir, "soong.log"))
+	trace.SetOutput(filepath.Join(logsDir, "build.trace"))
+	stat.AddOutput(status.NewVerboseLog(log, filepath.Join(logsDir, "verbose.log")))
+	stat.AddOutput(status.NewErrorLog(log, filepath.Join(logsDir, "error.log")))
+
 	if start, ok := os.LookupEnv("TRACE_BEGIN_SOONG"); ok {
 		if !strings.HasSuffix(start, "N") {
 			if start_time, err := strconv.ParseUint(start, 10, 64); err == nil {
@@ -114,6 +134,17 @@
 	} else if os.Args[1] == "--dumpvars-mode" {
 		dumpVars(buildCtx, config, os.Args[2:])
 	} else {
+		if config.IsVerbose() {
+			writer.Print("! The argument `showcommands` is no longer supported.")
+			writer.Print("! Instead, the verbose log is always written to a compressed file in the output dir:")
+			writer.Print("!")
+			writer.Print(fmt.Sprintf("!   gzip -cd %s/verbose.log.gz | less -R", logsDir))
+			writer.Print("!")
+			writer.Print("! Older versions are saved in verbose.log.#.gz files")
+			writer.Print("")
+			time.Sleep(5 * time.Second)
+		}
+
 		toBuild := build.BuildAll
 		if config.Checkbuild() {
 			toBuild |= build.RunBuildTests
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 42be88f..03d4ea6 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -163,8 +163,6 @@
 	if len(g.properties.Tools) > 0 {
 		ctx.VisitDirectDepsBlueprint(func(module blueprint.Module) {
 			switch ctx.OtherModuleDependencyTag(module) {
-			case android.SourceDepTag:
-				// Nothing to do
 			case hostToolDepTag:
 				tool := ctx.OtherModuleName(module)
 				var path android.OptionalPath
@@ -201,8 +199,6 @@
 				} else {
 					ctx.ModuleErrorf("host tool %q missing output file", tool)
 				}
-			default:
-				ctx.ModuleErrorf("unknown dependency on %q", ctx.OtherModuleName(module))
 			}
 		})
 	}
@@ -227,13 +223,18 @@
 	task := g.taskGenerator(ctx, String(g.properties.Cmd), srcFiles)
 
 	rawCommand, err := android.Expand(task.cmd, func(name string) (string, error) {
+		// report the error directly without returning an error to android.Expand to catch multiple errors in a
+		// single run
+		reportError := func(fmt string, args ...interface{}) (string, error) {
+			ctx.PropertyErrorf("cmd", fmt, args...)
+			return "SOONG_ERROR", nil
+		}
+
 		switch name {
 		case "location":
 			if len(g.properties.Tools) == 0 && len(toolFiles) == 0 {
-				return "", fmt.Errorf("at least one `tools` or `tool_files` is required if $(location) is used")
-			}
-
-			if len(g.properties.Tools) > 0 {
+				return reportError("at least one `tools` or `tool_files` is required if $(location) is used")
+			} else if len(g.properties.Tools) > 0 {
 				return tools[g.properties.Tools[0]].String(), nil
 			} else {
 				return tools[toolFiles[0].Rel()].String(), nil
@@ -245,7 +246,7 @@
 		case "depfile":
 			referencedDepfile = true
 			if !Bool(g.properties.Depfile) {
-				return "", fmt.Errorf("$(depfile) used without depfile property")
+				return reportError("$(depfile) used without depfile property")
 			}
 			return "__SBOX_DEPFILE__", nil
 		case "genDir":
@@ -256,22 +257,22 @@
 				if tool, ok := tools[label]; ok {
 					return tool.String(), nil
 				} else {
-					return "", fmt.Errorf("unknown location label %q", label)
+					return reportError("unknown location label %q", label)
 				}
 			}
-			return "", fmt.Errorf("unknown variable '$(%s)'", name)
+			return reportError("unknown variable '$(%s)'", name)
 		}
 	})
 
-	if Bool(g.properties.Depfile) && !referencedDepfile {
-		ctx.PropertyErrorf("cmd", "specified depfile=true but did not include a reference to '${depfile}' in cmd")
-	}
-
 	if err != nil {
 		ctx.PropertyErrorf("cmd", "%s", err.Error())
 		return
 	}
 
+	if Bool(g.properties.Depfile) && !referencedDepfile {
+		ctx.PropertyErrorf("cmd", "specified depfile=true but did not include a reference to '${depfile}' in cmd")
+	}
+
 	// tell the sbox command which directory to use as its sandbox root
 	buildDir := android.PathForOutput(ctx).String()
 	sandboxPath := shared.TempDirForOutDir(buildDir)
diff --git a/java/aar.go b/java/aar.go
index 9e5cddb..0cfc585 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -27,6 +27,7 @@
 	ExportPackage() android.Path
 	ExportedProguardFlagFiles() android.Paths
 	ExportedStaticPackages() android.Paths
+	ExportedManifest() android.Path
 }
 
 func init() {
@@ -74,8 +75,8 @@
 	return a.exportPackage
 }
 
-func (a *aapt) aapt2Flags(ctx android.ModuleContext, sdkVersion string) (flags []string, deps android.Paths,
-	resDirs, overlayDirs []globbedResourceDir, overlayFiles, rroDirs android.Paths, manifestPath android.Path) {
+func (a *aapt) aapt2Flags(ctx android.ModuleContext, sdkContext sdkContext, manifestPath android.Path) (flags []string,
+	deps android.Paths, resDirs, overlayDirs []globbedResourceDir, rroDirs android.Paths) {
 
 	hasVersionCode := false
 	hasVersionName := false
@@ -116,29 +117,17 @@
 		assetFiles = append(assetFiles, androidResourceGlob(ctx, dir)...)
 	}
 
-	// App manifest file
-	manifestFile := proptools.StringDefault(a.aaptProperties.Manifest, "AndroidManifest.xml")
-	manifestPath = android.PathForModuleSrc(ctx, manifestFile)
 	linkFlags = append(linkFlags, "--manifest "+manifestPath.String())
 	linkDeps = append(linkDeps, manifestPath)
 
 	linkFlags = append(linkFlags, android.JoinWithPrefix(assetDirs.Strings(), "-A "))
 	linkDeps = append(linkDeps, assetFiles...)
 
-	transitiveStaticLibs, libDeps, libFlags := aaptLibs(ctx, sdkVersion)
-
-	overlayFiles = append(overlayFiles, transitiveStaticLibs...)
-	linkDeps = append(linkDeps, libDeps...)
-	linkFlags = append(linkFlags, libFlags...)
-
 	// SDK version flags
-	switch sdkVersion {
-	case "", "current", "system_current", "test_current":
-		sdkVersion = proptools.NinjaEscape([]string{ctx.Config().DefaultAppTargetSdk()})[0]
-	}
+	minSdkVersion := sdkVersionOrDefault(ctx, sdkContext.minSdkVersion())
 
-	linkFlags = append(linkFlags, "--min-sdk-version "+sdkVersion)
-	linkFlags = append(linkFlags, "--target-sdk-version "+sdkVersion)
+	linkFlags = append(linkFlags, "--min-sdk-version "+minSdkVersion)
+	linkFlags = append(linkFlags, "--target-sdk-version "+minSdkVersion)
 
 	// Version code
 	if !hasVersionCode {
@@ -150,8 +139,8 @@
 		if ctx.ModuleName() == "framework-res" {
 			// Some builds set AppsDefaultVersionName() to include the build number ("O-123456").  aapt2 copies the
 			// version name of framework-res into app manifests as compileSdkVersionCodename, which confuses things
-			// if it contains the build number.  Use the DefaultAppTargetSdk instead.
-			versionName = ctx.Config().DefaultAppTargetSdk()
+			// if it contains the build number.  Use the PlatformVersionName instead.
+			versionName = ctx.Config().PlatformVersionName()
 		} else {
 			versionName = ctx.Config().AppsDefaultVersionName()
 		}
@@ -159,21 +148,31 @@
 		linkFlags = append(linkFlags, "--version-name ", versionName)
 	}
 
-	return linkFlags, linkDeps, resDirs, overlayDirs, overlayFiles, rroDirs, manifestPath
+	return linkFlags, linkDeps, resDirs, overlayDirs, rroDirs
 }
 
-func (a *aapt) deps(ctx android.BottomUpMutatorContext, sdkVersion string) {
+func (a *aapt) deps(ctx android.BottomUpMutatorContext, sdkContext sdkContext) {
 	if !ctx.Config().UnbundledBuild() {
-		sdkDep := decodeSdkDep(ctx, sdkVersion)
+		sdkDep := decodeSdkDep(ctx, sdkContext)
 		if sdkDep.frameworkResModule != "" {
 			ctx.AddDependency(ctx.Module(), frameworkResTag, sdkDep.frameworkResModule)
 		}
 	}
 }
 
-func (a *aapt) buildActions(ctx android.ModuleContext, sdkVersion string, extraLinkFlags ...string) {
-	linkFlags, linkDeps, resDirs, overlayDirs, overlayFiles, rroDirs, manifestPath := a.aapt2Flags(ctx, sdkVersion)
+func (a *aapt) buildActions(ctx android.ModuleContext, sdkContext sdkContext, extraLinkFlags ...string) {
+	transitiveStaticLibs, staticLibManifests, libDeps, libFlags := aaptLibs(ctx, sdkContext)
 
+	// App manifest file
+	manifestFile := proptools.StringDefault(a.aaptProperties.Manifest, "AndroidManifest.xml")
+	manifestSrcPath := android.PathForModuleSrc(ctx, manifestFile)
+
+	manifestPath := manifestMerger(ctx, manifestSrcPath, sdkContext, staticLibManifests)
+
+	linkFlags, linkDeps, resDirs, overlayDirs, rroDirs := a.aapt2Flags(ctx, sdkContext, manifestPath)
+
+	linkFlags = append(linkFlags, libFlags...)
+	linkDeps = append(linkDeps, libDeps...)
 	linkFlags = append(linkFlags, extraLinkFlags...)
 
 	packageRes := android.PathForModuleOut(ctx, "package-res.apk")
@@ -191,7 +190,7 @@
 		compiledOverlay = append(compiledOverlay, aapt2Compile(ctx, dir.dir, dir.files).Paths()...)
 	}
 
-	compiledOverlay = append(compiledOverlay, overlayFiles...)
+	compiledOverlay = append(compiledOverlay, transitiveStaticLibs...)
 
 	aapt2Link(ctx, packageRes, srcJar, proguardOptionsFile, rTxt, extraPackages,
 		linkFlags, linkDeps, compiledRes, compiledOverlay)
@@ -206,14 +205,14 @@
 }
 
 // aaptLibs collects libraries from dependencies and sdk_version and converts them into paths
-func aaptLibs(ctx android.ModuleContext, sdkVersion string) (transitiveStaticLibs, deps android.Paths,
-	flags []string) {
+func aaptLibs(ctx android.ModuleContext, sdkContext sdkContext) (transitiveStaticLibs, staticLibManifests,
+	deps android.Paths, flags []string) {
 
 	var sharedLibs android.Paths
 
-	sdkDep := decodeSdkDep(ctx, sdkVersion)
+	sdkDep := decodeSdkDep(ctx, sdkContext)
 	if sdkDep.useFiles {
-		sharedLibs = append(sharedLibs, sdkDep.jar)
+		sharedLibs = append(sharedLibs, sdkDep.jars...)
 	}
 
 	ctx.VisitDirectDeps(func(module android.Module) {
@@ -232,6 +231,7 @@
 			if exportPackage != nil {
 				transitiveStaticLibs = append(transitiveStaticLibs, exportPackage)
 				transitiveStaticLibs = append(transitiveStaticLibs, aarDep.ExportedStaticPackages()...)
+				staticLibManifests = append(staticLibManifests, aarDep.ExportedManifest())
 			}
 		}
 	})
@@ -249,7 +249,7 @@
 
 	transitiveStaticLibs = android.FirstUniquePaths(transitiveStaticLibs)
 
-	return transitiveStaticLibs, deps, flags
+	return transitiveStaticLibs, staticLibManifests, deps, flags
 }
 
 type AndroidLibrary struct {
@@ -272,17 +272,21 @@
 	return a.exportedStaticPackages
 }
 
+func (a *AndroidLibrary) ExportedManifest() android.Path {
+	return a.manifestPath
+}
+
 var _ AndroidLibraryDependency = (*AndroidLibrary)(nil)
 
 func (a *AndroidLibrary) DepsMutator(ctx android.BottomUpMutatorContext) {
 	a.Module.deps(ctx)
 	if !Bool(a.properties.No_framework_libs) && !Bool(a.properties.No_standard_libs) {
-		a.aapt.deps(ctx, String(a.deviceProperties.Sdk_version))
+		a.aapt.deps(ctx, sdkContext(a))
 	}
 }
 
 func (a *AndroidLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	a.aapt.buildActions(ctx, String(a.deviceProperties.Sdk_version), "--static-lib")
+	a.aapt.buildActions(ctx, sdkContext(a), "--static-lib")
 
 	ctx.CheckbuildFile(a.proguardOptionsFile)
 	ctx.CheckbuildFile(a.exportPackage)
@@ -338,7 +342,8 @@
 type AARImportProperties struct {
 	Aars []string
 
-	Sdk_version *string
+	Sdk_version     *string
+	Min_sdk_version *string
 
 	Static_libs []string
 	Libs        []string
@@ -354,10 +359,22 @@
 	proguardFlags         android.WritablePath
 	exportPackage         android.WritablePath
 	extraAaptPackagesFile android.WritablePath
+	manifest              android.WritablePath
 
 	exportedStaticPackages android.Paths
 }
 
+func (a *AARImport) sdkVersion() string {
+	return String(a.properties.Sdk_version)
+}
+
+func (a *AARImport) minSdkVersion() string {
+	if a.properties.Min_sdk_version != nil {
+		return *a.properties.Min_sdk_version
+	}
+	return a.sdkVersion()
+}
+
 var _ AndroidLibraryDependency = (*AARImport)(nil)
 
 func (a *AARImport) ExportPackage() android.Path {
@@ -372,6 +389,10 @@
 	return a.exportedStaticPackages
 }
 
+func (a *AARImport) ExportedManifest() android.Path {
+	return a.manifest
+}
+
 func (a *AARImport) Prebuilt() *android.Prebuilt {
 	return &a.prebuilt
 }
@@ -382,7 +403,7 @@
 
 func (a *AARImport) DepsMutator(ctx android.BottomUpMutatorContext) {
 	if !ctx.Config().UnbundledBuild() {
-		sdkDep := decodeSdkDep(ctx, String(a.properties.Sdk_version))
+		sdkDep := decodeSdkDep(ctx, sdkContext(a))
 		if sdkDep.useModule && sdkDep.frameworkResModule != "" {
 			ctx.AddDependency(ctx.Module(), frameworkResTag, sdkDep.frameworkResModule)
 		}
@@ -413,12 +434,12 @@
 	extractedResDir := extractedAARDir.Join(ctx, "res")
 	a.classpathFile = extractedAARDir.Join(ctx, "classes.jar")
 	a.proguardFlags = extractedAARDir.Join(ctx, "proguard.txt")
-	manifest := extractedAARDir.Join(ctx, "AndroidManifest.xml")
+	a.manifest = extractedAARDir.Join(ctx, "AndroidManifest.xml")
 
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        unzipAAR,
 		Input:       aar,
-		Outputs:     android.WritablePaths{a.classpathFile, a.proguardFlags, manifest},
+		Outputs:     android.WritablePaths{a.classpathFile, a.proguardFlags, a.manifest},
 		Description: "unzip AAR",
 		Args: map[string]string{
 			"expectedDirs": extractedResDir.String(),
@@ -446,10 +467,12 @@
 		"--auto-add-overlay",
 	}
 
-	linkFlags = append(linkFlags, "--manifest "+manifest.String())
-	linkDeps = append(linkDeps, manifest)
+	linkFlags = append(linkFlags, "--manifest "+a.manifest.String())
+	linkDeps = append(linkDeps, a.manifest)
 
-	transitiveStaticLibs, libDeps, libFlags := aaptLibs(ctx, String(a.properties.Sdk_version))
+	transitiveStaticLibs, staticLibManifests, libDeps, libFlags := aaptLibs(ctx, sdkContext(a))
+
+	_ = staticLibManifests
 
 	linkDeps = append(linkDeps, libDeps...)
 	linkFlags = append(linkFlags, libFlags...)
@@ -474,6 +497,10 @@
 	return nil
 }
 
+func (a *AARImport) ExportedSdkLibs() []string {
+	return nil
+}
+
 var _ android.PrebuiltInterface = (*Import)(nil)
 
 func AARImportFactory() android.Module {
diff --git a/java/android_manifest.go b/java/android_manifest.go
new file mode 100644
index 0000000..8fcdcba
--- /dev/null
+++ b/java/android_manifest.go
@@ -0,0 +1,71 @@
+// Copyright 2018 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/java/config"
+
+	"github.com/google/blueprint"
+
+	"android/soong/android"
+)
+
+var manifestFixerRule = pctx.AndroidStaticRule("manifestFixer",
+	blueprint.RuleParams{
+		Command:     `${config.ManifestFixerCmd} --minSdkVersion ${minSdkVersion} $usesLibraries $in $out`,
+		CommandDeps: []string{"${config.ManifestFixerCmd}"},
+	},
+	"minSdkVersion", "usesLibraries")
+
+var manifestMergerRule = pctx.AndroidStaticRule("manifestMerger",
+	blueprint.RuleParams{
+		Command: `${config.JavaCmd} -classpath ${config.ManifestMergerClasspath} com.android.manifmerger.Merger ` +
+			`--main $in $libs --out $out`,
+		CommandDeps: config.ManifestMergerClasspath,
+	},
+	"libs")
+
+func manifestMerger(ctx android.ModuleContext, manifest android.Path, sdkContext sdkContext,
+	staticLibManifests android.Paths) android.Path {
+
+	// Inject minSdkVersion into the manifest
+	fixedManifest := android.PathForModuleOut(ctx, "manifest_fixer", "AndroidManifest.xml")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   manifestFixerRule,
+		Input:  manifest,
+		Output: fixedManifest,
+		Args: map[string]string{
+			"minSdkVersion": sdkVersionOrDefault(ctx, sdkContext.minSdkVersion()),
+		},
+	})
+	manifest = fixedManifest
+
+	// Merge static aar dependency manifests if necessary
+	if len(staticLibManifests) > 0 {
+		mergedManifest := android.PathForModuleOut(ctx, "manifest_merger", "AndroidManifest.xml")
+		ctx.Build(pctx, android.BuildParams{
+			Rule:      manifestMergerRule,
+			Input:     manifest,
+			Implicits: staticLibManifests,
+			Output:    mergedManifest,
+			Args: map[string]string{
+				"libs": android.JoinWithPrefix(staticLibManifests.Strings(), "--uses-library "),
+			},
+		})
+		manifest = mergedManifest
+	}
+
+	return manifest
+}
diff --git a/java/androidmk.go b/java/androidmk.go
index b168f2c..5740eca 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -37,7 +37,7 @@
 					fmt.Fprintln(w, "LOCAL_LOGTAGS_FILES :=", strings.Join(logtags, " "))
 				}
 
-				if library.properties.Installable != nil && *library.properties.Installable == false {
+				if library.installFile == nil {
 					fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := true")
 				}
 				if library.dexJarFile != nil {
@@ -56,13 +56,17 @@
 						fmt.Fprintln(w, "LOCAL_DEX_PREOPT_PROFILE_CLASS_LISTING := $(LOCAL_PATH)/"+*library.deviceProperties.Dex_preopt.Profile)
 					}
 				}
-				fmt.Fprintln(w, "LOCAL_SDK_VERSION :=", String(library.deviceProperties.Sdk_version))
+				fmt.Fprintln(w, "LOCAL_SDK_VERSION :=", library.sdkVersion())
 				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())
 				}
 
+				if len(library.exportedSdkLibs) != 0 {
+					fmt.Fprintln(w, "LOCAL_EXPORT_SDK_LIBRARIES :=", strings.Join(library.exportedSdkLibs, " "))
+				}
+
 				// Temporary hack: export sources used to compile framework.jar to Make
 				// to be used for droiddoc
 				// TODO(ccross): remove this once droiddoc is in soong
@@ -81,7 +85,7 @@
 				fmt.Fprintln(w, "LOCAL_IS_HOST_MODULE := true")
 				fmt.Fprintln(w, "LOCAL_MODULE_CLASS := JAVA_LIBRARIES")
 				fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", library.implementationJarFile.String())
-				if library.properties.Installable != nil && *library.properties.Installable == false {
+				if library.installFile == nil {
 					fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := true")
 				}
 				if library.dexJarFile != nil {
@@ -117,7 +121,7 @@
 			func(w io.Writer, outputFile android.Path) {
 				fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := ", !Bool(prebuilt.properties.Installable))
 				fmt.Fprintln(w, "LOCAL_SOONG_HEADER_JAR :=", prebuilt.combinedClasspathFile.String())
-				fmt.Fprintln(w, "LOCAL_SDK_VERSION :=", String(prebuilt.properties.Sdk_version))
+				fmt.Fprintln(w, "LOCAL_SDK_VERSION :=", prebuilt.sdkVersion())
 			},
 		},
 	}
@@ -136,7 +140,8 @@
 				fmt.Fprintln(w, "LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE :=", prebuilt.exportPackage.String())
 				fmt.Fprintln(w, "LOCAL_SOONG_EXPORT_PROGUARD_FLAGS :=", prebuilt.proguardFlags.String())
 				fmt.Fprintln(w, "LOCAL_SOONG_STATIC_LIBRARY_EXTRA_PACKAGES :=", prebuilt.extraAaptPackagesFile.String())
-				fmt.Fprintln(w, "LOCAL_SDK_VERSION :=", String(prebuilt.properties.Sdk_version))
+				fmt.Fprintln(w, "LOCAL_FULL_MANIFEST_FILE :=", prebuilt.manifest.String())
+				fmt.Fprintln(w, "LOCAL_SDK_VERSION :=", prebuilt.sdkVersion())
 			},
 		},
 	}
@@ -228,6 +233,19 @@
 	}
 }
 
+func (a *AndroidTest) AndroidMk() android.AndroidMkData {
+	data := a.AndroidApp.AndroidMk()
+	data.Extra = append(data.Extra, func(w io.Writer, outputFile android.Path) {
+		fmt.Fprintln(w, "LOCAL_MODULE_TAGS := tests")
+		if len(a.testProperties.Test_suites) > 0 {
+			fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE :=",
+				strings.Join(a.testProperties.Test_suites, " "))
+		}
+	})
+
+	return data
+}
+
 func (a *AndroidLibrary) AndroidMk() android.AndroidMkData {
 	data := a.Library.AndroidMk()
 
@@ -286,26 +304,55 @@
 				if ddoc.Javadoc.stubsSrcJar != nil {
 					fmt.Fprintln(w, "LOCAL_DROIDDOC_STUBS_SRCJAR := ", ddoc.Javadoc.stubsSrcJar.String())
 				}
+				if ddoc.checkCurrentApiTimestamp != nil {
+					fmt.Fprintln(w, ".PHONY:", ddoc.Name()+"-check-current-api")
+					fmt.Fprintln(w, ddoc.Name()+"-check-current-api:",
+						ddoc.checkCurrentApiTimestamp.String())
+
+					fmt.Fprintln(w, ".PHONY: checkapi")
+					fmt.Fprintln(w, "checkapi:",
+						ddoc.checkCurrentApiTimestamp.String())
+
+					fmt.Fprintln(w, ".PHONY: droidcore")
+					fmt.Fprintln(w, "droidcore: checkapi")
+				}
+				if ddoc.updateCurrentApiTimestamp != nil {
+					fmt.Fprintln(w, ".PHONY:", ddoc.Name(), "-update-current-api")
+					fmt.Fprintln(w, ddoc.Name()+"-update-current-api:",
+						ddoc.updateCurrentApiTimestamp.String())
+
+					fmt.Fprintln(w, ".PHONY: update-api")
+					fmt.Fprintln(w, "update-api:",
+						ddoc.updateCurrentApiTimestamp.String())
+				}
+				if ddoc.checkLastReleasedApiTimestamp != nil {
+					fmt.Fprintln(w, ".PHONY:", ddoc.Name()+"-check-last-released-api")
+					fmt.Fprintln(w, ddoc.Name()+"-check-last-released-api:",
+						ddoc.checkLastReleasedApiTimestamp.String())
+				}
 				apiFilePrefix := "INTERNAL_PLATFORM_"
 				if String(ddoc.properties.Api_tag_name) != "" {
 					apiFilePrefix += String(ddoc.properties.Api_tag_name) + "_"
 				}
-				if String(ddoc.properties.Api_filename) != "" {
+				if ddoc.apiFile != nil {
 					fmt.Fprintln(w, apiFilePrefix+"API_FILE := ", ddoc.apiFile.String())
 				}
-				if String(ddoc.properties.Private_api_filename) != "" {
+				if ddoc.dexApiFile != nil {
+					fmt.Fprintln(w, apiFilePrefix+"DEX_API_FILE := ", ddoc.dexApiFile.String())
+				}
+				if ddoc.privateApiFile != nil {
 					fmt.Fprintln(w, apiFilePrefix+"PRIVATE_API_FILE := ", ddoc.privateApiFile.String())
 				}
-				if String(ddoc.properties.Private_dex_api_filename) != "" {
+				if ddoc.privateDexApiFile != nil {
 					fmt.Fprintln(w, apiFilePrefix+"PRIVATE_DEX_API_FILE := ", ddoc.privateDexApiFile.String())
 				}
-				if String(ddoc.properties.Removed_api_filename) != "" {
+				if ddoc.removedApiFile != nil {
 					fmt.Fprintln(w, apiFilePrefix+"REMOVED_API_FILE := ", ddoc.removedApiFile.String())
 				}
-				if String(ddoc.properties.Removed_dex_api_filename) != "" {
+				if ddoc.removedDexApiFile != nil {
 					fmt.Fprintln(w, apiFilePrefix+"REMOVED_DEX_API_FILE := ", ddoc.removedDexApiFile.String())
 				}
-				if String(ddoc.properties.Exact_api_filename) != "" {
+				if ddoc.exactApiFile != nil {
 					fmt.Fprintln(w, apiFilePrefix+"EXACT_API_FILE := ", ddoc.exactApiFile.String())
 				}
 			},
diff --git a/java/app.go b/java/app.go
index ae0592a..9e7530e 100644
--- a/java/app.go
+++ b/java/app.go
@@ -26,6 +26,7 @@
 
 func init() {
 	android.RegisterModuleType("android_app", AndroidAppFactory)
+	android.RegisterModuleType("android_test", AndroidTestFactory)
 }
 
 // AndroidManifest.xml merging
@@ -50,8 +51,6 @@
 
 	// list of resource labels to generate individual resource packages
 	Package_splits []string
-
-	Instrumentation_for *string
 }
 
 type AndroidApp struct {
@@ -61,6 +60,8 @@
 	certificate certificate
 
 	appProperties appProperties
+
+	extraLinkFlags []string
 }
 
 func (a *AndroidApp) ExportedProguardFlagFiles() android.Paths {
@@ -71,6 +72,10 @@
 	return nil
 }
 
+func (a *AndroidApp) ExportedManifest() android.Path {
+	return a.manifestPath
+}
+
 var _ AndroidLibraryDependency = (*AndroidApp)(nil)
 
 type certificate struct {
@@ -80,19 +85,16 @@
 func (a *AndroidApp) DepsMutator(ctx android.BottomUpMutatorContext) {
 	a.Module.deps(ctx)
 	if !Bool(a.properties.No_framework_libs) && !Bool(a.properties.No_standard_libs) {
-		a.aapt.deps(ctx, String(a.deviceProperties.Sdk_version))
+		a.aapt.deps(ctx, sdkContext(a))
 	}
 }
 
 func (a *AndroidApp) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	var linkFlags []string
-	if String(a.appProperties.Instrumentation_for) != "" {
-		linkFlags = append(linkFlags,
-			"--rename-instrumentation-target-package",
-			String(a.appProperties.Instrumentation_for))
-	} else {
-		a.properties.Instrument = true
-	}
+	a.generateAndroidBuildActions(ctx)
+}
+
+func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) {
+	linkFlags := append([]string(nil), a.extraLinkFlags...)
 
 	hasProduct := false
 	for _, f := range a.aaptProperties.Aaptflags {
@@ -119,7 +121,7 @@
 	// TODO: LOCAL_PACKAGE_OVERRIDES
 	//    $(addprefix --rename-manifest-package , $(PRIVATE_MANIFEST_PACKAGE_NAME)) \
 
-	a.aapt.buildActions(ctx, String(a.deviceProperties.Sdk_version), linkFlags...)
+	a.aapt.buildActions(ctx, sdkContext(a), linkFlags...)
 
 	// apps manifests are handled by aapt, don't let Module see them
 	a.properties.Manifest = nil
@@ -188,6 +190,9 @@
 	module.Module.deviceProperties.Optimize.Enabled = proptools.BoolPtr(true)
 	module.Module.deviceProperties.Optimize.Shrink = proptools.BoolPtr(true)
 
+	module.Module.properties.Instrument = true
+	module.Module.properties.Installable = proptools.BoolPtr(true)
+
 	module.AddProperties(
 		&module.Module.properties,
 		&module.Module.deviceProperties,
@@ -198,3 +203,45 @@
 	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
 	return module
 }
+
+type appTestProperties struct {
+	Instrumentation_for *string
+}
+
+type AndroidTest struct {
+	AndroidApp
+
+	appTestProperties appTestProperties
+
+	testProperties testProperties
+}
+
+func (a *AndroidTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	if String(a.appTestProperties.Instrumentation_for) != "" {
+		a.AndroidApp.extraLinkFlags = append(a.AndroidApp.extraLinkFlags,
+			"--rename-instrumentation-target-package",
+			String(a.appTestProperties.Instrumentation_for))
+	}
+
+	a.generateAndroidBuildActions(ctx)
+}
+
+func AndroidTestFactory() android.Module {
+	module := &AndroidTest{}
+
+	module.Module.deviceProperties.Optimize.Enabled = proptools.BoolPtr(true)
+	module.Module.properties.Installable = proptools.BoolPtr(true)
+
+	module.AddProperties(
+		&module.Module.properties,
+		&module.Module.deviceProperties,
+		&module.Module.protoProperties,
+		&module.aaptProperties,
+		&module.appProperties,
+		&module.appTestProperties,
+		&module.testProperties)
+
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+
+	return module
+}
diff --git a/java/app_test.go b/java/app_test.go
index 6770119..c7c94ec 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -71,7 +71,10 @@
 
 			foo := ctx.ModuleForTests("foo", "android_common")
 
-			expectedLinkImplicits := []string{"AndroidManifest.xml"}
+			var expectedLinkImplicits []string
+
+			manifestFixer := foo.Output("manifest_fixer/AndroidManifest.xml")
+			expectedLinkImplicits = append(expectedLinkImplicits, manifestFixer.Output.String())
 
 			frameworkRes := ctx.ModuleForTests("framework-res", "android_common")
 			expectedLinkImplicits = append(expectedLinkImplicits,
diff --git a/java/builder.go b/java/builder.go
index d36e0dc..55be3a6 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -26,7 +26,6 @@
 	"github.com/google/blueprint"
 
 	"android/soong/android"
-	"android/soong/java/config"
 )
 
 var (
@@ -42,10 +41,11 @@
 		blueprint.RuleParams{
 			Command: `rm -rf "$outDir" "$annoDir" "$srcJarDir" && mkdir -p "$outDir" "$annoDir" "$srcJarDir" && ` +
 				`${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
+				`(if [ -s $srcJarDir/list ] || [ -s $out.rsp ] ; then ` +
 				`${config.SoongJavacWrapper} ${config.JavacWrapper}${config.JavacCmd} ${config.JavacHeapFlags} ${config.CommonJdkFlags} ` +
-				`$javacFlags $bootClasspath $classpath ` +
+				`$processorpath $javacFlags $bootClasspath $classpath ` +
 				`-source $javaVersion -target $javaVersion ` +
-				`-d $outDir -s $annoDir @$out.rsp @$srcJarDir/list && ` +
+				`-d $outDir -s $annoDir @$out.rsp @$srcJarDir/list ; fi ) && ` +
 				`${config.SoongZipCmd} -jar -o $out -C $outDir -D $outDir`,
 			CommandDeps: []string{
 				"${config.JavacCmd}",
@@ -56,7 +56,7 @@
 			Rspfile:          "$out.rsp",
 			RspfileContent:   "$in",
 		},
-		"javacFlags", "bootClasspath", "classpath", "srcJars", "srcJarDir",
+		"javacFlags", "bootClasspath", "classpath", "processorpath", "srcJars", "srcJarDir",
 		"outDir", "annoDir", "javaVersion")
 
 	kotlinc = pctx.AndroidGomaStaticRule("kotlinc",
@@ -80,29 +80,6 @@
 		},
 		"kotlincFlags", "classpath", "srcJars", "srcJarDir", "outDir", "kotlinJvmTarget")
 
-	errorprone = pctx.AndroidStaticRule("errorprone",
-		blueprint.RuleParams{
-			Command: `rm -rf "$outDir" "$annoDir" "$srcJarDir" && mkdir -p "$outDir" "$annoDir" "$srcJarDir" && ` +
-				`${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
-				`${config.SoongJavacWrapper} ${config.ErrorProneCmd} ` +
-				`$javacFlags $bootClasspath $classpath ` +
-				`-source $javaVersion -target $javaVersion ` +
-				`-d $outDir -s $annoDir @$out.rsp @$srcJarDir/list && ` +
-				`${config.SoongZipCmd} -jar -o $out -C $outDir -D $outDir`,
-			CommandDeps: []string{
-				"${config.JavaCmd}",
-				"${config.ErrorProneJavacJar}",
-				"${config.ErrorProneJar}",
-				"${config.SoongZipCmd}",
-				"${config.ZipSyncCmd}",
-			},
-			CommandOrderOnly: []string{"${config.SoongJavacWrapper}"},
-			Rspfile:          "$out.rsp",
-			RspfileContent:   "$in",
-		},
-		"javacFlags", "bootClasspath", "classpath", "srcJars", "srcJarDir",
-		"outDir", "annoDir", "javaVersion")
-
 	turbine = pctx.AndroidStaticRule("turbine",
 		blueprint.RuleParams{
 			Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
@@ -155,11 +132,13 @@
 	javacFlags    string
 	bootClasspath classpath
 	classpath     classpath
+	processorPath classpath
 	systemModules classpath
 	aidlFlags     string
 	javaVersion   string
 
 	errorProneExtraJavacFlags string
+	errorProneProcessorPath   classpath
 
 	kotlincFlags     string
 	kotlincClasspath classpath
@@ -207,26 +186,24 @@
 		desc += strconv.Itoa(shardIdx)
 	}
 
-	transformJavaToClasses(ctx, outputFile, shardIdx, srcFiles, srcJars, flags, deps, "javac", desc, javac)
+	transformJavaToClasses(ctx, outputFile, shardIdx, srcFiles, srcJars, flags, deps, "javac", desc)
 }
 
 func RunErrorProne(ctx android.ModuleContext, outputFile android.WritablePath,
 	srcFiles, srcJars android.Paths, flags javaBuilderFlags) {
 
-	if config.ErrorProneJar == "" {
-		ctx.ModuleErrorf("cannot build with Error Prone, missing external/error_prone?")
-	}
+	flags.processorPath = append(flags.errorProneProcessorPath, flags.processorPath...)
 
 	if len(flags.errorProneExtraJavacFlags) > 0 {
 		if len(flags.javacFlags) > 0 {
-			flags.javacFlags = flags.errorProneExtraJavacFlags + " " + flags.javacFlags
+			flags.javacFlags += " " + flags.errorProneExtraJavacFlags
 		} else {
 			flags.javacFlags = flags.errorProneExtraJavacFlags
 		}
 	}
 
 	transformJavaToClasses(ctx, outputFile, -1, srcFiles, srcJars, flags, nil,
-		"errorprone", "errorprone", errorprone)
+		"errorprone", "errorprone")
 }
 
 func TransformJavaToHeaderClasses(ctx android.ModuleContext, outputFile android.WritablePath,
@@ -243,7 +220,7 @@
 		// ensure java does not fall back to the default bootclasspath.
 		bootClasspath = `--bootclasspath ""`
 	} else {
-		bootClasspath = strings.Join(flags.bootClasspath.FormDesugarClasspath("--bootclasspath"), " ")
+		bootClasspath = strings.Join(flags.bootClasspath.FormTurbineClasspath("--bootclasspath"), " ")
 	}
 
 	ctx.Build(pctx, android.BuildParams{
@@ -256,7 +233,7 @@
 			"javacFlags":    flags.javacFlags,
 			"bootClasspath": bootClasspath,
 			"srcJars":       strings.Join(srcJars.Strings(), " "),
-			"classpath":     strings.Join(flags.classpath.FormDesugarClasspath("--classpath"), " "),
+			"classpath":     strings.Join(flags.classpath.FormTurbineClasspath("--classpath"), " "),
 			"outDir":        android.PathForModuleOut(ctx, "turbine", "classes").String(),
 			"javaVersion":   flags.javaVersion,
 		},
@@ -275,7 +252,7 @@
 func transformJavaToClasses(ctx android.ModuleContext, outputFile android.WritablePath,
 	shardIdx int, srcFiles, srcJars android.Paths,
 	flags javaBuilderFlags, deps android.Paths,
-	intermediatesDir, desc string, rule blueprint.Rule) {
+	intermediatesDir, desc string) {
 
 	deps = append(deps, srcJars...)
 
@@ -295,6 +272,7 @@
 	}
 
 	deps = append(deps, flags.classpath...)
+	deps = append(deps, flags.processorPath...)
 
 	srcJarDir := "srcjars"
 	outDir := "classes"
@@ -306,7 +284,7 @@
 		annoDir = filepath.Join(shardDir, annoDir)
 	}
 	ctx.Build(pctx, android.BuildParams{
-		Rule:        rule,
+		Rule:        javac,
 		Description: desc,
 		Output:      outputFile,
 		Inputs:      srcFiles,
@@ -315,6 +293,7 @@
 			"javacFlags":    flags.javacFlags,
 			"bootClasspath": bootClasspath,
 			"classpath":     flags.classpath.FormJavaClassPath("-classpath"),
+			"processorpath": flags.processorPath.FormJavaClassPath("-processorpath"),
 			"srcJars":       strings.Join(srcJars.Strings(), " "),
 			"srcJarDir":     android.PathForModuleOut(ctx, intermediatesDir, srcJarDir).String(),
 			"outDir":        android.PathForModuleOut(ctx, intermediatesDir, outDir).String(),
@@ -339,7 +318,8 @@
 }
 
 func TransformJarsToJar(ctx android.ModuleContext, outputFile android.WritablePath, desc string,
-	jars android.Paths, manifest android.OptionalPath, stripDirs bool, dirsToStrip []string) {
+	jars android.Paths, manifest android.OptionalPath, stripDirEntries bool, filesToStrip []string,
+	dirsToStrip []string) {
 
 	var deps android.Paths
 
@@ -349,22 +329,19 @@
 		deps = append(deps, manifest.Path())
 	}
 
-	if dirsToStrip != nil {
-		for _, dir := range dirsToStrip {
-			jarArgs = append(jarArgs, "-stripDir ", dir)
-		}
+	for _, dir := range dirsToStrip {
+		jarArgs = append(jarArgs, "-stripDir ", dir)
+	}
+
+	for _, file := range filesToStrip {
+		jarArgs = append(jarArgs, "-stripFile ", file)
 	}
 
 	// Remove any module-info.class files that may have come from prebuilt jars, they cause problems
 	// for downstream tools like desugar.
 	jarArgs = append(jarArgs, "-stripFile module-info.class")
 
-	// Remove any kotlin-reflect related files
-	// TODO(pszczepaniak): Support kotlin-reflect
-	jarArgs = append(jarArgs, "-stripFile \"*.kotlin_module\"")
-	jarArgs = append(jarArgs, "-stripFile \"*.kotlin_builtin\"")
-
-	if stripDirs {
+	if stripDirEntries {
 		jarArgs = append(jarArgs, "-D")
 	}
 
@@ -419,7 +396,7 @@
 	}
 }
 
-func (x *classpath) FormDesugarClasspath(optName string) []string {
+func (x *classpath) FormTurbineClasspath(optName string) []string {
 	if x == nil || *x == nil {
 		return nil
 	}
diff --git a/java/config/config.go b/java/config/config.go
index 6633f79..ae497a6 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -30,6 +30,8 @@
 	DefaultBootclasspathLibraries = []string{"core-oj", "core-libart"}
 	DefaultSystemModules          = "core-system-modules"
 	DefaultLibraries              = []string{"ext", "framework", "okhttp"}
+	DefaultLambdaStubsLibrary     = "core-lambda-stubs"
+	SdkLambdaStubsPath            = "prebuilts/sdk/tools/core-lambda-stubs.jar"
 
 	DefaultJacocoExcludeFilter = []string{"org.junit.*", "org.jacoco.*", "org.mockito.*"}
 
@@ -40,6 +42,16 @@
 		"android.car",
 		"android.car7",
 	}
+
+	ManifestMergerClasspath = []string{
+		"prebuilts/gradle-plugin/com/android/tools/build/manifest-merger/26.1.0/manifest-merger-26.1.0.jar",
+		"prebuilts/gradle-plugin/com/android/tools/common/26.1.0/common-26.1.0.jar",
+		"prebuilts/gradle-plugin/com/android/tools/sdk-common/26.1.0/sdk-common-26.1.0.jar",
+		"prebuilts/gradle-plugin/com/android/tools/sdklib/26.1.0/sdklib-26.1.0.jar",
+		"prebuilts/gradle-plugin/org/jetbrains/kotlin/kotlin-runtime/1.0.5/kotlin-runtime-1.0.5.jar",
+		"prebuilts/gradle-plugin/org/jetbrains/kotlin/kotlin-stdlib/1.1.3/kotlin-stdlib-1.1.3.jar",
+		"prebuilts/misc/common/guava/guava-21.0.jar",
+	}
 )
 
 func init() {
@@ -90,18 +102,7 @@
 	pctx.HostBinToolVariable("MergeZipsCmd", "merge_zips")
 	pctx.HostBinToolVariable("Zip2ZipCmd", "zip2zip")
 	pctx.HostBinToolVariable("ZipSyncCmd", "zipsync")
-	pctx.VariableFunc("DxCmd", func(ctx android.PackageVarContext) string {
-		config := ctx.Config()
-		if config.IsEnvFalse("USE_D8") {
-			if config.UnbundledBuild() || config.IsPdkBuild() {
-				return "prebuilts/build-tools/common/bin/dx"
-			} else {
-				return pctx.HostBinToolPath(ctx, "dx").String()
-			}
-		} else {
-			return pctx.HostBinToolPath(ctx, "d8-compat-dx").String()
-		}
-	})
+	pctx.HostBinToolVariable("ApiCheckCmd", "apicheck")
 	pctx.HostBinToolVariable("D8Cmd", "d8")
 	pctx.HostBinToolVariable("R8Cmd", "r8-compat-proguard")
 
@@ -115,9 +116,9 @@
 	})
 
 	pctx.HostJavaToolVariable("JarjarCmd", "jarjar.jar")
-	pctx.HostJavaToolVariable("DesugarJar", "desugar.jar")
 	pctx.HostJavaToolVariable("JsilverJar", "jsilver.jar")
 	pctx.HostJavaToolVariable("DoclavaJar", "doclava.jar")
+	pctx.HostJavaToolVariable("MetalavaJar", "metalava.jar")
 
 	pctx.HostBinToolVariable("SoongJavacWrapper", "soong_javac_wrapper")
 
@@ -141,4 +142,9 @@
 	}
 
 	hostBinToolVariableWithPrebuilt("Aapt2Cmd", "prebuilts/sdk/tools", "aapt2")
+
+	pctx.SourcePathVariable("ManifestFixerCmd", "build/soong/scripts/manifest_fixer.py")
+
+	pctx.SourcePathsVariable("ManifestMergerJars", " ", ManifestMergerClasspath...)
+	pctx.SourcePathsVariable("ManifestMergerClasspath", ":", ManifestMergerClasspath...)
 }
diff --git a/java/config/error_prone.go b/java/config/error_prone.go
index f203234..48681b5 100644
--- a/java/config/error_prone.go
+++ b/java/config/error_prone.go
@@ -14,34 +14,40 @@
 
 package config
 
-import "android/soong/android"
+import (
+	"strings"
+
+	"android/soong/android"
+)
 
 var (
 	// These will be filled out by external/error_prone/soong/error_prone.go if it is available
-	ErrorProneJavacJar    string
-	ErrorProneJar         string
-	ErrorProneClasspath   string
-	ErrorProneChecksError string
-	ErrorProneFlags       string
+	ErrorProneClasspath             []string
+	ErrorProneChecksError           []string
+	ErrorProneChecksWarning         []string
+	ErrorProneChecksDefaultDisabled []string
+	ErrorProneChecksOff             []string
+	ErrorProneFlags                 []string
 )
 
 // 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) {
+func errorProneVar(name string, val *[]string, sep string) {
 	pctx.VariableFunc(name, func(android.PackageVarContext) string {
-		return *val
+		return strings.Join(*val, sep)
 	})
 }
 
 func init() {
-	errorProneVar("ErrorProneJar", &ErrorProneJar)
-	errorProneVar("ErrorProneJavacJar", &ErrorProneJavacJar)
-	errorProneVar("ErrorProneClasspath", &ErrorProneClasspath)
-	errorProneVar("ErrorProneChecksError", &ErrorProneChecksError)
-	errorProneVar("ErrorProneFlags", &ErrorProneFlags)
-
-	pctx.StaticVariable("ErrorProneCmd",
-		"${JavaCmd} -Xmx${JavacHeapSize} -Xbootclasspath/p:${ErrorProneJavacJar} "+
-			"-cp ${ErrorProneJar}:${ErrorProneClasspath} "+
-			"${ErrorProneFlags} ${CommonJdkFlags} ${ErrorProneChecksError}")
-
+	errorProneVar("ErrorProneClasspath", &ErrorProneClasspath, ":")
+	errorProneVar("ErrorProneChecksError", &ErrorProneChecksError, " ")
+	errorProneVar("ErrorProneChecksWarning", &ErrorProneChecksWarning, " ")
+	errorProneVar("ErrorProneChecksDefaultDisabled", &ErrorProneChecksDefaultDisabled, " ")
+	errorProneVar("ErrorProneChecksOff", &ErrorProneChecksOff, " ")
+	errorProneVar("ErrorProneFlags", &ErrorProneFlags, " ")
+	pctx.StaticVariable("ErrorProneChecks", strings.Join([]string{
+		"${ErrorProneChecksOff}",
+		"${ErrorProneChecksError}",
+		"${ErrorProneChecksWarning}",
+		"${ErrorProneChecksDefaultDisabled}",
+	}, " "))
 }
diff --git a/java/config/makevars.go b/java/config/makevars.go
index 27c7daa..275f496 100644
--- a/java/config/makevars.go
+++ b/java/config/makevars.go
@@ -46,31 +46,23 @@
 	ctx.Strict("JAVADOC", "${JavadocCmd}")
 	ctx.Strict("COMMON_JDK_FLAGS", "${CommonJdkFlags}")
 
-	if ctx.Config().UseD8Desugar() {
-		ctx.Strict("DX", "${D8Cmd}")
-		ctx.Strict("DX_COMMAND", "${D8Cmd} -JXms16M -JXmx2048M")
-		ctx.Strict("USE_D8_DESUGAR", "true")
-	} else {
-		ctx.Strict("DX", "${DxCmd}")
-		ctx.Strict("DX_COMMAND", "${DxCmd} -JXms16M -JXmx2048M")
-		ctx.Strict("USE_D8_DESUGAR", "false")
-	}
+	ctx.Strict("DX", "${D8Cmd}")
+	ctx.Strict("DX_COMMAND", "${D8Cmd} -JXms16M -JXmx2048M")
 	ctx.Strict("R8_COMPAT_PROGUARD", "${R8Cmd}")
 
 	ctx.Strict("TURBINE", "${TurbineJar}")
 
-	if ctx.Config().IsEnvTrue("RUN_ERROR_PRONE") {
-		ctx.Strict("TARGET_JAVAC", "${ErrorProneCmd}")
-		ctx.Strict("HOST_JAVAC", "${ErrorProneCmd}")
-	} else {
-		ctx.Strict("TARGET_JAVAC", "${JavacCmd} ${CommonJdkFlags}")
-		ctx.Strict("HOST_JAVAC", "${JavacCmd} ${CommonJdkFlags}")
+	if ctx.Config().RunErrorProne() {
+		ctx.Strict("ERROR_PRONE_JARS", strings.Join(ErrorProneClasspath, " "))
+		ctx.Strict("ERROR_PRONE_FLAGS", "${ErrorProneFlags}")
+		ctx.Strict("ERROR_PRONE_CHECKS", "${ErrorProneChecks}")
 	}
 
-	if ctx.Config().UseOpenJDK9() {
-		ctx.Strict("JLINK", "${JlinkCmd}")
-		ctx.Strict("JMOD", "${JmodCmd}")
-	}
+	ctx.Strict("TARGET_JAVAC", "${JavacCmd} ${CommonJdkFlags}")
+	ctx.Strict("HOST_JAVAC", "${JavacCmd} ${CommonJdkFlags}")
+
+	ctx.Strict("JLINK", "${JlinkCmd}")
+	ctx.Strict("JMOD", "${JmodCmd}")
 
 	ctx.Strict("SOONG_JAVAC_WRAPPER", "${SoongJavacWrapper}")
 	ctx.Strict("ZIPSYNC", "${ZipSyncCmd}")
@@ -79,4 +71,10 @@
 	ctx.Strict("DEFAULT_JACOCO_EXCLUDE_FILTER", strings.Join(DefaultJacocoExcludeFilter, ","))
 
 	ctx.Strict("EXTRACT_JAR_PACKAGES", "${ExtractJarPackagesCmd}")
+
+	ctx.Strict("MANIFEST_FIXER", "${ManifestFixerCmd}")
+
+	ctx.Strict("ANDROID_MANIFEST_MERGER_DEPS", "${ManifestMergerJars}")
+	ctx.Strict("ANDROID_MANIFEST_MERGER",
+		"${JavaCmd} -classpath ${ManifestMergerClasspath} com.android.manifmerger.Merger")
 }
diff --git a/java/dex.go b/java/dex.go
index 66e71b5..db240fc 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -22,84 +22,12 @@
 	"android/soong/android"
 )
 
-var desugar = pctx.AndroidStaticRule("desugar",
-	blueprint.RuleParams{
-		Command: `rm -rf $dumpDir && mkdir -p $dumpDir && ` +
-			`${config.JavaCmd} ` +
-			`-Djdk.internal.lambda.dumpProxyClasses=$$(cd $dumpDir && pwd) ` +
-			`$javaFlags ` +
-			`-jar ${config.DesugarJar} $classpathFlags $desugarFlags ` +
-			`-i $in -o $out`,
-		CommandDeps: []string{"${config.DesugarJar}", "${config.JavaCmd}"},
-	},
-	"javaFlags", "classpathFlags", "desugarFlags", "dumpDir")
-
-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")
-	}
-
-	desugarJar := android.PathForModuleOut(ctx, "desugar", jarName)
-	dumpDir := android.PathForModuleOut(ctx, "desugar", "classes")
-
-	javaFlags := ""
-	if ctx.Config().UseOpenJDK9() {
-		javaFlags = "--add-opens java.base/java.lang.invoke=ALL-UNNAMED"
-	}
-
-	var classpathFlags []string
-	classpathFlags = append(classpathFlags, flags.bootClasspath.FormDesugarClasspath("--bootclasspath_entry")...)
-	classpathFlags = append(classpathFlags, flags.classpath.FormDesugarClasspath("--classpath_entry")...)
-
-	var deps android.Paths
-	deps = append(deps, flags.bootClasspath...)
-	deps = append(deps, flags.classpath...)
-
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        desugar,
-		Description: "desugar",
-		Output:      desugarJar,
-		Input:       classesJar,
-		Implicits:   deps,
-		Args: map[string]string{
-			"dumpDir":        dumpDir.String(),
-			"javaFlags":      javaFlags,
-			"classpathFlags": strings.Join(classpathFlags, " "),
-			"desugarFlags":   strings.Join(desugarFlags, " "),
-		},
-	})
-
-	return desugarJar
-}
-
-var dx = pctx.AndroidStaticRule("dx",
-	blueprint.RuleParams{
-		Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
-			`${config.DxCmd} --dex --output=$outDir $dxFlags $in && ` +
-			`${config.SoongZipCmd} -o $outDir/classes.dex.jar -C $outDir -D $outDir && ` +
-			`${config.MergeZipsCmd} -D -stripFile "*.class" $out $outDir/classes.dex.jar $in`,
-		CommandDeps: []string{
-			"${config.DxCmd}",
-			"${config.SoongZipCmd}",
-			"${config.MergeZipsCmd}",
-		},
-	},
-	"outDir", "dxFlags")
-
 var d8 = pctx.AndroidStaticRule("d8",
 	blueprint.RuleParams{
 		Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
 			`${config.D8Cmd} --output $outDir $dxFlags $in && ` +
 			`${config.SoongZipCmd} -o $outDir/classes.dex.jar -C $outDir -D $outDir && ` +
-			`${config.MergeZipsCmd} -D -stripFile "*.class" $out $outDir/classes.dex.jar $in`,
+			`${config.MergeZipsCmd} -D -stripFile "**/*.class" $out $outDir/classes.dex.jar $in`,
 		CommandDeps: []string{
 			"${config.D8Cmd}",
 			"${config.SoongZipCmd}",
@@ -116,7 +44,7 @@
 			`-printmapping $outDict ` +
 			`$dxFlags $r8Flags && ` +
 			`${config.SoongZipCmd} -o $outDir/classes.dex.jar -C $outDir -D $outDir && ` +
-			`${config.MergeZipsCmd} -D -stripFile "*.class" $out $outDir/classes.dex.jar $in`,
+			`${config.MergeZipsCmd} -D -stripFile "**/*.class" $out $outDir/classes.dex.jar $in`,
 		CommandDeps: []string{
 			"${config.R8Cmd}",
 			"${config.SoongZipCmd}",
@@ -125,39 +53,29 @@
 	},
 	"outDir", "outDict", "dxFlags", "r8Flags")
 
-func (j *Module) dxFlags(ctx android.ModuleContext, fullD8 bool) []string {
+func (j *Module) dxFlags(ctx android.ModuleContext) []string {
 	flags := j.deviceProperties.Dxflags
-	if fullD8 {
-		// Translate all the DX flags to D8 ones until all the build files have been migrated
-		// to D8 flags. See: b/69377755
-		flags = android.RemoveListFromList(flags,
-			[]string{"--core-library", "--dex", "--multi-dex"})
-	}
+	// Translate all the DX flags to D8 ones until all the build files have been migrated
+	// to D8 flags. See: b/69377755
+	flags = android.RemoveListFromList(flags,
+		[]string{"--core-library", "--dex", "--multi-dex"})
 
 	if ctx.Config().Getenv("NO_OPTIMIZE_DX") != "" {
-		if fullD8 {
-			flags = append(flags, "--debug")
-		} else {
-			flags = append(flags, "--no-optimize")
-		}
+		flags = append(flags, "--debug")
 	}
 
 	if ctx.Config().Getenv("GENERATE_DEX_DEBUG") != "" {
 		flags = append(flags,
 			"--debug",
 			"--verbose")
-		if !fullD8 {
-			flags = append(flags,
-				"--dump-to="+android.PathForModuleOut(ctx, "classes.lst").String(),
-				"--dump-width=1000")
-		}
 	}
 
-	if fullD8 {
-		flags = append(flags, "--min-api "+j.minSdkVersionNumber(ctx))
-	} else {
-		flags = append(flags, "--min-sdk-version="+j.minSdkVersionNumber(ctx))
+	minSdkVersion, err := sdkVersionToNumberAsString(ctx, j.minSdkVersion())
+	if err != nil {
+		ctx.PropertyErrorf("min_sdk_version", "%s", err)
 	}
+
+	flags = append(flags, "--min-api "+minSdkVersion)
 	return flags
 }
 
@@ -166,7 +84,7 @@
 
 	// When an app contains references to APIs that are not in the SDK specified by
 	// its LOCAL_SDK_VERSION for example added by support library or by runtime
-	// classes added by desugar, we artifically raise the "SDK version" "linked" by
+	// classes added by desugaring, we artifically raise the "SDK version" "linked" by
 	// ProGuard, to
 	// - suppress ProGuard warnings of referencing symbols unknown to the lower SDK version.
 	// - prevent ProGuard stripping subclass in the support library that extends class added in the higher SDK version.
@@ -223,13 +141,8 @@
 	classesJar android.Path, jarName string) android.Path {
 
 	useR8 := Bool(j.deviceProperties.Optimize.Enabled)
-	fullD8 := useR8 || ctx.Config().UseD8Desugar()
 
-	if !fullD8 {
-		classesJar = j.desugar(ctx, flags, classesJar, jarName)
-	}
-
-	dxFlags := j.dxFlags(ctx, fullD8)
+	dxFlags := j.dxFlags(ctx)
 
 	// Compile classes.jar into classes.dex and then javalib.jar
 	javalibJar := android.PathForModuleOut(ctx, "dex", jarName)
@@ -238,14 +151,16 @@
 	if useR8 {
 		// TODO(ccross): if this is an instrumentation test of an obfuscated app, use the
 		// dictionary of the app and move the app from libraryjars to injars.
-		j.proguardDictionary = android.PathForModuleOut(ctx, "proguard_dictionary")
+		proguardDictionary := android.PathForModuleOut(ctx, "proguard_dictionary")
+		j.proguardDictionary = proguardDictionary
 		r8Flags, r8Deps := j.r8Flags(ctx, flags)
 		ctx.Build(pctx, android.BuildParams{
-			Rule:        r8,
-			Description: "r8",
-			Output:      javalibJar,
-			Input:       classesJar,
-			Implicits:   r8Deps,
+			Rule:           r8,
+			Description:    "r8",
+			Output:         javalibJar,
+			ImplicitOutput: proguardDictionary,
+			Input:          classesJar,
+			Implicits:      r8Deps,
 			Args: map[string]string{
 				"dxFlags": strings.Join(dxFlags, " "),
 				"r8Flags": strings.Join(r8Flags, " "),
@@ -254,15 +169,9 @@
 			},
 		})
 	} else {
-		rule := dx
-		desc := "dx"
-		if fullD8 {
-			rule = d8
-			desc = "d8"
-		}
 		ctx.Build(pctx, android.BuildParams{
-			Rule:        rule,
-			Description: desc,
+			Rule:        d8,
+			Description: "d8",
 			Output:      javalibJar,
 			Input:       classesJar,
 			Args: map[string]string{
diff --git a/java/droiddoc.go b/java/droiddoc.go
index 07042a1..9ec2be8 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -19,6 +19,7 @@
 	"android/soong/java/config"
 	"fmt"
 	"path/filepath"
+	"runtime"
 	"strings"
 
 	"github.com/google/blueprint"
@@ -33,7 +34,7 @@
 				`$opts $bootclasspathArgs $classpathArgs -sourcepath $sourcepath ` +
 				`-d $outDir -quiet  && ` +
 				`${config.SoongZipCmd} -write_if_changed -d -o $docZip -C $outDir -D $outDir && ` +
-				`${config.SoongZipCmd} -write_if_changed -jar -o $out -C $stubsDir -D $stubsDir`,
+				`${config.SoongZipCmd} -write_if_changed -jar -o $out -C $stubsDir -D $stubsDir $postDoclavaCmds`,
 			CommandDeps: []string{
 				"${config.ZipSyncCmd}",
 				"${config.JavadocCmd}",
@@ -44,7 +45,48 @@
 			Restat:         true,
 		},
 		"outDir", "srcJarDir", "stubsDir", "srcJars", "opts",
-		"bootclasspathArgs", "classpathArgs", "sourcepath", "docZip")
+		"bootclasspathArgs", "classpathArgs", "sourcepath", "docZip", "postDoclavaCmds")
+
+	apiCheck = pctx.AndroidStaticRule("apiCheck",
+		blueprint.RuleParams{
+			Command: `( ${config.ApiCheckCmd} -JXmx1024m -J"classpath $classpath" $opts ` +
+				`$apiFile $apiFileToCheck $removedApiFile $removedApiFileToCheck ` +
+				`&& touch $out ) || (echo -e "$msg" ; exit 38)`,
+			CommandDeps: []string{
+				"${config.ApiCheckCmd}",
+			},
+		},
+		"classpath", "opts", "apiFile", "apiFileToCheck", "removedApiFile", "removedApiFileToCheck", "msg")
+
+	updateApi = pctx.AndroidStaticRule("updateApi",
+		blueprint.RuleParams{
+			Command: `( ( cp -f $apiFileToCheck $apiFile && cp -f $removedApiFileToCheck $removedApiFile ) ` +
+				`&& touch $out ) || (echo failed to update public API ; exit 38)`,
+		},
+		"apiFile", "apiFileToCheck", "removedApiFile", "removedApiFileToCheck")
+
+	metalava = pctx.AndroidStaticRule("metalava",
+		blueprint.RuleParams{
+			Command: `rm -rf "$outDir" "$srcJarDir" "$stubsDir" && mkdir -p "$outDir" "$srcJarDir" "$stubsDir" && ` +
+				`${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
+				`${config.JavaCmd} -jar ${config.MetalavaJar} -encoding UTF-8 -source $javaVersion @$out.rsp @$srcJarDir/list ` +
+				`$bootclasspathArgs $classpathArgs -sourcepath $sourcepath --no-banner --color --quiet ` +
+				`--stubs $stubsDir $opts && ` +
+				`${config.SoongZipCmd} -write_if_changed -d -o $docZip -C $outDir -D $outDir && ` +
+				`${config.SoongZipCmd} -write_if_changed -jar -o $out -C $stubsDir -D $stubsDir`,
+			CommandDeps: []string{
+				"${config.ZipSyncCmd}",
+				"${config.JavaCmd}",
+				"${config.MetalavaJar}",
+				"${config.JavadocCmd}",
+				"${config.SoongZipCmd}",
+			},
+			Rspfile:        "$out.rsp",
+			RspfileContent: "$in",
+			Restat:         true,
+		},
+		"outDir", "srcJarDir", "stubsDir", "srcJars", "javaVersion", "bootclasspathArgs",
+		"classpathArgs", "sourcepath", "opts", "docZip")
 )
 
 func init() {
@@ -57,6 +99,10 @@
 	android.RegisterModuleType("javadoc_host", JavadocHostFactory)
 }
 
+var (
+	srcsLibTag = dependencyTag{name: "sources from javalib"}
+)
+
 type JavadocProperties struct {
 	// list of source files used to compile the Java module.  May be .java, .logtags, .proto,
 	// or .aidl files.
@@ -92,6 +138,31 @@
 
 	// if not blank, set to the version of the sdk to compile against
 	Sdk_version *string `android:"arch_variant"`
+
+	Aidl struct {
+		// Top level directories to pass to aidl tool
+		Include_dirs []string
+
+		// Directories rooted at the Android.bp file to pass to aidl tool
+		Local_include_dirs []string
+	}
+
+	// If not blank, set the java version passed to javadoc as -source
+	Java_version *string
+}
+
+type ApiToCheck struct {
+	// path to the API txt file that the new API extracted from source code is checked
+	// against. The path can be local to the module or from other module (via :module syntax).
+	Api_file *string
+
+	// path to the API txt file that the new @removed API extractd from source code is
+	// checked against. The path can be local to the module or from other module (via
+	// :module syntax).
+	Removed_api_file *string
+
+	// Arguments to the apicheck tool.
+	Args *string
 }
 
 type DroiddocProperties struct {
@@ -130,6 +201,16 @@
 	// names of the output files used in args that will be generated
 	Out []string
 
+	// if set to true, collect the values used by the Dev tools and
+	// write them in files packaged with the SDK. Defaults to false.
+	Write_sdk_values *bool
+
+	// index.html under current module will be copied to docs out dir, if not null.
+	Static_doc_index_redirect *string
+
+	// source.properties under current module will be copied to docs out dir, if not null.
+	Static_doc_properties *string
+
 	// a list of files under current module source dir which contains known tags in Java sources.
 	// filegroup or genrule can be included within this property.
 	Knowntags []string
@@ -140,6 +221,9 @@
 	// the generated public API filename by Doclava.
 	Api_filename *string
 
+	// the generated public Dex API filename by Doclava.
+	Dex_api_filename *string
+
 	// the generated private API filename by Doclava.
 	Private_api_filename *string
 
@@ -152,11 +236,34 @@
 	// the generated removed Dex API filename by Doclava.
 	Removed_dex_api_filename *string
 
+	// mapping of dex signatures to source file and line number. This is a temporary property and
+	// will be deleted; you probably shouldn't be using it.
+	Dex_mapping_filename *string
+
 	// the generated exact API filename by Doclava.
 	Exact_api_filename *string
 
 	// if set to false, don't allow droiddoc to generate stubs source files. Defaults to true.
 	Create_stubs *bool
+
+	Check_api struct {
+		Last_released ApiToCheck
+
+		Current ApiToCheck
+	}
+
+	// if set to true, create stubs through Metalava instead of Doclava. Javadoc/Doclava is
+	// currently still used for documentation generation, and will be replaced by Dokka soon.
+	Metalava_enabled *bool
+
+	// user can specify the version of previous released API file in order to do compatibility check.
+	Metalava_previous_api *string
+
+	// is set to true, Metalava will allow framework SDK to contain annotations.
+	Metalava_annotations_enabled *bool
+
+	// a top level directory contains XML files set to merge annotations.
+	Metalava_merge_annotations_dir *string
 }
 
 type Javadoc struct {
@@ -184,11 +291,17 @@
 
 	properties        DroiddocProperties
 	apiFile           android.WritablePath
+	dexApiFile        android.WritablePath
 	privateApiFile    android.WritablePath
 	privateDexApiFile android.WritablePath
 	removedApiFile    android.WritablePath
 	removedDexApiFile android.WritablePath
 	exactApiFile      android.WritablePath
+	apiMappingFile    android.WritablePath
+
+	checkCurrentApiTimestamp      android.WritablePath
+	updateCurrentApiTimestamp     android.WritablePath
+	checkLastReleasedApiTimestamp android.WritablePath
 }
 
 func InitDroiddocModule(module android.DefaultableModule, hod android.HostOrDeviceSupported) {
@@ -234,20 +347,37 @@
 	return module
 }
 
+func (j *Javadoc) sdkVersion() string {
+	return String(j.properties.Sdk_version)
+}
+
+func (j *Javadoc) minSdkVersion() string {
+	return j.sdkVersion()
+}
+
 func (j *Javadoc) addDeps(ctx android.BottomUpMutatorContext) {
 	if ctx.Device() {
-		sdkDep := decodeSdkDep(ctx, String(j.properties.Sdk_version))
+		sdkDep := decodeSdkDep(ctx, sdkContext(j))
 		if sdkDep.useDefaultLibs {
 			ctx.AddDependency(ctx.Module(), bootClasspathTag, config.DefaultBootclasspathLibraries...)
+			if ctx.Config().TargetOpenJDK9() {
+				ctx.AddDependency(ctx.Module(), systemModulesTag, config.DefaultSystemModules)
+			}
 			if !Bool(j.properties.No_framework_libs) {
 				ctx.AddDependency(ctx.Module(), libTag, []string{"ext", "framework"}...)
 			}
 		} else if sdkDep.useModule {
-			ctx.AddDependency(ctx.Module(), bootClasspathTag, sdkDep.module)
+			if ctx.Config().TargetOpenJDK9() {
+				ctx.AddDependency(ctx.Module(), systemModulesTag, sdkDep.systemModules)
+			}
+			ctx.AddDependency(ctx.Module(), bootClasspathTag, sdkDep.modules...)
 		}
 	}
 
 	ctx.AddDependency(ctx.Module(), libTag, j.properties.Libs...)
+	if j.properties.Srcs_lib != nil {
+		ctx.AddDependency(ctx.Module(), srcsLibTag, *j.properties.Srcs_lib)
+	}
 
 	android.ExtractSourcesDeps(ctx, j.properties.Srcs)
 
@@ -268,52 +398,87 @@
 	}
 }
 
+func (j *Javadoc) collectBuilderFlags(ctx android.ModuleContext, deps deps) javaBuilderFlags {
+	var flags javaBuilderFlags
+
+	// aidl flags.
+	aidlFlags := j.aidlFlags(ctx, deps.aidlPreprocess, deps.aidlIncludeDirs)
+	if len(aidlFlags) > 0 {
+		// optimization.
+		ctx.Variable(pctx, "aidlFlags", strings.Join(aidlFlags, " "))
+		flags.aidlFlags = "$aidlFlags"
+	}
+
+	return flags
+}
+
+func (j *Javadoc) aidlFlags(ctx android.ModuleContext, aidlPreprocess android.OptionalPath,
+	aidlIncludeDirs android.Paths) []string {
+
+	aidlIncludes := android.PathsForModuleSrc(ctx, j.properties.Aidl.Local_include_dirs)
+	aidlIncludes = append(aidlIncludes, android.PathsForSource(ctx, j.properties.Aidl.Include_dirs)...)
+
+	var flags []string
+	if aidlPreprocess.Valid() {
+		flags = append(flags, "-p"+aidlPreprocess.String())
+	} else {
+		flags = append(flags, android.JoinWithPrefix(aidlIncludeDirs.Strings(), "-I"))
+	}
+
+	flags = append(flags, android.JoinWithPrefix(aidlIncludes.Strings(), "-I"))
+	flags = append(flags, "-I"+android.PathForModuleSrc(ctx).String())
+	if src := android.ExistentPathForSource(ctx, ctx.ModuleDir(), "src"); src.Valid() {
+		flags = append(flags, "-I"+src.String())
+	}
+
+	return flags
+}
+
+func (j *Javadoc) genSources(ctx android.ModuleContext, srcFiles android.Paths,
+	flags javaBuilderFlags) android.Paths {
+
+	outSrcFiles := make(android.Paths, 0, len(srcFiles))
+
+	for _, srcFile := range srcFiles {
+		switch srcFile.Ext() {
+		case ".aidl":
+			javaFile := genAidl(ctx, srcFile, flags.aidlFlags)
+			outSrcFiles = append(outSrcFiles, javaFile)
+		default:
+			outSrcFiles = append(outSrcFiles, srcFile)
+		}
+	}
+
+	return outSrcFiles
+}
+
 func (j *Javadoc) collectDeps(ctx android.ModuleContext) deps {
 	var deps deps
 
-	sdkDep := decodeSdkDep(ctx, String(j.properties.Sdk_version))
+	sdkDep := decodeSdkDep(ctx, sdkContext(j))
 	if sdkDep.invalidVersion {
-		ctx.AddMissingDependencies([]string{sdkDep.module})
+		ctx.AddMissingDependencies(sdkDep.modules)
 	} else if sdkDep.useFiles {
-		deps.bootClasspath = append(deps.bootClasspath, sdkDep.jar)
+		deps.bootClasspath = append(deps.bootClasspath, sdkDep.jars...)
 	}
 
 	ctx.VisitDirectDeps(func(module android.Module) {
 		otherName := ctx.OtherModuleName(module)
 		tag := ctx.OtherModuleDependencyTag(module)
 
-		switch dep := module.(type) {
-		case Dependency:
-			switch tag {
-			case bootClasspathTag:
+		switch tag {
+		case bootClasspathTag:
+			if dep, ok := module.(Dependency); ok {
 				deps.bootClasspath = append(deps.bootClasspath, dep.ImplementationJars()...)
-			case libTag:
-				deps.classpath = append(deps.classpath, dep.ImplementationJars()...)
-				if otherName == String(j.properties.Srcs_lib) {
-					srcs := dep.(SrcDependency).CompiledSrcs()
-					whitelistPathPrefixes := make(map[string]bool)
-					j.genWhitelistPathPrefixes(whitelistPathPrefixes)
-					for _, src := range srcs {
-						if _, ok := src.(android.WritablePath); ok { // generated sources
-							deps.srcs = append(deps.srcs, src)
-						} else { // select source path for documentation based on whitelist path prefixs.
-							for k, _ := range whitelistPathPrefixes {
-								if strings.HasPrefix(src.Rel(), k) {
-									deps.srcs = append(deps.srcs, src)
-									break
-								}
-							}
-						}
-					}
-					deps.srcJars = append(deps.srcJars, dep.(SrcDependency).CompiledSrcJars()...)
-				}
-			default:
+			} else {
 				panic(fmt.Errorf("unknown dependency %q for %q", otherName, ctx.ModuleName()))
 			}
-		case SdkLibraryDependency:
-			switch tag {
-			case libTag:
-				sdkVersion := String(j.properties.Sdk_version)
+		case libTag:
+			switch dep := module.(type) {
+			case Dependency:
+				deps.classpath = append(deps.classpath, dep.ImplementationJars()...)
+			case SdkLibraryDependency:
+				sdkVersion := j.sdkVersion()
 				linkType := javaSdk
 				if strings.HasPrefix(sdkVersion, "system_") || strings.HasPrefix(sdkVersion, "test_") {
 					linkType = javaSystem
@@ -321,31 +486,50 @@
 					linkType = javaPlatform
 				}
 				deps.classpath = append(deps.classpath, dep.HeaderJars(linkType)...)
-			default:
-				ctx.ModuleErrorf("dependency on java_sdk_library %q can only be in libs", otherName)
-			}
-		case android.SourceFileProducer:
-			switch tag {
-			case libTag:
+			case android.SourceFileProducer:
 				checkProducesJars(ctx, dep)
 				deps.classpath = append(deps.classpath, dep.Srcs()...)
-			case android.DefaultsDepTag, android.SourceDepTag:
-				// Nothing to do
-			default:
-				ctx.ModuleErrorf("dependency on genrule %q may only be in srcs, libs", otherName)
-			}
-		default:
-			switch tag {
-			case android.DefaultsDepTag, android.SourceDepTag, droiddocTemplateTag:
-				// Nothing to do
 			default:
 				ctx.ModuleErrorf("depends on non-java module %q", otherName)
 			}
+		case srcsLibTag:
+			switch dep := module.(type) {
+			case Dependency:
+				srcs := dep.(SrcDependency).CompiledSrcs()
+				whitelistPathPrefixes := make(map[string]bool)
+				j.genWhitelistPathPrefixes(whitelistPathPrefixes)
+				for _, src := range srcs {
+					if _, ok := src.(android.WritablePath); ok { // generated sources
+						deps.srcs = append(deps.srcs, src)
+					} else { // select source path for documentation based on whitelist path prefixs.
+						for k, _ := range whitelistPathPrefixes {
+							if strings.HasPrefix(src.Rel(), k) {
+								deps.srcs = append(deps.srcs, src)
+								break
+							}
+						}
+					}
+				}
+				deps.srcJars = append(deps.srcJars, dep.(SrcDependency).CompiledSrcJars()...)
+			default:
+				ctx.ModuleErrorf("depends on non-java module %q", otherName)
+			}
+		case systemModulesTag:
+			if deps.systemModules != nil {
+				panic("Found two system module dependencies")
+			}
+			sm := module.(*SystemModules)
+			if sm.outputFile == nil {
+				panic("Missing directory for system module dependency")
+			}
+			deps.systemModules = sm.outputFile
 		}
 	})
 	// do not pass exclude_srcs directly when expanding srcFiles since exclude_srcs
 	// may contain filegroup or genrule.
 	srcFiles := ctx.ExpandSources(j.properties.Srcs, j.properties.Exclude_srcs)
+	flags := j.collectBuilderFlags(ctx, deps)
+	srcFiles = j.genSources(ctx, srcFiles, flags)
 
 	// srcs may depend on some genrule output.
 	j.srcJars = srcFiles.FilterByExt(".srcjar")
@@ -377,19 +561,15 @@
 	implicits = append(implicits, deps.classpath...)
 
 	var bootClasspathArgs, classpathArgs string
-	if ctx.Config().UseOpenJDK9() {
-		if len(deps.bootClasspath) > 0 {
-			// For OpenJDK 9 we use --patch-module to define the core libraries code.
-			// TODO(tobiast): Reorganize this when adding proper support for OpenJDK 9
-			// modules. Here we treat all code in core libraries as being in java.base
-			// to work around the OpenJDK 9 module system. http://b/62049770
-			bootClasspathArgs = "--patch-module=java.base=" + strings.Join(deps.bootClasspath.Strings(), ":")
+
+	javaVersion := getJavaVersion(ctx, String(j.properties.Java_version), sdkContext(j))
+	if len(deps.bootClasspath) > 0 {
+		var systemModules classpath
+		if deps.systemModules != nil {
+			systemModules = append(systemModules, deps.systemModules)
 		}
-	} else {
-		if len(deps.bootClasspath.Strings()) > 0 {
-			// For OpenJDK 8 we can use -bootclasspath to define the core libraries code.
-			bootClasspathArgs = deps.bootClasspath.FormJavaClassPath("-bootclasspath")
-		}
+		bootClasspathArgs = systemModules.FormJavaSystemModulesPath("--system ", ctx.Device())
+		bootClasspathArgs = bootClasspathArgs + " --patch-module java.base=."
 	}
 	if len(deps.classpath.Strings()) > 0 {
 		classpathArgs = "-classpath " + strings.Join(deps.classpath.Strings(), ":")
@@ -397,7 +577,7 @@
 
 	implicits = append(implicits, j.srcJars...)
 
-	opts := "-J-Xmx1024m -XDignore.symbol.file -Xdoclint:none"
+	opts := "-source " + javaVersion + " -J-Xmx1024m -XDignore.symbol.file -Xdoclint:none"
 
 	ctx.Build(pctx, android.BuildParams{
 		Rule:           javadoc,
@@ -420,13 +600,36 @@
 	})
 }
 
+func (d *Droiddoc) checkCurrentApi() bool {
+	if String(d.properties.Check_api.Current.Api_file) != "" &&
+		String(d.properties.Check_api.Current.Removed_api_file) != "" {
+		return true
+	} else if String(d.properties.Check_api.Current.Api_file) != "" {
+		panic("check_api.current.removed_api_file: has to be non empty!")
+	} else if String(d.properties.Check_api.Current.Removed_api_file) != "" {
+		panic("check_api.current.api_file: has to be non empty!")
+	}
+
+	return false
+}
+
+func (d *Droiddoc) checkLastReleasedApi() bool {
+	if String(d.properties.Check_api.Last_released.Api_file) != "" &&
+		String(d.properties.Check_api.Last_released.Removed_api_file) != "" {
+		return true
+	} else if String(d.properties.Check_api.Last_released.Api_file) != "" {
+		panic("check_api.last_released.removed_api_file: has to be non empty!")
+	} else if String(d.properties.Check_api.Last_released.Removed_api_file) != "" {
+		panic("check_api.last_released.api_file: has to be non empty!")
+	}
+
+	return false
+}
+
 func (d *Droiddoc) DepsMutator(ctx android.BottomUpMutatorContext) {
 	d.Javadoc.addDeps(ctx)
 
-	if String(d.properties.Custom_template) == "" {
-		// TODO: This is almost always droiddoc-templates-sdk
-		ctx.PropertyErrorf("custom_template", "must specify a template")
-	} else {
+	if String(d.properties.Custom_template) != "" {
 		ctx.AddDependency(ctx.Module(), droiddocTemplateTag, String(d.properties.Custom_template))
 	}
 
@@ -435,6 +638,28 @@
 
 	// knowntags may contain filegroup or genrule.
 	android.ExtractSourcesDeps(ctx, d.properties.Knowntags)
+
+	if String(d.properties.Static_doc_index_redirect) != "" {
+		android.ExtractSourceDeps(ctx, d.properties.Static_doc_index_redirect)
+	}
+
+	if String(d.properties.Static_doc_properties) != "" {
+		android.ExtractSourceDeps(ctx, d.properties.Static_doc_properties)
+	}
+
+	if d.checkCurrentApi() {
+		android.ExtractSourceDeps(ctx, d.properties.Check_api.Current.Api_file)
+		android.ExtractSourceDeps(ctx, d.properties.Check_api.Current.Removed_api_file)
+	}
+
+	if d.checkLastReleasedApi() {
+		android.ExtractSourceDeps(ctx, d.properties.Check_api.Last_released.Api_file)
+		android.ExtractSourceDeps(ctx, d.properties.Check_api.Last_released.Removed_api_file)
+	}
+
+	if String(d.properties.Metalava_previous_api) != "" {
+		android.ExtractSourceDeps(ctx, d.properties.Metalava_previous_api)
+	}
 }
 
 func (d *Droiddoc) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -444,6 +669,22 @@
 	implicits = append(implicits, deps.bootClasspath...)
 	implicits = append(implicits, deps.classpath...)
 
+	var bootClasspathArgs string
+	javaVersion := getJavaVersion(ctx, String(d.Javadoc.properties.Java_version), sdkContext(d))
+	// Doclava has problem with "-source 1.9", so override javaVersion when Doclava
+	// is running with EXPERIMENTAL_USE_OPENJDK9=true. And eventually Doclava will be
+	// replaced by Metalava.
+	if !Bool(d.properties.Metalava_enabled) {
+		javaVersion = "1.8"
+	}
+	// continue to use -bootclasspath even if Metalava under -source 1.9 is enabled
+	// since it doesn't support system modules yet.
+	if len(deps.bootClasspath.Strings()) > 0 {
+		// For OpenJDK 8 we can use -bootclasspath to define the core libraries code.
+		bootClasspathArgs = deps.bootClasspath.FormJavaClassPath("-bootclasspath")
+	}
+	classpathArgs := deps.classpath.FormJavaClassPath("-classpath")
+
 	argFiles := ctx.ExpandSources(d.properties.Arg_files, nil)
 	argFilesMap := map[string]android.Path{}
 
@@ -472,158 +713,378 @@
 	})
 
 	if err != nil {
-		ctx.PropertyErrorf("extra_args", "%s", err.Error())
+		ctx.PropertyErrorf("args", "%s", err.Error())
 		return
 	}
 
-	var bootClasspathArgs, classpathArgs string
-	if len(deps.bootClasspath.Strings()) > 0 {
-		bootClasspathArgs = "-bootclasspath " + strings.Join(deps.bootClasspath.Strings(), ":")
-	}
-	if len(deps.classpath.Strings()) > 0 {
-		classpathArgs = "-classpath " + strings.Join(deps.classpath.Strings(), ":")
-	}
-
-	var templateDir string
-	ctx.VisitDirectDepsWithTag(droiddocTemplateTag, func(m android.Module) {
-		if t, ok := m.(*DroiddocTemplate); ok {
-			implicits = append(implicits, t.deps...)
-			templateDir = t.dir.String()
+	genDocsForMetalava := false
+	var metalavaArgs string
+	if Bool(d.properties.Metalava_enabled) {
+		if strings.Contains(args, "--generate-documentation") {
+			if !strings.Contains(args, "-nodocs") {
+				genDocsForMetalava = true
+			}
+			// TODO(nanzhang): Add a Soong property to handle documentation args.
+			metalavaArgs = strings.Split(args, "--generate-documentation")[0]
 		} else {
-			ctx.PropertyErrorf("custom_template", "module %q is not a droiddoc_template", ctx.OtherModuleName(m))
+			metalavaArgs = args
 		}
-	})
-
-	var htmlDirArgs string
-	if len(d.properties.Html_dirs) > 0 {
-		htmlDir := android.PathForModuleSrc(ctx, d.properties.Html_dirs[0])
-		implicits = append(implicits, ctx.Glob(htmlDir.Join(ctx, "**/*").String(), nil)...)
-		htmlDirArgs = "-htmldir " + htmlDir.String()
 	}
 
-	var htmlDir2Args string
-	if len(d.properties.Html_dirs) > 1 {
-		htmlDir2 := android.PathForModuleSrc(ctx, d.properties.Html_dirs[1])
-		implicits = append(implicits, ctx.Glob(htmlDir2.Join(ctx, "**/*").String(), nil)...)
-		htmlDir2Args = "-htmldir2 " + htmlDir2.String()
+	var templateDir, htmlDirArgs, htmlDir2Args string
+	if !Bool(d.properties.Metalava_enabled) || genDocsForMetalava {
+		if String(d.properties.Custom_template) == "" {
+			// TODO: This is almost always droiddoc-templates-sdk
+			ctx.PropertyErrorf("custom_template", "must specify a template")
+		}
+
+		ctx.VisitDirectDepsWithTag(droiddocTemplateTag, func(m android.Module) {
+			if t, ok := m.(*DroiddocTemplate); ok {
+				implicits = append(implicits, t.deps...)
+				templateDir = t.dir.String()
+			} else {
+				ctx.PropertyErrorf("custom_template", "module %q is not a droiddoc_template", ctx.OtherModuleName(m))
+			}
+		})
+
+		if len(d.properties.Html_dirs) > 0 {
+			htmlDir := android.PathForModuleSrc(ctx, d.properties.Html_dirs[0])
+			implicits = append(implicits, ctx.Glob(htmlDir.Join(ctx, "**/*").String(), nil)...)
+			htmlDirArgs = "-htmldir " + htmlDir.String()
+		}
+
+		if len(d.properties.Html_dirs) > 1 {
+			htmlDir2 := android.PathForModuleSrc(ctx, d.properties.Html_dirs[1])
+			implicits = append(implicits, ctx.Glob(htmlDir2.Join(ctx, "**/*").String(), nil)...)
+			htmlDir2Args = "-htmldir2 " + htmlDir2.String()
+		}
+
+		if len(d.properties.Html_dirs) > 2 {
+			ctx.PropertyErrorf("html_dirs", "Droiddoc only supports up to 2 html dirs")
+		}
+
+		knownTags := ctx.ExpandSources(d.properties.Knowntags, nil)
+		implicits = append(implicits, knownTags...)
+
+		for _, kt := range knownTags {
+			args = args + " -knowntags " + kt.String()
+		}
+
+		for _, hdf := range d.properties.Hdf {
+			args = args + " -hdf " + hdf
+		}
+
+		if String(d.properties.Proofread_file) != "" {
+			proofreadFile := android.PathForModuleOut(ctx, String(d.properties.Proofread_file))
+			args = args + " -proofread " + proofreadFile.String()
+		}
+
+		if String(d.properties.Todo_file) != "" {
+			// tricky part:
+			// we should not compute full path for todo_file through PathForModuleOut().
+			// the non-standard doclet will get the full path relative to "-o".
+			args = args + " -todo " + String(d.properties.Todo_file)
+		}
+
+		if String(d.properties.Resourcesdir) != "" {
+			// TODO: should we add files under resourcesDir to the implicits? It seems that
+			// resourcesDir is one sub dir of htmlDir
+			resourcesDir := android.PathForModuleSrc(ctx, String(d.properties.Resourcesdir))
+			args = args + " -resourcesdir " + resourcesDir.String()
+		}
+
+		if String(d.properties.Resourcesoutdir) != "" {
+			// TODO: it seems -resourceoutdir reference/android/images/ didn't get generated anywhere.
+			args = args + " -resourcesoutdir " + String(d.properties.Resourcesoutdir)
+		}
 	}
 
-	if len(d.properties.Html_dirs) > 2 {
-		ctx.PropertyErrorf("html_dirs", "Droiddoc only supports up to 2 html dirs")
-	}
-
-	knownTags := ctx.ExpandSources(d.properties.Knowntags, nil)
-	implicits = append(implicits, knownTags...)
-
-	for _, kt := range knownTags {
-		args = args + " -knowntags " + kt.String()
-	}
-	for _, hdf := range d.properties.Hdf {
-		args = args + " -hdf " + hdf
-	}
-
-	if String(d.properties.Proofread_file) != "" {
-		proofreadFile := android.PathForModuleOut(ctx, String(d.properties.Proofread_file))
-		args = args + " -proofread " + proofreadFile.String()
-	}
-
-	if String(d.properties.Todo_file) != "" {
-		// tricky part:
-		// we should not compute full path for todo_file through PathForModuleOut().
-		// the non-standard doclet will get the full path relative to "-o".
-		args = args + " -todo " + String(d.properties.Todo_file)
-	}
-
-	if String(d.properties.Resourcesdir) != "" {
-		// TODO: should we add files under resourcesDir to the implicits? It seems that
-		// resourcesDir is one sub dir of htmlDir
-		resourcesDir := android.PathForModuleSrc(ctx, String(d.properties.Resourcesdir))
-		args = args + " -resourcesdir " + resourcesDir.String()
-	}
-
-	if String(d.properties.Resourcesoutdir) != "" {
-		// TODO: it seems -resourceoutdir reference/android/images/ didn't get generated anywhere.
-		args = args + " -resourcesoutdir " + String(d.properties.Resourcesoutdir)
+	var docArgsForMetalava string
+	if Bool(d.properties.Metalava_enabled) && genDocsForMetalava {
+		docArgsForMetalava = strings.Split(args, "--generate-documentation")[1]
 	}
 
 	var implicitOutputs android.WritablePaths
-	if String(d.properties.Api_filename) != "" {
-		d.apiFile = android.PathForModuleOut(ctx, String(d.properties.Api_filename))
+
+	if d.checkCurrentApi() || d.checkLastReleasedApi() || String(d.properties.Api_filename) != "" {
+		d.apiFile = android.PathForModuleOut(ctx, ctx.ModuleName()+"_api.txt")
 		args = args + " -api " + d.apiFile.String()
+		metalavaArgs = metalavaArgs + " --api " + d.apiFile.String()
 		implicitOutputs = append(implicitOutputs, d.apiFile)
 	}
 
+	if d.checkCurrentApi() || d.checkLastReleasedApi() || String(d.properties.Removed_api_filename) != "" {
+		d.removedApiFile = android.PathForModuleOut(ctx, ctx.ModuleName()+"_removed.txt")
+		args = args + " -removedApi " + d.removedApiFile.String()
+		metalavaArgs = metalavaArgs + " --removed-api " + d.removedApiFile.String()
+		implicitOutputs = append(implicitOutputs, d.removedApiFile)
+	}
+
 	if String(d.properties.Private_api_filename) != "" {
 		d.privateApiFile = android.PathForModuleOut(ctx, String(d.properties.Private_api_filename))
 		args = args + " -privateApi " + d.privateApiFile.String()
+		metalavaArgs = metalavaArgs + " --private-api " + d.privateApiFile.String()
 		implicitOutputs = append(implicitOutputs, d.privateApiFile)
 	}
 
+	if String(d.properties.Dex_api_filename) != "" {
+		d.dexApiFile = android.PathForModuleOut(ctx, String(d.properties.Dex_api_filename))
+		args = args + " -dexApi " + d.dexApiFile.String()
+		implicitOutputs = append(implicitOutputs, d.dexApiFile)
+	}
+
 	if String(d.properties.Private_dex_api_filename) != "" {
 		d.privateDexApiFile = android.PathForModuleOut(ctx, String(d.properties.Private_dex_api_filename))
 		args = args + " -privateDexApi " + d.privateDexApiFile.String()
+		metalavaArgs = metalavaArgs + " --private-dex-api " + d.privateDexApiFile.String()
 		implicitOutputs = append(implicitOutputs, d.privateDexApiFile)
 	}
 
-	if String(d.properties.Removed_api_filename) != "" {
-		d.removedApiFile = android.PathForModuleOut(ctx, String(d.properties.Removed_api_filename))
-		args = args + " -removedApi " + d.removedApiFile.String()
-		implicitOutputs = append(implicitOutputs, d.removedApiFile)
-	}
-
 	if String(d.properties.Removed_dex_api_filename) != "" {
 		d.removedDexApiFile = android.PathForModuleOut(ctx, String(d.properties.Removed_dex_api_filename))
 		args = args + " -removedDexApi " + d.removedDexApiFile.String()
+		metalavaArgs = metalavaArgs + " --removed-dex-api " + d.removedDexApiFile.String()
 		implicitOutputs = append(implicitOutputs, d.removedDexApiFile)
 	}
 
 	if String(d.properties.Exact_api_filename) != "" {
 		d.exactApiFile = android.PathForModuleOut(ctx, String(d.properties.Exact_api_filename))
 		args = args + " -exactApi " + d.exactApiFile.String()
+		metalavaArgs = metalavaArgs + " --exact-api " + d.exactApiFile.String()
 		implicitOutputs = append(implicitOutputs, d.exactApiFile)
 	}
 
-	implicits = append(implicits, d.Javadoc.srcJars...)
-
-	jsilver := android.PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "framework", "jsilver.jar")
-	doclava := android.PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "framework", "doclava.jar")
-	implicits = append(implicits, jsilver)
-	implicits = append(implicits, doclava)
-
-	opts := "-source 1.8 -J-Xmx1600m -J-XX:-OmitStackTraceInFastThrow -XDignore.symbol.file " +
-		"-doclet com.google.doclava.Doclava -docletpath " + jsilver.String() + ":" + doclava.String() + " " +
-		"-templatedir " + templateDir + " " + htmlDirArgs + " " + htmlDir2Args + " " +
-		"-hdf page.build " + ctx.Config().BuildId() + "-" + ctx.Config().BuildNumberFromFile() + " " +
-		"-hdf page.now " + `"$$(date -d @$$(cat ` + ctx.Config().Getenv("BUILD_DATETIME_FILE") + `) "+%d %b %Y %k:%M")"` +
-		" " + args
-	if BoolDefault(d.properties.Create_stubs, true) {
-		opts += " -stubs " + android.PathForModuleOut(ctx, "docs", "stubsDir").String()
+	if String(d.properties.Dex_mapping_filename) != "" {
+		d.apiMappingFile = android.PathForModuleOut(ctx, String(d.properties.Dex_mapping_filename))
+		args = args + " -apiMapping " + d.apiMappingFile.String()
+		// Omitted: metalava support
+		implicitOutputs = append(implicitOutputs, d.apiMappingFile)
 	}
 
+	implicits = append(implicits, d.Javadoc.srcJars...)
+
 	implicitOutputs = append(implicitOutputs, d.Javadoc.docZip)
 	for _, o := range d.properties.Out {
 		implicitOutputs = append(implicitOutputs, android.PathForModuleGen(ctx, o))
 	}
 
-	ctx.Build(pctx, android.BuildParams{
-		Rule:            javadoc,
-		Description:     "Droiddoc",
-		Output:          d.Javadoc.stubsSrcJar,
-		Inputs:          d.Javadoc.srcFiles,
-		Implicits:       implicits,
-		ImplicitOutputs: implicitOutputs,
-		Args: map[string]string{
+	jsilver := android.PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "framework", "jsilver.jar")
+	doclava := android.PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "framework", "doclava.jar")
+
+	var date string
+	if runtime.GOOS == "darwin" {
+		date = `date -r`
+	} else {
+		date = `date -d`
+	}
+
+	doclavaOpts := "-source " + javaVersion + " -J-Xmx1600m -J-XX:-OmitStackTraceInFastThrow -XDignore.symbol.file " +
+		"-doclet com.google.doclava.Doclava -docletpath " + jsilver.String() + ":" + doclava.String() + " " +
+		"-templatedir " + templateDir + " " + htmlDirArgs + " " + htmlDir2Args + " " +
+		"-hdf page.build " + ctx.Config().BuildId() + "-" + ctx.Config().BuildNumberFromFile() + " " +
+		`-hdf page.now "$$(` + date + ` @$$(cat ` + ctx.Config().Getenv("BUILD_DATETIME_FILE") + `) "+%d %b %Y %k:%M")" `
+
+	if !Bool(d.properties.Metalava_enabled) {
+		opts := doclavaOpts + args
+
+		implicits = append(implicits, jsilver)
+		implicits = append(implicits, doclava)
+
+		if BoolDefault(d.properties.Create_stubs, true) {
+			opts += " -stubs " + android.PathForModuleOut(ctx, "docs", "stubsDir").String()
+		}
+
+		if Bool(d.properties.Write_sdk_values) {
+			opts += " -sdkvalues " + android.PathForModuleOut(ctx, "docs").String()
+		}
+
+		var postDoclavaCmds string
+		if String(d.properties.Static_doc_index_redirect) != "" {
+			static_doc_index_redirect := ctx.ExpandSource(String(d.properties.Static_doc_index_redirect),
+				"static_doc_index_redirect")
+			implicits = append(implicits, static_doc_index_redirect)
+			postDoclavaCmds += " && cp " + static_doc_index_redirect.String() + " " +
+				android.PathForModuleOut(ctx, "docs", "out", "index.html").String()
+		}
+
+		if String(d.properties.Static_doc_properties) != "" {
+			static_doc_properties := ctx.ExpandSource(String(d.properties.Static_doc_properties),
+				"static_doc_properties")
+			implicits = append(implicits, static_doc_properties)
+			postDoclavaCmds += " && cp " + static_doc_properties.String() + " " +
+				android.PathForModuleOut(ctx, "docs", "out", "source.properties").String()
+		}
+
+		ctx.Build(pctx, android.BuildParams{
+			Rule:            javadoc,
+			Description:     "Droiddoc",
+			Output:          d.Javadoc.stubsSrcJar,
+			Inputs:          d.Javadoc.srcFiles,
+			Implicits:       implicits,
+			ImplicitOutputs: implicitOutputs,
+			Args: map[string]string{
+				"outDir":            android.PathForModuleOut(ctx, "docs", "out").String(),
+				"srcJarDir":         android.PathForModuleOut(ctx, "docs", "srcjars").String(),
+				"stubsDir":          android.PathForModuleOut(ctx, "docs", "stubsDir").String(),
+				"srcJars":           strings.Join(d.Javadoc.srcJars.Strings(), " "),
+				"opts":              opts,
+				"bootclasspathArgs": bootClasspathArgs,
+				"classpathArgs":     classpathArgs,
+				"sourcepath":        strings.Join(d.Javadoc.sourcepaths.Strings(), ":"),
+				"docZip":            d.Javadoc.docZip.String(),
+				"postDoclavaCmds":   postDoclavaCmds,
+			},
+		})
+	} else {
+		opts := metalavaArgs
+
+		buildArgs := map[string]string{
 			"outDir":            android.PathForModuleOut(ctx, "docs", "out").String(),
 			"srcJarDir":         android.PathForModuleOut(ctx, "docs", "srcjars").String(),
 			"stubsDir":          android.PathForModuleOut(ctx, "docs", "stubsDir").String(),
 			"srcJars":           strings.Join(d.Javadoc.srcJars.Strings(), " "),
-			"opts":              opts,
+			"javaVersion":       javaVersion,
 			"bootclasspathArgs": bootClasspathArgs,
 			"classpathArgs":     classpathArgs,
 			"sourcepath":        strings.Join(d.Javadoc.sourcepaths.Strings(), ":"),
 			"docZip":            d.Javadoc.docZip.String(),
-		},
-	})
+		}
+
+		var previousApi android.Path
+		if String(d.properties.Metalava_previous_api) != "" {
+			previousApi = ctx.ExpandSource(String(d.properties.Metalava_previous_api),
+				"metalava_previous_api")
+			opts += " --previous-api " + previousApi.String()
+			implicits = append(implicits, previousApi)
+		}
+
+		if Bool(d.properties.Metalava_annotations_enabled) {
+			if String(d.properties.Metalava_previous_api) == "" {
+				ctx.PropertyErrorf("metalava_previous_api",
+					"has to be non-empty if annotations was enabled!")
+			}
+			opts += " --include-annotations --migrate-nullness"
+
+			annotationsZip := android.PathForModuleOut(ctx, ctx.ModuleName()+"_annotations.zip")
+			implicitOutputs = append(implicitOutputs, annotationsZip)
+
+			if String(d.properties.Metalava_merge_annotations_dir) == "" {
+				ctx.PropertyErrorf("metalava_merge_annotations",
+					"has to be non-empty if annotations was enabled!")
+			}
+
+			mergeAnnotationsDir := android.PathForSource(ctx, String(d.properties.Metalava_merge_annotations_dir))
+
+			opts += " --extract-annotations " + annotationsZip.String() + " --merge-annotations " + mergeAnnotationsDir.String()
+			// TODO(tnorbye): find owners to fix these warnings when annotation was enabled.
+			opts += " --hide HiddenTypedefConstant --hide SuperfluousPrefix --hide AnnotationExtraction"
+		}
+
+		if genDocsForMetalava {
+			opts += " --doc-stubs " + android.PathForModuleOut(ctx, "docs", "docStubsDir").String() +
+				" --write-doc-stubs-source-list $outDir/doc_stubs_src_list " +
+				" --generate-documentation ${config.JavadocCmd} -encoding UTF-8 DOC_STUBS_SOURCE_LIST " +
+				doclavaOpts + docArgsForMetalava + bootClasspathArgs + " " + classpathArgs + " " + " -sourcepath " +
+				android.PathForModuleOut(ctx, "docs", "docStubsDir").String() + " -quiet -d $outDir "
+			implicits = append(implicits, jsilver)
+			implicits = append(implicits, doclava)
+		}
+
+		buildArgs["opts"] = opts
+		ctx.Build(pctx, android.BuildParams{
+			Rule:            metalava,
+			Description:     "Metalava",
+			Output:          d.Javadoc.stubsSrcJar,
+			Inputs:          d.Javadoc.srcFiles,
+			Implicits:       implicits,
+			ImplicitOutputs: implicitOutputs,
+			Args:            buildArgs,
+		})
+	}
+
+	java8Home := ctx.Config().Getenv("ANDROID_JAVA8_HOME")
+
+	checkApiClasspath := classpath{jsilver, doclava, android.PathForSource(ctx, java8Home, "lib/tools.jar")}
+
+	if d.checkCurrentApi() && !ctx.Config().IsPdkBuild() {
+		d.checkCurrentApiTimestamp = android.PathForModuleOut(ctx, "check_current_api.timestamp")
+
+		apiFile := ctx.ExpandSource(String(d.properties.Check_api.Current.Api_file),
+			"check_api.current.api_file")
+		removedApiFile := ctx.ExpandSource(String(d.properties.Check_api.Current.Removed_api_file),
+			"check_api.current_removed_api_file")
+
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        apiCheck,
+			Description: "Current API check",
+			Output:      d.checkCurrentApiTimestamp,
+			Inputs:      nil,
+			Implicits: append(android.Paths{apiFile, removedApiFile, d.apiFile, d.removedApiFile},
+				checkApiClasspath...),
+			Args: map[string]string{
+				"classpath":             checkApiClasspath.FormJavaClassPath(""),
+				"opts":                  String(d.properties.Check_api.Current.Args),
+				"apiFile":               apiFile.String(),
+				"apiFileToCheck":        d.apiFile.String(),
+				"removedApiFile":        removedApiFile.String(),
+				"removedApiFileToCheck": d.removedApiFile.String(),
+				"msg": fmt.Sprintf(`\n******************************\n`+
+					`You have tried to change the API from what has been previously approved.\n\n`+
+					`To make these errors go away, you have two choices:\n`+
+					`   1. You can add '@hide' javadoc comments to the methods, etc. listed in the\n`+
+					`      errors above.\n\n`+
+					`   2. You can update current.txt by executing the following command:\n`+
+					`         make %s-update-current-api\n\n`+
+					`      To submit the revised current.txt to the main Android repository,\n`+
+					`      you will need approval.\n`+
+					`******************************\n`, ctx.ModuleName()),
+			},
+		})
+
+		d.updateCurrentApiTimestamp = android.PathForModuleOut(ctx, "update_current_api.timestamp")
+
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        updateApi,
+			Description: "update current API",
+			Output:      d.updateCurrentApiTimestamp,
+			Implicits:   append(android.Paths{}, apiFile, removedApiFile, d.apiFile, d.removedApiFile),
+			Args: map[string]string{
+				"apiFile":               apiFile.String(),
+				"apiFileToCheck":        d.apiFile.String(),
+				"removedApiFile":        removedApiFile.String(),
+				"removedApiFileToCheck": d.removedApiFile.String(),
+			},
+		})
+	}
+	if d.checkLastReleasedApi() && !ctx.Config().IsPdkBuild() {
+		d.checkLastReleasedApiTimestamp = android.PathForModuleOut(ctx, "check_last_released_api.timestamp")
+
+		apiFile := ctx.ExpandSource(String(d.properties.Check_api.Last_released.Api_file),
+			"check_api.last_released.api_file")
+		removedApiFile := ctx.ExpandSource(String(d.properties.Check_api.Last_released.Removed_api_file),
+			"check_api.last_released.removed_api_file")
+
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        apiCheck,
+			Description: "Last Released API check",
+			Output:      d.checkLastReleasedApiTimestamp,
+			Inputs:      nil,
+			Implicits: append(android.Paths{apiFile, removedApiFile, d.apiFile, d.removedApiFile},
+				checkApiClasspath...),
+			Args: map[string]string{
+				"classpath":             checkApiClasspath.FormJavaClassPath(""),
+				"opts":                  String(d.properties.Check_api.Last_released.Args),
+				"apiFile":               apiFile.String(),
+				"apiFileToCheck":        d.apiFile.String(),
+				"removedApiFile":        removedApiFile.String(),
+				"removedApiFileToCheck": d.removedApiFile.String(),
+				"msg": `\n******************************\n` +
+					`You have tried to change the API from what has been previously released in\n` +
+					`an SDK.  Please fix the errors listed above.\n` +
+					`******************************\n`,
+			},
+		})
+	}
 }
 
 var droiddocTemplateTag = dependencyTag{name: "droiddoc-template"}
diff --git a/java/java.go b/java/java.go
index 8c23124..06d3564 100644
--- a/java/java.go
+++ b/java/java.go
@@ -34,8 +34,8 @@
 func init() {
 	android.RegisterModuleType("java_defaults", defaultsFactory)
 
-	android.RegisterModuleType("java_library", LibraryFactory(true))
-	android.RegisterModuleType("java_library_static", LibraryFactory(false))
+	android.RegisterModuleType("java_library", LibraryFactory)
+	android.RegisterModuleType("java_library_static", LibraryFactory)
 	android.RegisterModuleType("java_library_host", LibraryHostFactory)
 	android.RegisterModuleType("java_binary", BinaryFactory)
 	android.RegisterModuleType("java_binary_host", BinaryHostFactory)
@@ -107,7 +107,8 @@
 	// If not blank, set the java version passed to javac as -source and -target
 	Java_version *string
 
-	// If set to false, don't allow this module to be installed.  Defaults to true.
+	// If set to true, allow this module to be dexed and installed on devices.  Has no
+	// effect on host modules, which are always considered installable.
 	Installable *bool
 
 	// If set to true, include sources used to compile the module in to the final jar
@@ -168,9 +169,17 @@
 	// list of module-specific flags that will be used for dex compiles
 	Dxflags []string `android:"arch_variant"`
 
-	// if not blank, set to the version of the sdk to compile against
+	// if not blank, set to the version of the sdk to compile against.  Defaults to compiling against the current
+	// sdk if platform_apis is not set.
 	Sdk_version *string
 
+	// if not blank, set the minimum version of the sdk that the compiled artifacts will run against.
+	// Defaults to sdk_version if not set.
+	Min_sdk_version *string
+
+	// if true, compile against the platform APIs instead of an SDK.
+	Platform_apis *bool
+
 	Aidl struct {
 		// Top level directories to pass to aidl tool
 		Include_dirs []string
@@ -184,11 +193,17 @@
 
 		// whether to generate traces (for systrace) for this interface
 		Generate_traces *bool
+
+		// whether to generate Binder#GetTransaction name method.
+		Generate_get_transaction_name *bool
 	}
 
 	// If true, export a copy of the module as a -hostdex module for host testing.
 	Hostdex *bool
 
+	// If set to true, compile dex regardless of installable.  Defaults to false.
+	Compile_dex *bool
+
 	Dex_preopt struct {
 		// If false, prevent dexpreopting and stripping the dex file from the final jar.  Defaults to
 		// true.
@@ -209,8 +224,8 @@
 	}
 
 	Optimize struct {
-		// If false, disable all optimization.  Defaults to true for apps, false for
-		// libraries and tests.
+		// If false, disable all optimization.  Defaults to true for android_app and android_test
+		// modules, false for java_library and java_test modules.
 		Enabled *bool
 
 		// If true, optimize for size by removing unused code.  Defaults to true for apps,
@@ -278,6 +293,9 @@
 
 	// list of extra progurad flag files
 	extraProguardFlagFiles android.Paths
+
+	// list of SDK lib names that this java moudule is exporting
+	exportedSdkLibs []string
 }
 
 func (j *Module) Srcs() android.Paths {
@@ -290,6 +308,7 @@
 	HeaderJars() android.Paths
 	ImplementationJars() android.Paths
 	AidlIncludeDirs() android.Paths
+	ExportedSdkLibs() []string
 }
 
 type SdkLibraryDependency interface {
@@ -324,6 +343,7 @@
 var (
 	staticLibTag     = dependencyTag{name: "staticlib"}
 	libTag           = dependencyTag{name: "javalib"}
+	annoTag          = dependencyTag{name: "annotation processor"}
 	bootClasspathTag = dependencyTag{name: "bootclasspath"}
 	systemModulesTag = dependencyTag{name: "system modules"}
 	frameworkResTag  = dependencyTag{name: "framework-res"}
@@ -335,29 +355,15 @@
 type sdkDep struct {
 	useModule, useFiles, useDefaultLibs, invalidVersion bool
 
-	module        string
+	modules       []string
 	systemModules string
 
 	frameworkResModule string
 
-	jar  android.Path
+	jars android.Paths
 	aidl android.Path
 }
 
-func sdkStringToNumber(ctx android.BaseContext, v string) int {
-	switch v {
-	case "", "current", "system_current", "test_current", "core_current":
-		return android.FutureApiLevel
-	default:
-		if i, err := strconv.Atoi(android.GetNumericSdkVersion(v)); err != nil {
-			ctx.PropertyErrorf("sdk_version", "invalid sdk version")
-			return -1
-		} else {
-			return i
-		}
-	}
-}
-
 func (j *Module) shouldInstrument(ctx android.BaseContext) bool {
 	return j.properties.Instrument && ctx.Config().IsEnvTrue("EMMA_INSTRUMENT")
 }
@@ -368,10 +374,62 @@
 			ctx.Config().UnbundledBuild())
 }
 
-func decodeSdkDep(ctx android.BaseContext, v string) sdkDep {
-	i := sdkStringToNumber(ctx, v)
-	if i == -1 {
-		// Invalid sdk version, error handled by sdkStringToNumber.
+func (j *Module) sdkVersion() string {
+	return String(j.deviceProperties.Sdk_version)
+}
+
+func (j *Module) minSdkVersion() string {
+	if j.deviceProperties.Min_sdk_version != nil {
+		return *j.deviceProperties.Min_sdk_version
+	}
+	return j.sdkVersion()
+}
+
+type sdkContext interface {
+	// sdkVersion eturns the sdk_version property of the current module, or an empty string if it is not set.
+	sdkVersion() string
+	// minSdkVersion returns the min_sdk_version property of the current module, or sdkVersion() if it is not set.
+	minSdkVersion() string
+}
+
+func sdkVersionOrDefault(ctx android.BaseContext, v string) string {
+	switch v {
+	case "", "current", "system_current", "test_current", "core_current":
+		return ctx.Config().DefaultAppTargetSdk()
+	default:
+		return v
+	}
+}
+
+// Returns a sdk version as a number.  For modules targeting an unreleased SDK (meaning it does not yet have a number)
+// it returns android.FutureApiLevel (10000).
+func sdkVersionToNumber(ctx android.BaseContext, v string) (int, error) {
+	switch v {
+	case "", "current", "test_current", "system_current", "core_current":
+		return ctx.Config().DefaultAppTargetSdkInt(), nil
+	default:
+		n := android.GetNumericSdkVersion(v)
+		if i, err := strconv.Atoi(n); err != nil {
+			return -1, fmt.Errorf("invalid sdk version %q", n)
+		} else {
+			return i, nil
+		}
+	}
+}
+
+func sdkVersionToNumberAsString(ctx android.BaseContext, v string) (string, error) {
+	n, err := sdkVersionToNumber(ctx, v)
+	if err != nil {
+		return "", err
+	}
+	return strconv.Itoa(n), nil
+}
+
+func decodeSdkDep(ctx android.BaseContext, sdkContext sdkContext) sdkDep {
+	v := sdkContext.sdkVersion()
+	i, err := sdkVersionToNumber(ctx, v)
+	if err != nil {
+		ctx.PropertyErrorf("sdk_version", "%s", err)
 		return sdkDep{}
 	}
 
@@ -409,11 +467,12 @@
 		aidl := filepath.Join(public_dir, "framework.aidl")
 		jarPath := android.ExistentPathForSource(ctx, jar)
 		aidlPath := android.ExistentPathForSource(ctx, aidl)
+		lambdaStubsPath := android.PathForSource(ctx, config.SdkLambdaStubsPath)
 
 		if (!jarPath.Valid() || !aidlPath.Valid()) && ctx.Config().AllowMissingDependencies() {
 			return sdkDep{
 				invalidVersion: true,
-				module:         fmt.Sprintf("sdk_%s_%s_android", api, v),
+				modules:        []string{fmt.Sprintf("sdk_%s_%s_android", api, v)},
 			}
 		}
 
@@ -429,7 +488,7 @@
 
 		return sdkDep{
 			useFiles: true,
-			jar:      jarPath.Path(),
+			jars:     android.Paths{jarPath.Path(), lambdaStubsPath},
 			aidl:     aidlPath.Path(),
 		}
 	}
@@ -437,7 +496,7 @@
 	toModule := func(m, r string) sdkDep {
 		ret := sdkDep{
 			useModule:          true,
-			module:             m,
+			modules:            []string{m, config.DefaultLambdaStubsLibrary},
 			systemModules:      m + "_system_modules",
 			frameworkResModule: r,
 		}
@@ -473,7 +532,7 @@
 func (j *Module) deps(ctx android.BottomUpMutatorContext) {
 	if ctx.Device() {
 		if !Bool(j.properties.No_standard_libs) {
-			sdkDep := decodeSdkDep(ctx, String(j.deviceProperties.Sdk_version))
+			sdkDep := decodeSdkDep(ctx, sdkContext(j))
 			if sdkDep.useDefaultLibs {
 				ctx.AddDependency(ctx.Module(), bootClasspathTag, config.DefaultBootclasspathLibraries...)
 				if ctx.Config().TargetOpenJDK9() {
@@ -486,7 +545,7 @@
 				if ctx.Config().TargetOpenJDK9() {
 					ctx.AddDependency(ctx.Module(), systemModulesTag, sdkDep.systemModules)
 				}
-				ctx.AddDependency(ctx.Module(), bootClasspathTag, sdkDep.module)
+				ctx.AddDependency(ctx.Module(), bootClasspathTag, sdkDep.modules...)
 				if Bool(j.deviceProperties.Optimize.Enabled) {
 					ctx.AddDependency(ctx.Module(), proguardRaiseTag, config.DefaultBootclasspathLibraries...)
 					ctx.AddDependency(ctx.Module(), proguardRaiseTag, config.DefaultLibraries...)
@@ -503,15 +562,19 @@
 		}
 		if ctx.ModuleName() == "android_stubs_current" ||
 			ctx.ModuleName() == "android_system_stubs_current" ||
-			ctx.ModuleName() == "android_test_stubs_current" {
+			ctx.ModuleName() == "android_test_stubs_current" ||
+			ctx.ModuleName() == "metalava_android_stubs_current" ||
+			ctx.ModuleName() == "metalava_android_system_stubs_current" ||
+			ctx.ModuleName() == "metalava_android_test_stubs_current" {
 			ctx.AddDependency(ctx.Module(), frameworkApkTag, "framework-res")
 		}
 	}
 
 	ctx.AddDependency(ctx.Module(), libTag, j.properties.Libs...)
 	ctx.AddDependency(ctx.Module(), staticLibTag, j.properties.Static_libs...)
-	ctx.AddDependency(ctx.Module(), libTag, j.properties.Annotation_processors...)
-
+	ctx.AddFarVariationDependencies([]blueprint.Variation{
+		{"arch", ctx.Config().BuildOsCommonVariant},
+	}, annoTag, j.properties.Annotation_processors...)
 	android.ExtractSourcesDeps(ctx, j.properties.Srcs)
 	android.ExtractSourcesDeps(ctx, j.properties.Exclude_srcs)
 	android.ExtractSourcesDeps(ctx, j.properties.Java_resources)
@@ -585,12 +648,17 @@
 		flags = append(flags, "-t")
 	}
 
+	if Bool(j.deviceProperties.Aidl.Generate_get_transaction_name) {
+		flags = append(flags, "--transaction_names")
+	}
+
 	return flags
 }
 
 type deps struct {
 	classpath          classpath
 	bootClasspath      classpath
+	processorPath      classpath
 	staticJars         android.Paths
 	staticHeaderJars   android.Paths
 	staticJarResources android.Paths
@@ -621,15 +689,16 @@
 )
 
 func getLinkType(m *Module, name string) linkType {
-	ver := String(m.deviceProperties.Sdk_version)
+	ver := m.sdkVersion()
+	noStdLibs := Bool(m.properties.No_standard_libs)
 	switch {
-	case name == "core.current.stubs" || ver == "core_current":
+	case name == "core.current.stubs" || ver == "core_current" || noStdLibs || name == "stub-annotations":
 		return javaCore
-	case name == "android_system_stubs_current" || strings.HasPrefix(ver, "system_"):
+	case name == "android_system_stubs_current" || strings.HasPrefix(ver, "system_") || name == "metalava_android_system_stubs_current":
 		return javaSystem
-	case name == "android_test_stubs_current" || strings.HasPrefix(ver, "test_"):
+	case name == "android_test_stubs_current" || strings.HasPrefix(ver, "test_") || name == "metalava_android_test_stubs_current":
 		return javaPlatform
-	case name == "android_stubs_current" || ver == "current":
+	case name == "android_stubs_current" || ver == "current" || name == "metalava_android_stubs_current":
 		return javaSdk
 	case ver == "":
 		return javaPlatform
@@ -679,12 +748,12 @@
 	var deps deps
 
 	if ctx.Device() {
-		sdkDep := decodeSdkDep(ctx, String(j.deviceProperties.Sdk_version))
+		sdkDep := decodeSdkDep(ctx, sdkContext(j))
 		if sdkDep.invalidVersion {
-			ctx.AddMissingDependencies([]string{sdkDep.module})
+			ctx.AddMissingDependencies(sdkDep.modules)
 		} else if sdkDep.useFiles {
 			// sdkDep.jar is actually equivalent to turbine header.jar.
-			deps.classpath = append(deps.classpath, sdkDep.jar)
+			deps.classpath = append(deps.classpath, sdkDep.jars...)
 			deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, sdkDep.aidl)
 		}
 	}
@@ -706,10 +775,16 @@
 				deps.bootClasspath = append(deps.bootClasspath, dep.HeaderJars()...)
 			case libTag:
 				deps.classpath = append(deps.classpath, dep.HeaderJars()...)
+				// sdk lib names from dependencies are re-exported
+				j.exportedSdkLibs = append(j.exportedSdkLibs, dep.ExportedSdkLibs()...)
 			case staticLibTag:
 				deps.classpath = append(deps.classpath, dep.HeaderJars()...)
 				deps.staticJars = append(deps.staticJars, dep.ImplementationJars()...)
 				deps.staticHeaderJars = append(deps.staticHeaderJars, dep.HeaderJars()...)
+				// sdk lib names from dependencies are re-exported
+				j.exportedSdkLibs = append(j.exportedSdkLibs, dep.ExportedSdkLibs()...)
+			case annoTag:
+				deps.processorPath = append(deps.processorPath, dep.ImplementationJars()...)
 			case frameworkResTag:
 				if ctx.ModuleName() == "framework" {
 					// framework.jar has a one-off dependency on the R.java and Manifest.java files
@@ -719,7 +794,10 @@
 			case frameworkApkTag:
 				if ctx.ModuleName() == "android_stubs_current" ||
 					ctx.ModuleName() == "android_system_stubs_current" ||
-					ctx.ModuleName() == "android_test_stubs_current" {
+					ctx.ModuleName() == "android_test_stubs_current" ||
+					ctx.ModuleName() == "metalava_android_stubs_current" ||
+					ctx.ModuleName() == "metalava_android_system_stubs_current" ||
+					ctx.ModuleName() == "metalava_android_test_stubs_current" {
 					// framework stubs.jar need to depend on framework-res.apk, in order to pull the
 					// resource files out of there for aapt.
 					//
@@ -737,6 +815,8 @@
 			switch tag {
 			case libTag:
 				deps.classpath = append(deps.classpath, dep.HeaderJars(getLinkType(j, ctx.ModuleName()))...)
+				// names of sdk libs that are directly depended are exported
+				j.exportedSdkLibs = append(j.exportedSdkLibs, otherName)
 			default:
 				ctx.ModuleErrorf("dependency on java_sdk_library %q can only be in libs", otherName)
 			}
@@ -774,9 +854,33 @@
 		}
 	})
 
+	j.exportedSdkLibs = android.FirstUniqueStrings(j.exportedSdkLibs)
+
 	return deps
 }
 
+func getJavaVersion(ctx android.ModuleContext, javaVersion string, sdkContext sdkContext) string {
+	var ret string
+	sdk, err := sdkVersionToNumber(ctx, sdkContext.sdkVersion())
+	if err != nil {
+		ctx.PropertyErrorf("sdk_version", "%s", err)
+	}
+	if javaVersion != "" {
+		ret = javaVersion
+	} else if ctx.Device() && sdk <= 23 {
+		ret = "1.7"
+	} else if ctx.Device() && sdk <= 26 || !ctx.Config().TargetOpenJDK9() {
+		ret = "1.8"
+	} else if ctx.Device() && sdkContext.sdkVersion() != "" && sdk == android.FutureApiLevel {
+		// TODO(ccross): once we generate stubs we should be able to use 1.9 for sdk_version: "current"
+		ret = "1.8"
+	} else {
+		ret = "1.9"
+	}
+
+	return ret
+}
+
 func (j *Module) collectBuilderFlags(ctx android.ModuleContext, deps deps) javaBuilderFlags {
 
 	var flags javaBuilderFlags
@@ -797,28 +901,29 @@
 		flags.javacFlags = "$javacFlags"
 	}
 
-	if len(j.properties.Errorprone.Javacflags) > 0 {
-		flags.errorProneExtraJavacFlags = strings.Join(j.properties.Errorprone.Javacflags, " ")
+	if ctx.Config().RunErrorProne() {
+		if config.ErrorProneClasspath == nil {
+			ctx.ModuleErrorf("cannot build with Error Prone, missing external/error_prone?")
+		}
+
+		errorProneFlags := []string{
+			"-Xplugin:ErrorProne",
+			"${config.ErrorProneChecks}",
+		}
+		errorProneFlags = append(errorProneFlags, j.properties.Errorprone.Javacflags...)
+
+		flags.errorProneExtraJavacFlags = "${config.ErrorProneFlags} " +
+			"'" + strings.Join(errorProneFlags, " ") + "'"
+		flags.errorProneProcessorPath = classpath(android.PathsForSource(ctx, config.ErrorProneClasspath))
 	}
 
 	// javaVersion flag.
-	sdk := sdkStringToNumber(ctx, String(j.deviceProperties.Sdk_version))
-	if j.properties.Java_version != nil {
-		flags.javaVersion = *j.properties.Java_version
-	} else if ctx.Device() && sdk <= 23 {
-		flags.javaVersion = "1.7"
-	} else if ctx.Device() && sdk <= 26 || !ctx.Config().TargetOpenJDK9() {
-		flags.javaVersion = "1.8"
-	} else if ctx.Device() && String(j.deviceProperties.Sdk_version) != "" && sdk == android.FutureApiLevel {
-		// TODO(ccross): once we generate stubs we should be able to use 1.9 for sdk_version: "current"
-		flags.javaVersion = "1.8"
-	} else {
-		flags.javaVersion = "1.9"
-	}
+	flags.javaVersion = getJavaVersion(ctx, String(j.properties.Java_version), sdkContext(j))
 
 	// classpath
 	flags.bootClasspath = append(flags.bootClasspath, deps.bootClasspath...)
 	flags.classpath = append(flags.classpath, deps.classpath...)
+	flags.processorPath = append(flags.processorPath, deps.processorPath...)
 
 	if len(flags.bootClasspath) == 0 && ctx.Host() && !ctx.Config().TargetOpenJDK9() &&
 		!Bool(j.properties.No_standard_libs) &&
@@ -897,6 +1002,8 @@
 		}
 	}
 
+	var stripFiles []string
+
 	if srcFiles.HasExt(".kt") {
 		// If there are kotlin files, compile them first but pass all the kotlin and java files
 		// kotlinc will use the java files to resolve types referenced by the kotlin files, but
@@ -923,14 +1030,21 @@
 		}
 
 		// Make javac rule depend on the kotlinc rule
+		flags.classpath = append(flags.classpath, deps.kotlinStdlib...)
 		flags.classpath = append(flags.classpath, kotlinJar)
 
 		// Jar kotlin classes into the final jar after javac
 		jars = append(jars, kotlinJar)
 
-		// Don't add kotlin-stdlib if using (on-device) renamed stdlib
-		// (it's expected to be on device bootclasspath)
-		if !Bool(j.properties.Renamed_kotlin_stdlib) {
+		if Bool(j.properties.Renamed_kotlin_stdlib) {
+			// Remove any kotlin-reflect related files
+			// TODO(pszczepaniak): Support kotlin-reflect
+			stripFiles = append(stripFiles,
+				"**/*.kotlin_module",
+				"**/*.kotlin_builtins")
+		} else {
+			// Only add kotlin-stdlib if not using (on-device) renamed stdlib
+			// (it's expected to be on device bootclasspath)
 			jars = append(jars, deps.kotlinStdlib...)
 		}
 	}
@@ -957,7 +1071,7 @@
 	}
 	if len(uniqueSrcFiles) > 0 || len(srcJars) > 0 {
 		var extraJarDeps android.Paths
-		if ctx.Config().IsEnvTrue("RUN_ERROR_PRONE") {
+		if ctx.Config().RunErrorProne() {
 			// If error-prone is enabled, add an additional rule to compile the java files into
 			// a separate set of classes (so that they don't overwrite the normal ones and require
 			// a rebuild when error-prone is turned off).
@@ -1044,7 +1158,8 @@
 		outputFile = jars[0]
 	} else {
 		combinedJar := android.PathForModuleOut(ctx, "combined", jarName)
-		TransformJarsToJar(ctx, combinedJar, "for javac", jars, manifest, false, nil)
+		TransformJarsToJar(ctx, combinedJar, "for javac", jars, manifest,
+			false, stripFiles, nil)
 		outputFile = combinedJar
 	}
 
@@ -1084,11 +1199,15 @@
 		outputFile = j.instrument(ctx, flags, outputFile, jarName)
 	}
 
-	if ctx.Device() && j.installable() {
-		outputFile = j.compileDex(ctx, flags, outputFile, jarName)
+	if ctx.Device() && (Bool(j.properties.Installable) || Bool(j.deviceProperties.Compile_dex)) {
+		var dexOutputFile android.Path
+		dexOutputFile = j.compileDex(ctx, flags, outputFile, jarName)
 		if ctx.Failed() {
 			return
 		}
+		if Bool(j.properties.Installable) {
+			outputFile = dexOutputFile
+		}
 	}
 	ctx.CheckbuildFile(outputFile)
 	j.outputFile = outputFile
@@ -1116,7 +1235,8 @@
 	// we cannot skip the combine step for now if there is only one jar
 	// since we have to strip META-INF/TRANSITIVE dir from turbine.jar
 	combinedJar := android.PathForModuleOut(ctx, "turbine-combined", jarName)
-	TransformJarsToJar(ctx, combinedJar, "for turbine", jars, android.OptionalPath{}, false, []string{"META-INF"})
+	TransformJarsToJar(ctx, combinedJar, "for turbine", jars, android.OptionalPath{},
+		false, nil, []string{"META-INF"})
 	headerJar = combinedJar
 
 	if j.properties.Jarjar_rules != nil {
@@ -1148,21 +1268,6 @@
 	return instrumentedJar
 }
 
-// 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", "core_current":
-		return strconv.Itoa(ctx.Config().DefaultAppTargetSdkInt())
-	default:
-		return android.GetNumericSdkVersion(String(j.deviceProperties.Sdk_version))
-	}
-}
-
-func (j *Module) installable() bool {
-	return BoolDefault(j.properties.Installable, true)
-}
-
 var _ Dependency = (*Library)(nil)
 
 func (j *Module) HeaderJars() android.Paths {
@@ -1177,6 +1282,10 @@
 	return j.exportAidlIncludeDirs
 }
 
+func (j *Module) ExportedSdkLibs() []string {
+	return j.exportedSdkLibs
+}
+
 var _ logtagsProducer = (*Module)(nil)
 
 func (j *Module) logtags() android.Paths {
@@ -1194,7 +1303,7 @@
 func (j *Library) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	j.compile(ctx)
 
-	if j.installable() {
+	if Bool(j.properties.Installable) || ctx.Host() {
 		j.installFile = ctx.InstallFile(android.PathForModuleInstall(ctx, "framework"),
 			ctx.ModuleName()+".jar", j.outputFile)
 	}
@@ -1204,22 +1313,16 @@
 	j.deps(ctx)
 }
 
-func LibraryFactory(installable bool) func() android.Module {
-	return func() android.Module {
-		module := &Library{}
+func LibraryFactory() android.Module {
+	module := &Library{}
 
-		if !installable {
-			module.properties.Installable = proptools.BoolPtr(false)
-		}
+	module.AddProperties(
+		&module.Module.properties,
+		&module.Module.deviceProperties,
+		&module.Module.protoProperties)
 
-		module.AddProperties(
-			&module.Module.properties,
-			&module.Module.deviceProperties,
-			&module.Module.protoProperties)
-
-		InitJavaModule(module, android.HostAndDeviceSupported)
-		return module
-	}
+	InitJavaModule(module, android.HostAndDeviceSupported)
+	return module
 }
 
 func LibraryHostFactory() android.Module {
@@ -1229,6 +1332,8 @@
 		&module.Module.properties,
 		&module.Module.protoProperties)
 
+	module.Module.properties.Installable = proptools.BoolPtr(true)
+
 	InitJavaModule(module, android.HostSupported)
 	return module
 }
@@ -1268,6 +1373,8 @@
 		&module.Module.protoProperties,
 		&module.testProperties)
 
+	module.Module.properties.Installable = proptools.BoolPtr(true)
+
 	InitJavaModule(module, android.HostAndDeviceSupported)
 	android.InitDefaultableModule(module)
 	return module
@@ -1281,6 +1388,8 @@
 		&module.Module.protoProperties,
 		&module.testProperties)
 
+	module.Module.properties.Installable = proptools.BoolPtr(true)
+
 	InitJavaModule(module, android.HostSupported)
 	android.InitDefaultableModule(module)
 	return module
@@ -1350,6 +1459,8 @@
 		&module.Module.protoProperties,
 		&module.binaryProperties)
 
+	module.Module.properties.Installable = proptools.BoolPtr(true)
+
 	android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommonFirst)
 	android.InitDefaultableModule(module)
 	return module
@@ -1363,6 +1474,8 @@
 		&module.Module.protoProperties,
 		&module.binaryProperties)
 
+	module.Module.properties.Installable = proptools.BoolPtr(true)
+
 	android.InitAndroidArchModule(module, android.HostSupported, android.MultilibCommonFirst)
 	android.InitDefaultableModule(module)
 	return module
@@ -1378,6 +1491,15 @@
 	Sdk_version *string
 
 	Installable *bool
+
+	// List of shared java libs that this module has dependencies to
+	Libs []string
+
+	// List of files to remove from the jar file(s)
+	Exclude_files []string
+
+	// List of directories to remove from the jar file(s)
+	Exclude_dirs []string
 }
 
 type Import struct {
@@ -1386,8 +1508,16 @@
 
 	properties ImportProperties
 
-	classpathFiles        android.Paths
 	combinedClasspathFile android.Path
+	exportedSdkLibs       []string
+}
+
+func (j *Import) sdkVersion() string {
+	return String(j.properties.Sdk_version)
+}
+
+func (j *Import) minSdkVersion() string {
+	return j.sdkVersion()
 }
 
 func (j *Import) Prebuilt() *android.Prebuilt {
@@ -1403,30 +1533,59 @@
 }
 
 func (j *Import) DepsMutator(ctx android.BottomUpMutatorContext) {
+	android.ExtractSourcesDeps(ctx, j.properties.Jars)
+	ctx.AddDependency(ctx.Module(), libTag, j.properties.Libs...)
 }
 
 func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	j.classpathFiles = android.PathsForModuleSrc(ctx, j.properties.Jars)
+	jars := ctx.ExpandSources(j.properties.Jars, nil)
 
 	outputFile := android.PathForModuleOut(ctx, "classes.jar")
-	TransformJarsToJar(ctx, outputFile, "for prebuilts", j.classpathFiles, android.OptionalPath{}, false, nil)
+	TransformJarsToJar(ctx, outputFile, "for prebuilts", jars, android.OptionalPath{},
+		false, j.properties.Exclude_files, j.properties.Exclude_dirs)
 	j.combinedClasspathFile = outputFile
+
+	ctx.VisitDirectDeps(func(module android.Module) {
+		otherName := ctx.OtherModuleName(module)
+		tag := ctx.OtherModuleDependencyTag(module)
+
+		switch dep := module.(type) {
+		case Dependency:
+			switch tag {
+			case libTag, staticLibTag:
+				// sdk lib names from dependencies are re-exported
+				j.exportedSdkLibs = append(j.exportedSdkLibs, dep.ExportedSdkLibs()...)
+			}
+		case SdkLibraryDependency:
+			switch tag {
+			case libTag:
+				// names of sdk libs that are directly depended are exported
+				j.exportedSdkLibs = append(j.exportedSdkLibs, otherName)
+			}
+		}
+	})
+
+	j.exportedSdkLibs = android.FirstUniqueStrings(j.exportedSdkLibs)
 }
 
 var _ Dependency = (*Import)(nil)
 
 func (j *Import) HeaderJars() android.Paths {
-	return j.classpathFiles
+	return android.Paths{j.combinedClasspathFile}
 }
 
 func (j *Import) ImplementationJars() android.Paths {
-	return j.classpathFiles
+	return android.Paths{j.combinedClasspathFile}
 }
 
 func (j *Import) AidlIncludeDirs() android.Paths {
 	return nil
 }
 
+func (j *Import) ExportedSdkLibs() []string {
+	return j.exportedSdkLibs
+}
+
 var _ android.PrebuiltInterface = (*Import)(nil)
 
 func ImportFactory() android.Module {
diff --git a/java/java_test.go b/java/java_test.go
index ea52496..6bba29b 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -25,6 +25,8 @@
 	"strconv"
 	"strings"
 	"testing"
+
+	"github.com/google/blueprint/proptools"
 )
 
 var buildDir string
@@ -72,7 +74,7 @@
 	ctx.RegisterModuleType("android_app", android.ModuleFactoryAdaptor(AndroidAppFactory))
 	ctx.RegisterModuleType("android_library", android.ModuleFactoryAdaptor(AndroidLibraryFactory))
 	ctx.RegisterModuleType("java_binary_host", android.ModuleFactoryAdaptor(BinaryHostFactory))
-	ctx.RegisterModuleType("java_library", android.ModuleFactoryAdaptor(LibraryFactory(true)))
+	ctx.RegisterModuleType("java_library", android.ModuleFactoryAdaptor(LibraryFactory))
 	ctx.RegisterModuleType("java_library_host", android.ModuleFactoryAdaptor(LibraryHostFactory))
 	ctx.RegisterModuleType("java_import", android.ModuleFactoryAdaptor(ImportFactory))
 	ctx.RegisterModuleType("java_defaults", android.ModuleFactoryAdaptor(defaultsFactory))
@@ -84,10 +86,12 @@
 	ctx.RegisterModuleType("droiddoc_host", android.ModuleFactoryAdaptor(DroiddocHostFactory))
 	ctx.RegisterModuleType("droiddoc_template", android.ModuleFactoryAdaptor(DroiddocTemplateFactory))
 	ctx.RegisterModuleType("java_sdk_library", android.ModuleFactoryAdaptor(sdkLibraryFactory))
+	ctx.RegisterModuleType("prebuilt_apis", android.ModuleFactoryAdaptor(prebuiltApisFactory))
 	ctx.PreArchMutators(android.RegisterPrebuiltsPreArchMutators)
 	ctx.PreArchMutators(android.RegisterPrebuiltsPostDepsMutators)
 	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
 	ctx.PreArchMutators(func(ctx android.RegisterMutatorsContext) {
+		ctx.TopDown("prebuilt_apis", prebuiltApisMutator).Parallel()
 		ctx.TopDown("java_sdk_library", sdkLibraryMutator).Parallel()
 	})
 	ctx.RegisterPreSingletonType("overlay", android.SingletonFactoryAdaptor(OverlaySingletonFactory))
@@ -96,6 +100,7 @@
 	extraModules := []string{
 		"core-oj",
 		"core-libart",
+		"core-lambda-stubs",
 		"framework",
 		"ext",
 		"okhttp",
@@ -141,28 +146,49 @@
 	}
 
 	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/a":   nil,
-		"java-res/b/b":   nil,
-		"java-res2/a":    nil,
-		"java-fg/a.java": nil,
-		"java-fg/b.java": nil,
-		"java-fg/c.java": nil,
+		"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/a":           nil,
+		"java-res/b/b":           nil,
+		"java-res2/a":            nil,
+		"java-fg/a.java":         nil,
+		"java-fg/b.java":         nil,
+		"java-fg/c.java":         nil,
+		"api/current.txt":        nil,
+		"api/removed.txt":        nil,
+		"api/system-current.txt": nil,
+		"api/system-removed.txt": nil,
+		"api/test-current.txt":   nil,
+		"api/test-removed.txt":   nil,
 
 		"prebuilts/sdk/14/public/android.jar":         nil,
 		"prebuilts/sdk/14/public/framework.aidl":      nil,
 		"prebuilts/sdk/14/system/android.jar":         nil,
+		"prebuilts/sdk/current/core/android.jar":      nil,
 		"prebuilts/sdk/current/public/android.jar":    nil,
 		"prebuilts/sdk/current/public/framework.aidl": nil,
 		"prebuilts/sdk/current/public/core.jar":       nil,
 		"prebuilts/sdk/current/system/android.jar":    nil,
 		"prebuilts/sdk/current/test/android.jar":      nil,
+		"prebuilts/sdk/28/public/api/foo.txt":         nil,
+		"prebuilts/sdk/28/system/api/foo.txt":         nil,
+		"prebuilts/sdk/28/test/api/foo.txt":           nil,
+		"prebuilts/sdk/28/public/api/foo-removed.txt": nil,
+		"prebuilts/sdk/28/system/api/foo-removed.txt": nil,
+		"prebuilts/sdk/28/test/api/foo-removed.txt":   nil,
+		"prebuilts/sdk/28/public/api/bar.txt":         nil,
+		"prebuilts/sdk/28/system/api/bar.txt":         nil,
+		"prebuilts/sdk/28/test/api/bar.txt":           nil,
+		"prebuilts/sdk/28/public/api/bar-removed.txt": nil,
+		"prebuilts/sdk/28/system/api/bar-removed.txt": nil,
+		"prebuilts/sdk/28/test/api/bar-removed.txt":   nil,
+		"prebuilts/sdk/tools/core-lambda-stubs.jar":   nil,
+		"prebuilts/sdk/Android.bp":                    []byte(`prebuilt_apis { name: "sdk", api_dirs: ["14", "28", "current"],}`),
 
 		// For framework-res, which is an implicit dependency for framework
 		"AndroidManifest.xml":                   nil,
@@ -175,9 +201,11 @@
 
 		"jdk8/jre/lib/jce.jar": nil,
 		"jdk8/jre/lib/rt.jar":  nil,
+		"jdk8/lib/tools.jar":   nil,
 
 		"bar-doc/a.java":                 nil,
 		"bar-doc/b.java":                 nil,
+		"bar-doc/IFoo.aidl":              nil,
 		"bar-doc/known_oj_tags.txt":      nil,
 		"external/doclava/templates-sdk": nil,
 
@@ -195,7 +223,7 @@
 
 func run(t *testing.T, ctx *android.TestContext, config android.Config) {
 	t.Helper()
-	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+	_, errs := ctx.ParseFileList(".", []string{"Android.bp", "prebuilts/sdk/Android.bp"})
 	android.FailIfErrored(t, errs)
 	_, errs = ctx.PrepareBuildActions(config)
 	android.FailIfErrored(t, errs)
@@ -315,6 +343,7 @@
 
 var classpathTestcases = []struct {
 	name          string
+	unbundled     bool
 	moduleType    string
 	host          android.OsClass
 	properties    string
@@ -341,20 +370,20 @@
 		properties:    `sdk_version: "14",`,
 		bootclasspath: []string{`""`},
 		system:        "bootclasspath", // special value to tell 1.9 test to expect bootclasspath
-		classpath:     []string{"prebuilts/sdk/14/public/android.jar"},
+		classpath:     []string{"prebuilts/sdk/14/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
 	},
 	{
 
 		name:          "current",
 		properties:    `sdk_version: "current",`,
-		bootclasspath: []string{"android_stubs_current"},
+		bootclasspath: []string{"android_stubs_current", "core-lambda-stubs"},
 		system:        "bootclasspath", // special value to tell 1.9 test to expect bootclasspath
 	},
 	{
 
 		name:          "system_current",
 		properties:    `sdk_version: "system_current",`,
-		bootclasspath: []string{"android_system_stubs_current"},
+		bootclasspath: []string{"android_system_stubs_current", "core-lambda-stubs"},
 		system:        "bootclasspath", // special value to tell 1.9 test to expect bootclasspath
 	},
 	{
@@ -363,20 +392,20 @@
 		properties:    `sdk_version: "system_14",`,
 		bootclasspath: []string{`""`},
 		system:        "bootclasspath", // special value to tell 1.9 test to expect bootclasspath
-		classpath:     []string{"prebuilts/sdk/14/system/android.jar"},
+		classpath:     []string{"prebuilts/sdk/14/system/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
 	},
 	{
 
 		name:          "test_current",
 		properties:    `sdk_version: "test_current",`,
-		bootclasspath: []string{"android_test_stubs_current"},
+		bootclasspath: []string{"android_test_stubs_current", "core-lambda-stubs"},
 		system:        "bootclasspath", // special value to tell 1.9 test to expect bootclasspath
 	},
 	{
 
 		name:          "core_current",
 		properties:    `sdk_version: "core_current",`,
-		bootclasspath: []string{"core.current.stubs"},
+		bootclasspath: []string{"core.current.stubs", "core-lambda-stubs"},
 		system:        "bootclasspath", // special value to tell 1.9 test to expect bootclasspath
 	},
 	{
@@ -425,6 +454,24 @@
 		properties: `host_supported: true, no_standard_libs: true, system_modules: "none"`,
 		classpath:  []string{},
 	},
+	{
+
+		name:          "unbundled sdk v14",
+		unbundled:     true,
+		properties:    `sdk_version: "14",`,
+		bootclasspath: []string{`""`},
+		system:        "bootclasspath", // special value to tell 1.9 test to expect bootclasspath
+		classpath:     []string{"prebuilts/sdk/14/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+	},
+	{
+
+		name:          "unbundled current",
+		unbundled:     true,
+		properties:    `sdk_version: "current",`,
+		bootclasspath: []string{`""`},
+		system:        "bootclasspath", // special value to tell 1.9 test to expect bootclasspath
+		classpath:     []string{"prebuilts/sdk/current/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+	},
 }
 
 func TestClasspath(t *testing.T) {
@@ -475,7 +522,12 @@
 
 			t.Run("1.8", func(t *testing.T) {
 				// Test default javac 1.8
-				ctx := testJava(t, bp)
+				config := testConfig(nil)
+				if testcase.unbundled {
+					config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true)
+				}
+				ctx := testContext(config, bp, nil)
+				run(t, ctx, config)
 
 				javac := ctx.ModuleForTests("foo", variant).Rule("javac")
 
@@ -503,6 +555,9 @@
 			// Test again with javac 1.9
 			t.Run("1.9", func(t *testing.T) {
 				config := testConfig(map[string]string{"EXPERIMENTAL_USE_OPENJDK9": "true"})
+				if testcase.unbundled {
+					config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true)
+				}
 				ctx := testContext(config, bp, nil)
 				run(t, ctx, config)
 
@@ -543,14 +598,15 @@
 
 	javac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
 	combineJar := ctx.ModuleForTests("foo", "android_common").Description("for javac")
+	barJar := ctx.ModuleForTests("bar", "android_common").Rule("combineJar").Output
+	bazJar := ctx.ModuleForTests("baz", "android_common").Rule("combineJar").Output
 
-	bar := "a.jar"
-	if !strings.Contains(javac.Args["classpath"], bar) {
-		t.Errorf("foo classpath %v does not contain %q", javac.Args["classpath"], bar)
+	if !strings.Contains(javac.Args["classpath"], barJar.String()) {
+		t.Errorf("foo classpath %v does not contain %q", javac.Args["classpath"], barJar.String())
 	}
 
-	if len(combineJar.Inputs) != 2 || combineJar.Inputs[1].String() != "b.jar" {
-		t.Errorf("foo combineJar inputs %v does not contain %q", combineJar.Inputs, "b.jar")
+	if len(combineJar.Inputs) != 2 || combineJar.Inputs[1].String() != bazJar.String() {
+		t.Errorf("foo combineJar inputs %v does not contain %q", combineJar.Inputs, bazJar.String())
 	}
 }
 
@@ -899,6 +955,7 @@
 		    name: "bar-doc",
 		    srcs: [
 		        "bar-doc/*.java",
+		        "bar-doc/IFoo.aidl",
 		    ],
 		    exclude_srcs: [
 		        "bar-doc/b.java"
@@ -921,6 +978,14 @@
 	if stubsJar != barDoc.Output.String() {
 		t.Errorf("expected stubs Jar [%q], got %q", stubsJar, barDoc.Output.String())
 	}
+	inputs := ctx.ModuleForTests("bar-doc", "android_common").Rule("javadoc").Inputs
+	var javaSrcs []string
+	for _, i := range inputs {
+		javaSrcs = append(javaSrcs, i.Base())
+	}
+	if len(javaSrcs) != 2 || javaSrcs[0] != "a.java" || javaSrcs[1] != "IFoo.java" {
+		t.Errorf("inputs of bar-doc must be []string{\"a.java\", \"IFoo.java\", but was %#v.", javaSrcs)
+	}
 }
 
 func TestJarGenrules(t *testing.T) {
@@ -1031,16 +1096,27 @@
 			libs: ["foo", "bar"],
 			sdk_version: "system_current",
 		}
+		java_library {
+		    name: "qux",
+		    srcs: ["c.java"],
+		    libs: ["baz"],
+		    sdk_version: "system_current",
+		}
 		`)
 
 	// check the existence of the internal modules
 	ctx.ModuleForTests("foo", "android_common")
 	ctx.ModuleForTests("foo"+sdkStubsLibrarySuffix, "android_common")
 	ctx.ModuleForTests("foo"+sdkStubsLibrarySuffix+sdkSystemApiSuffix, "android_common")
+	ctx.ModuleForTests("foo"+sdkStubsLibrarySuffix+sdkTestApiSuffix, "android_common")
 	ctx.ModuleForTests("foo"+sdkDocsSuffix, "android_common")
 	ctx.ModuleForTests("foo"+sdkDocsSuffix+sdkSystemApiSuffix, "android_common")
+	ctx.ModuleForTests("foo"+sdkDocsSuffix+sdkTestApiSuffix, "android_common")
 	ctx.ModuleForTests("foo"+sdkImplLibrarySuffix, "android_common")
 	ctx.ModuleForTests("foo"+sdkXmlFileSuffix, "android_common")
+	ctx.ModuleForTests("foo.api.public.28", "")
+	ctx.ModuleForTests("foo.api.system.28", "")
+	ctx.ModuleForTests("foo.api.test.28", "")
 
 	bazJavac := ctx.ModuleForTests("baz", "android_common").Rule("javac")
 	// tests if baz is actually linked to the stubs lib
@@ -1058,4 +1134,13 @@
 		t.Errorf("baz javac classpath %v should not contain %q", bazJavac.Args["classpath"],
 			"foo.stubs.jar")
 	}
+
+	// test if baz has exported SDK lib names foo and bar to qux
+	qux := ctx.ModuleForTests("qux", "android_common")
+	if quxLib, ok := qux.Module().(*Library); ok {
+		sdkLibs := quxLib.ExportedSdkLibs()
+		if len(sdkLibs) != 2 || !android.InList("foo", sdkLibs) || !android.InList("bar", sdkLibs) {
+			t.Errorf("qux should export \"foo\" and \"bar\" but exports %v", sdkLibs)
+		}
+	}
 }
diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go
new file mode 100644
index 0000000..59b2092
--- /dev/null
+++ b/java/prebuilt_apis.go
@@ -0,0 +1,200 @@
+// Copyright 2018 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"
+	"sort"
+	"strconv"
+	"strings"
+
+	"github.com/google/blueprint/proptools"
+)
+
+// prebuilt_apis is a meta-module that generates filegroup modules for all
+// API txt files found under the directory where the Android.bp is located.
+// Specificaly, an API file located at ./<ver>/<scope>/api/<module>.txt
+// generates a filegroup module named <module>-api.<scope>.<ver>.
+//
+// It also creates <module>-api.<scope>.latest for the lastest <ver>.
+//
+func init() {
+	android.RegisterModuleType("prebuilt_apis", prebuiltApisFactory)
+
+	android.PreArchMutators(func(ctx android.RegisterMutatorsContext) {
+		ctx.TopDown("prebuilt_apis", prebuiltApisMutator).Parallel()
+	})
+}
+
+type prebuiltApisProperties struct {
+	// list of api version directories
+	Api_dirs []string
+}
+
+type prebuiltApis struct {
+	android.ModuleBase
+	properties prebuiltApisProperties
+}
+
+func (module *prebuiltApis) DepsMutator(ctx android.BottomUpMutatorContext) {
+	// no need to implement
+}
+
+func (module *prebuiltApis) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	// no need to implement
+}
+
+func parseJarPath(ctx android.BaseModuleContext, path string) (module string, apiver string, scope string) {
+	elements := strings.Split(path, "/")
+
+	apiver = elements[0]
+	scope = elements[1]
+
+	module = strings.TrimSuffix(elements[2], ".jar")
+	return
+}
+
+func parseApiFilePath(ctx android.BaseModuleContext, path string) (module string, apiver int, scope string) {
+	elements := strings.Split(path, "/")
+	ver, err := strconv.Atoi(elements[0])
+	if err != nil {
+		ctx.ModuleErrorf("invalid version %q found in path: %q", elements[0], path)
+		return
+	}
+	apiver = ver
+
+	scope = elements[1]
+	if scope != "public" && scope != "system" && scope != "test" {
+		ctx.ModuleErrorf("invalid scope %q found in path: %q", scope, path)
+		return
+	}
+
+	// elements[2] is string literal "api". skipping.
+	module = strings.TrimSuffix(elements[3], ".txt")
+	return
+}
+
+func createImport(mctx android.TopDownMutatorContext, module string, scope string, apiver string, path string) {
+	props := struct {
+		Name        *string
+		Jars        []string
+		Sdk_version *string
+		Installable *bool
+	}{}
+	props.Name = proptools.StringPtr(mctx.ModuleName() + "_" + scope + "_" + apiver + "_" + module)
+	props.Jars = append(props.Jars, path)
+	// TODO(hansson): change to scope after migration is done.
+	props.Sdk_version = proptools.StringPtr("current")
+	props.Installable = proptools.BoolPtr(false)
+
+	mctx.CreateModule(android.ModuleFactoryAdaptor(ImportFactory), &props)
+}
+
+func createFilegroup(mctx android.TopDownMutatorContext, module string, scope string, apiver string, path string) {
+	fgName := module + ".api." + scope + "." + apiver
+	filegroupProps := struct {
+		Name *string
+		Srcs []string
+	}{}
+	filegroupProps.Name = proptools.StringPtr(fgName)
+	filegroupProps.Srcs = []string{path}
+	mctx.CreateModule(android.ModuleFactoryAdaptor(android.FileGroupFactory), &filegroupProps)
+}
+
+func prebuiltSdkStubs(mctx android.TopDownMutatorContext) {
+	mydir := mctx.ModuleDir() + "/"
+	// <apiver>/<scope>/<module>.jar
+	var files []string
+	for _, apiver := range mctx.Module().(*prebuiltApis).properties.Api_dirs {
+		for _, scope := range []string{"public", "system", "test", "core"} {
+			vfiles, err := mctx.GlobWithDeps(mydir+apiver+"/"+scope+"*/*.jar", nil)
+			if err != nil {
+				mctx.ModuleErrorf("failed to glob jar files under %q: %s", mydir+apiver+"/"+scope, err)
+			}
+			files = append(files, vfiles...)
+		}
+	}
+
+	for _, f := range files {
+		// create a Import module for each jar file
+		localPath := strings.TrimPrefix(f, mydir)
+		module, apiver, scope := parseJarPath(mctx, localPath)
+		createImport(mctx, module, scope, apiver, localPath)
+	}
+}
+
+func prebuiltApiFiles(mctx android.TopDownMutatorContext) {
+	mydir := mctx.ModuleDir() + "/"
+	// <apiver>/<scope>/api/<module>.txt
+	files, err := mctx.GlobWithDeps(mydir+"*/*/api/*.txt", nil)
+	if err != nil {
+		mctx.ModuleErrorf("failed to glob api txt files under %q: %s", mydir, err)
+	}
+	if len(files) == 0 {
+		mctx.ModuleErrorf("no api file found under %q", mydir)
+	}
+
+	// construct a map to find out the latest api file path
+	// for each (<module>, <scope>) pair.
+	type latestApiInfo struct {
+		module string
+		scope  string
+		apiver int
+		path   string
+	}
+	m := make(map[string]latestApiInfo)
+
+	for _, f := range files {
+		// create a filegroup for each api txt file
+		localPath := strings.TrimPrefix(f, mydir)
+		module, apiver, scope := parseApiFilePath(mctx, localPath)
+		createFilegroup(mctx, module, scope, strconv.Itoa(apiver), localPath)
+
+		// find the latest apiver
+		key := module + "." + scope
+		info, ok := m[key]
+		if !ok {
+			m[key] = latestApiInfo{module, scope, apiver, localPath}
+		} else if apiver > info.apiver {
+			info.apiver = apiver
+			info.path = localPath
+		}
+	}
+	// create filegroups for the latest version of (<module>, <scope>) pairs
+	// sort the keys in order to make build.ninja stable
+	keys := make([]string, 0, len(m))
+	for k := range m {
+		keys = append(keys, k)
+	}
+	sort.Strings(keys)
+	for _, k := range keys {
+		info := m[k]
+		createFilegroup(mctx, info.module, info.scope, "latest", info.path)
+	}
+}
+
+func prebuiltApisMutator(mctx android.TopDownMutatorContext) {
+	if _, ok := mctx.Module().(*prebuiltApis); ok {
+		prebuiltApiFiles(mctx)
+		prebuiltSdkStubs(mctx)
+	}
+}
+
+func prebuiltApisFactory() android.Module {
+	module := &prebuiltApis{}
+	module.AddProperties(&module.properties)
+	android.InitAndroidModule(module)
+	return module
+}
diff --git a/java/proto.go b/java/proto.go
index cfd733a..58b039e 100644
--- a/java/proto.go
+++ b/java/proto.go
@@ -24,18 +24,23 @@
 
 func init() {
 	pctx.HostBinToolVariable("protocCmd", "aprotoc")
+	pctx.HostBinToolVariable("depFixCmd", "dep_fixer")
 }
 
 var (
 	proto = pctx.AndroidStaticRule("protoc",
 		blueprint.RuleParams{
 			Command: `rm -rf $out.tmp && mkdir -p $out.tmp && ` +
-				`$protocCmd $protoOut=$protoOutParams:$out.tmp -I $protoBase $protoFlags $in && ` +
+				`$protocCmd $protoOut=$protoOutParams:$out.tmp --dependency_out=$out.d -I $protoBase $protoFlags $in && ` +
+				`$depFixCmd $out.d && ` +
 				`${config.SoongZipCmd} -jar -o $out -C $out.tmp -D $out.tmp && rm -rf $out.tmp`,
 			CommandDeps: []string{
 				"$protocCmd",
+				"$depFixCmd",
 				"${config.SoongZipCmd}",
 			},
+			Depfile: "${out}.d",
+			Deps:    blueprint.DepsGCC,
 		}, "protoBase", "protoFlags", "protoOut", "protoOutParams")
 )
 
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 703401c..e65af65 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -46,6 +46,7 @@
 	publicApiStubsTag = dependencyTag{name: "public"}
 	systemApiStubsTag = dependencyTag{name: "system"}
 	testApiStubsTag   = dependencyTag{name: "test"}
+	implLibTag        = dependencyTag{name: "platform"}
 )
 
 type apiScope int
@@ -67,11 +68,8 @@
 // classpath at runtime if requested via <uses-library>.
 //
 // TODO: these are big features that are currently missing
-// 1) check for API consistency
-// 2) install stubs libs as the dist artifacts
-// 3) ensuring that apps have appropriate <uses-library> tag
-// 4) disallowing linking to the runtime shared lib
-// 5) HTML generation
+// 1) disallowing linking to the runtime shared lib
+// 2) HTML generation
 
 func init() {
 	android.RegisterModuleType("java_sdk_library", sdkLibraryFactory)
@@ -102,12 +100,20 @@
 	// These libraries are not compiled into the stubs jar.
 	Static_libs []string `android:"arch_variant"`
 
+	// List of Java libraries that will be in the classpath when building stubs
+	Stub_only_libs []string `android:"arch_variant"`
+
 	// list of package names that will be documented and publicized as API
 	Api_packages []string
 
 	// list of package names that must be hidden from the API
 	Hidden_api_packages []string
 
+	Errorprone struct {
+		// List of javac flags that should only be used when running errorprone.
+		Javacflags []string
+	}
+
 	// TODO: determines whether to create HTML doc or not
 	//Html_doc *bool
 }
@@ -122,6 +128,7 @@
 	publicApiStubsPath android.Paths
 	systemApiStubsPath android.Paths
 	testApiStubsPath   android.Paths
+	implLibPath        android.Paths
 }
 
 func (module *sdkLibrary) DepsMutator(ctx android.BottomUpMutatorContext) {
@@ -129,24 +136,27 @@
 	ctx.AddDependency(ctx.Module(), publicApiStubsTag, module.stubsName(apiScopePublic))
 	ctx.AddDependency(ctx.Module(), systemApiStubsTag, module.stubsName(apiScopeSystem))
 	ctx.AddDependency(ctx.Module(), testApiStubsTag, module.stubsName(apiScopeTest))
+	ctx.AddDependency(ctx.Module(), implLibTag, module.implName())
 }
 
 func (module *sdkLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	// Record the paths to the header jars of the stubs library.
+	// Record the paths to the header jars of the library (stubs and impl).
 	// When this java_sdk_library is dependened from others via "libs" property,
 	// the recorded paths will be returned depending on the link type of the caller.
 	ctx.VisitDirectDeps(func(to android.Module) {
 		otherName := ctx.OtherModuleName(to)
 		tag := ctx.OtherModuleDependencyTag(to)
 
-		if stubs, ok := to.(Dependency); ok {
+		if lib, ok := to.(Dependency); ok {
 			switch tag {
 			case publicApiStubsTag:
-				module.publicApiStubsPath = stubs.HeaderJars()
+				module.publicApiStubsPath = lib.HeaderJars()
 			case systemApiStubsTag:
-				module.systemApiStubsPath = stubs.HeaderJars()
+				module.systemApiStubsPath = lib.HeaderJars()
 			case testApiStubsTag:
-				module.testApiStubsPath = stubs.HeaderJars()
+				module.testApiStubsPath = lib.HeaderJars()
+			case implLibTag:
+				module.implLibPath = lib.HeaderJars()
 			default:
 				ctx.ModuleErrorf("depends on module %q of unknown tag %q", otherName, tag)
 			}
@@ -155,15 +165,31 @@
 }
 
 func (module *sdkLibrary) AndroidMk() android.AndroidMkData {
-	// Create a phony module that installs the impl library, for the case when this lib is
-	// in PRODUCT_PACKAGES.
 	return android.AndroidMkData{
 		Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
+			// Create a phony module that installs the impl library, for the case when this lib is
+			// in PRODUCT_PACKAGES.
 			fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
 			fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
 			fmt.Fprintln(w, "LOCAL_MODULE :=", name)
 			fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES := "+module.implName())
 			fmt.Fprintln(w, "include $(BUILD_PHONY_PACKAGE)")
+			// Create dist rules to install the stubs libs to the dist dir
+			if len(module.publicApiStubsPath) == 1 {
+				fmt.Fprintln(w, "$(call dist-for-goals,sdk win_sdk,"+
+					module.publicApiStubsPath.Strings()[0]+
+					":"+path.Join("apistubs", "public", module.BaseModuleName()+".jar")+")")
+			}
+			if len(module.systemApiStubsPath) == 1 {
+				fmt.Fprintln(w, "$(call dist-for-goals,sdk win_sdk,"+
+					module.systemApiStubsPath.Strings()[0]+
+					":"+path.Join("apistubs", "system", module.BaseModuleName()+".jar")+")")
+			}
+			if len(module.testApiStubsPath) == 1 {
+				fmt.Fprintln(w, "$(call dist-for-goals,sdk win_sdk,"+
+					module.testApiStubsPath.Strings()[0]+
+					":"+path.Join("apistubs", "test", module.BaseModuleName()+".jar")+")")
+			}
 		},
 	}
 }
@@ -245,21 +271,32 @@
 	return apiTagName
 }
 
-// returns the path (relative to this module) to the API txt file. Files are located
-// ./<api_dir>/<api_level>.txt where <api_level> is either current, system-current, removed,
-// or system-removed.
-func (module *sdkLibrary) apiFilePath(apiLevel string, apiScope apiScope) string {
-	apiDir := "api"
-	apiFile := apiLevel
+func (module *sdkLibrary) latestApiFilegroupName(apiScope apiScope) string {
+	name := ":" + module.BaseModuleName() + ".api."
 	switch apiScope {
+	case apiScopePublic:
+		name = name + "public"
 	case apiScopeSystem:
-		apiFile = "system-" + apiFile
+		name = name + "system"
 	case apiScopeTest:
-		apiFile = "test-" + apiFile
+		name = name + "test"
 	}
-	apiFile = apiFile + ".txt"
+	name = name + ".latest"
+	return name
+}
 
-	return path.Join(apiDir, apiFile)
+func (module *sdkLibrary) latestRemovedApiFilegroupName(apiScope apiScope) string {
+	name := ":" + module.BaseModuleName() + "-removed.api."
+	switch apiScope {
+	case apiScopePublic:
+		name = name + "public"
+	case apiScopeSystem:
+		name = name + "system"
+	case apiScopeTest:
+		name = name + "test"
+	}
+	name = name + ".latest"
+	return name
 }
 
 // Creates a static java library that has API stubs
@@ -268,6 +305,7 @@
 		Name              *string
 		Srcs              []string
 		Sdk_version       *string
+		Libs              []string
 		Soc_specific      *bool
 		Device_specific   *bool
 		Product_specific  *bool
@@ -285,6 +323,7 @@
 	// sources are generated from the droiddoc
 	props.Srcs = []string{":" + module.docsName(apiScope)}
 	props.Sdk_version = proptools.StringPtr(module.sdkVersion(apiScope))
+	props.Libs = module.properties.Stub_only_libs
 	// Unbundled apps will use the prebult one from /prebuilts/sdk
 	props.Product_variables.Unbundled_build.Enabled = proptools.BoolPtr(false)
 	props.Product_variables.Pdk.Enabled = proptools.BoolPtr(false)
@@ -297,7 +336,7 @@
 		props.Product_specific = proptools.BoolPtr(true)
 	}
 
-	mctx.CreateModule(android.ModuleFactoryAdaptor(LibraryFactory(false)), &props)
+	mctx.CreateModule(android.ModuleFactoryAdaptor(LibraryFactory), &props)
 }
 
 // Creates a droiddoc module that creates stubs source files from the given full source
@@ -316,6 +355,14 @@
 		Api_tag_name            *string
 		Api_filename            *string
 		Removed_api_filename    *string
+		Check_api               struct {
+			Current       ApiToCheck
+			Last_released ApiToCheck
+		}
+		Aidl struct {
+			Include_dirs       []string
+			Local_include_dirs []string
+		}
 	}{}
 
 	props.Name = proptools.StringPtr(module.docsName(apiScope))
@@ -323,7 +370,12 @@
 	props.Srcs = append(props.Srcs, module.properties.Api_srcs...)
 	props.Custom_template = proptools.StringPtr("droiddoc-templates-sdk")
 	props.Installable = proptools.BoolPtr(false)
+	// A droiddoc module has only one Libs property and doesn't distinguish between
+	// shared libs and static libs. So we need to add both of these libs to Libs property.
 	props.Libs = module.properties.Libs
+	props.Libs = append(props.Libs, module.properties.Static_libs...)
+	props.Aidl.Include_dirs = module.deviceProperties.Aidl.Include_dirs
+	props.Aidl.Local_include_dirs = module.deviceProperties.Aidl.Local_include_dirs
 
 	droiddocArgs := " -hide 110 -hide 111 -hide 113 -hide 121 -hide 125 -hide 126 -hide 127 -hide 128" +
 		" -stubpackages " + strings.Join(module.properties.Api_packages, ":") +
@@ -340,7 +392,6 @@
 	// List of APIs identified from the provided source files are created. They are later
 	// compared against to the not-yet-released (a.k.a current) list of APIs and to the
 	// last-released (a.k.a numbered) list of API.
-	// TODO: If any incompatible change is detected, break the build
 	currentApiFileName := "current.txt"
 	removedApiFileName := "removed.txt"
 	switch apiScope {
@@ -353,12 +404,31 @@
 	}
 	currentApiFileName = path.Join("api", currentApiFileName)
 	removedApiFileName = path.Join("api", removedApiFileName)
+	// TODO(jiyong): remove these three props
 	props.Api_tag_name = proptools.StringPtr(module.apiTagName(apiScope))
-	// Note: the exact names of these two are not important because they are always
-	// referenced by the make variable $(INTERNAL_PLATFORM_<TAG_NAME>_API_FILE)
 	props.Api_filename = proptools.StringPtr(currentApiFileName)
 	props.Removed_api_filename = proptools.StringPtr(removedApiFileName)
 
+	// check against the not-yet-release API
+	props.Check_api.Current.Api_file = proptools.StringPtr(currentApiFileName)
+	props.Check_api.Current.Removed_api_file = proptools.StringPtr(removedApiFileName)
+	// any change is reported as error
+	props.Check_api.Current.Args = proptools.StringPtr("-error 2 -error 3 -error 4 -error 5 " +
+		"-error 6 -error 7 -error 8 -error 9 -error 10 -error 11 -error 12 -error 13 " +
+		"-error 14 -error 15 -error 16 -error 17 -error 18 -error 19 -error 20 " +
+		"-error 21 -error 23 -error 24 -error 25 -error 26 -error 27")
+
+	// check against the latest released API
+	props.Check_api.Last_released.Api_file = proptools.StringPtr(
+		module.latestApiFilegroupName(apiScope))
+	props.Check_api.Last_released.Removed_api_file = proptools.StringPtr(
+		module.latestRemovedApiFilegroupName(apiScope))
+	// backward incompatible changes are reported as error
+	props.Check_api.Last_released.Args = proptools.StringPtr("-hide 2 -hide 3 -hide 4 -hide 5 " +
+		"-hide 6 -hide 24 -hide 25 -hide 26 -hide 27 " +
+		"-error 7 -error 8 -error 9 -error 10 -error 11 -error 12 -error 13 -error 14 " +
+		"-error 15 -error 16 -error 17 -error 18")
+
 	// Include the part of the framework source. This is required for the case when
 	// API class is extending from the framework class. In that case, doclava needs
 	// to know whether the base class is hidden or not. Since that information is
@@ -388,15 +458,21 @@
 		Soc_specific     *bool
 		Device_specific  *bool
 		Product_specific *bool
+		Installable      *bool
 		Required         []string
+		Errorprone       struct {
+			Javacflags []string
+		}
 	}{}
 
 	props.Name = proptools.StringPtr(module.implName())
 	props.Srcs = module.properties.Srcs
 	props.Libs = module.properties.Libs
 	props.Static_libs = module.properties.Static_libs
+	props.Installable = proptools.BoolPtr(true)
 	// XML file is installed along with the impl lib
 	props.Required = []string{module.xmlFileName()}
+	props.Errorprone.Javacflags = module.properties.Errorprone.Javacflags
 
 	if module.SocSpecific() {
 		props.Soc_specific = proptools.BoolPtr(true)
@@ -406,7 +482,7 @@
 		props.Product_specific = proptools.BoolPtr(true)
 	}
 
-	mctx.CreateModule(android.ModuleFactoryAdaptor(LibraryFactory(true)), &props, &module.deviceProperties)
+	mctx.CreateModule(android.ModuleFactoryAdaptor(LibraryFactory), &props, &module.deviceProperties)
 }
 
 // Creates the xml file that publicizes the runtime library
@@ -472,8 +548,10 @@
 // to satisfy SdkLibraryDependency interface
 func (module *sdkLibrary) HeaderJars(linkType linkType) android.Paths {
 	// This module is just a wrapper for the stubs.
-	if linkType == javaSystem || linkType == javaPlatform {
+	if linkType == javaSystem {
 		return module.systemApiStubsPath
+	} else if linkType == javaPlatform {
+		return module.implLibPath
 	} else {
 		return module.publicApiStubsPath
 	}
diff --git a/java/support_libraries.go b/java/support_libraries.go
new file mode 100644
index 0000000..320afae
--- /dev/null
+++ b/java/support_libraries.go
@@ -0,0 +1,66 @@
+// Copyright 2018 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 (
+	"sort"
+	"strings"
+
+	"android/soong/android"
+)
+
+func init() {
+	android.RegisterMakeVarsProvider(pctx, supportLibrariesMakeVarsProvider)
+}
+
+func supportLibrariesMakeVarsProvider(ctx android.MakeVarsContext) {
+	var supportAars, supportJars []string
+
+	sctx := ctx.SingletonContext()
+	sctx.VisitAllModules(func(module android.Module) {
+		dir := sctx.ModuleDir(module)
+		switch {
+		case strings.HasPrefix(dir, "prebuilts/sdk/current/extras"),
+			dir == "prebuilts/sdk/current/androidx",
+			dir == "prebuilts/sdk/current/car",
+			dir == "prebuilts/sdk/current/optional",
+			dir == "prebuilts/sdk/current/support":
+			// Support library
+		default:
+			// Not a support library
+			return
+		}
+
+		name := sctx.ModuleName(module)
+		if strings.HasSuffix(name, "-nodeps") {
+			return
+		}
+
+		switch module.(type) {
+		case *AndroidLibrary, *AARImport:
+			supportAars = append(supportAars, name)
+		case *Library, *Import:
+			supportJars = append(supportJars, name)
+		default:
+			sctx.ModuleErrorf(module, "unknown module type %t", module)
+		}
+	})
+
+	sort.Strings(supportAars)
+	sort.Strings(supportJars)
+
+	ctx.Strict("SUPPORT_LIBRARIES_AARS", strings.Join(supportAars, " "))
+	ctx.Strict("SUPPORT_LIBRARIES_JARS", strings.Join(supportJars, " "))
+}
diff --git a/java/system_modules.go b/java/system_modules.go
index 943eaeb..73a5131 100644
--- a/java/system_modules.go
+++ b/java/system_modules.go
@@ -119,9 +119,7 @@
 
 	jars = append(jars, android.PathsForModuleSrc(ctx, system.properties.Jars)...)
 
-	if ctx.Config().TargetOpenJDK9() {
-		system.outputFile = TransformJarsToSystemModules(ctx, "java.base", jars)
-	}
+	system.outputFile = TransformJarsToSystemModules(ctx, "java.base", jars)
 }
 
 func (system *SystemModules) DepsMutator(ctx android.BottomUpMutatorContext) {
@@ -131,17 +129,15 @@
 func (system *SystemModules) AndroidMk() android.AndroidMkData {
 	return android.AndroidMkData{
 		Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
-			if system.outputFile != nil {
-				makevar := "SOONG_SYSTEM_MODULES_" + name
-				fmt.Fprintln(w)
-				fmt.Fprintln(w, makevar, ":=", system.outputFile.String())
-				fmt.Fprintln(w, ".KATI_READONLY", ":=", makevar)
-				fmt.Fprintln(w, name+":", "$("+makevar+")")
-				fmt.Fprintln(w)
-				makevar = "SOONG_SYSTEM_MODULES_LIBS_" + name
-				fmt.Fprintln(w, makevar, ":=", strings.Join(system.properties.Libs, " "))
-				fmt.Fprintln(w, ".KATI_READONLY :=", makevar)
-			}
+			makevar := "SOONG_SYSTEM_MODULES_" + name
+			fmt.Fprintln(w)
+			fmt.Fprintln(w, makevar, ":=", system.outputFile.String())
+			fmt.Fprintln(w, ".KATI_READONLY", ":=", makevar)
+			fmt.Fprintln(w, name+":", "$("+makevar+")")
+			fmt.Fprintln(w)
+			makevar = "SOONG_SYSTEM_MODULES_LIBS_" + name
+			fmt.Fprintln(w, makevar, ":=", strings.Join(system.properties.Libs, " "))
+			fmt.Fprintln(w, ".KATI_READONLY :=", makevar)
 		},
 	}
 }
diff --git a/phony/phony.go b/phony/phony.go
index a39b5d5..0c62e8a 100644
--- a/phony/phony.go
+++ b/phony/phony.go
@@ -23,7 +23,7 @@
 )
 
 func init() {
-	android.RegisterModuleType("phony", phonyFactory)
+	android.RegisterModuleType("phony", PhonyFactory)
 }
 
 type phony struct {
@@ -31,7 +31,7 @@
 	requiredModuleNames []string
 }
 
-func phonyFactory() android.Module {
+func PhonyFactory() android.Module {
 	module := &phony{}
 
 	android.InitAndroidModule(module)
diff --git a/python/androidmk.go b/python/androidmk.go
index 5fa01ab..365b422 100644
--- a/python/androidmk.go
+++ b/python/androidmk.go
@@ -77,6 +77,7 @@
 		ret.OutputFile = android.OptionalPathForPath(installer.path)
 	}
 
+	ret.Required = append(ret.Required, "libc++")
 	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
 		path := installer.path.RelPathString()
 		dir, file := filepath.Split(path)
diff --git a/python/builder.go b/python/builder.go
index 969f9ef..ec4cb4e 100644
--- a/python/builder.go
+++ b/python/builder.go
@@ -96,11 +96,8 @@
 			Output:      binFile,
 			Implicits:   implicits,
 			Args: map[string]string{
-				"interp": strings.Replace(interpreter, "/", `\/`, -1),
-				// we need remove "runfiles/" suffix since stub script starts
-				// searching for main file in each sub-dir of "runfiles" directory tree.
-				"main": strings.Replace(strings.TrimPrefix(main, runFiles+"/"),
-					"/", `\/`, -1),
+				"interp":    strings.Replace(interpreter, "/", `\/`, -1),
+				"main":      strings.Replace(main, "/", `\/`, -1),
 				"template":  template.String(),
 				"stub":      stub,
 				"mergedZip": mergedZip.String(),
diff --git a/python/proto.go b/python/proto.go
index 82ee3cb..2370cd2 100644
--- a/python/proto.go
+++ b/python/proto.go
@@ -29,13 +29,15 @@
 	proto = pctx.AndroidStaticRule("protoc",
 		blueprint.RuleParams{
 			Command: `rm -rf $out.tmp && mkdir -p $out.tmp && ` +
-				`$protocCmd --python_out=$out.tmp -I $protoBase $protoFlags $in && ` +
-				`$parCmd -o $out -P $pkgPath -C $out.tmp -D $out.tmp && rm -rf $out.tmp`,
+				`$protocCmd --python_out=$out.tmp --dependency_out=$out.d -I $protoBase $protoFlags $in && ` +
+				`$parCmd -o $out $pkgPathArgs -C $out.tmp -D $out.tmp && rm -rf $out.tmp`,
 			CommandDeps: []string{
 				"$protocCmd",
 				"$parCmd",
 			},
-		}, "protoBase", "protoFlags", "pkgPath")
+			Depfile: "${out}.d",
+			Deps:    blueprint.DepsGCC,
+		}, "protoBase", "protoFlags", "pkgPathArgs")
 )
 
 func genProto(ctx android.ModuleContext, p *android.ProtoProperties,
@@ -51,15 +53,19 @@
 		protoBase = strings.TrimSuffix(protoFile.String(), protoFile.Rel())
 	}
 
+	var pkgPathArgs string
+	if pkgPath != "" {
+		pkgPathArgs = "-P " + pkgPath
+	}
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        proto,
 		Description: "protoc " + protoFile.Rel(),
 		Output:      srcJarFile,
 		Input:       protoFile,
 		Args: map[string]string{
-			"protoBase":  protoBase,
-			"protoFlags": strings.Join(protoFlags, " "),
-			"pkgPath":    pkgPath,
+			"protoBase":   protoBase,
+			"protoFlags":  strings.Join(protoFlags, " "),
+			"pkgPathArgs": pkgPathArgs,
 		},
 	})
 
diff --git a/python/python.go b/python/python.go
index a277988..feb17da 100644
--- a/python/python.go
+++ b/python/python.go
@@ -63,8 +63,7 @@
 	// files of the current module.
 	// eg. Pkg_path = "a/b/c"; Other packages can reference this module by using
 	// (from a.b.c import ...) statement.
-	// if left unspecified, all the source/data files of current module are copied to
-	// "runfiles/" tree directory directly.
+	// if left unspecified, all the source/data files path is unchanged within zip file.
 	Pkg_path *string `android:"arch_variant"`
 
 	// true, if the Python module is used internally, eg, Python std libs.
@@ -206,7 +205,7 @@
 var (
 	pythonLibTag       = dependencyTag{name: "pythonLib"}
 	launcherTag        = dependencyTag{name: "launcher"}
-	pyIdentifierRegexp = regexp.MustCompile(`^([a-z]|[A-Z]|_)([a-z]|[A-Z]|[0-9]|_)*$`)
+	pyIdentifierRegexp = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_-]*$`)
 	pyExt              = ".py"
 	protoExt           = ".proto"
 	pyVersion2         = "PY2"
@@ -215,7 +214,6 @@
 	mainFileName       = "__main__.py"
 	entryPointFile     = "entry_point.txt"
 	parFileExt         = ".zip"
-	runFiles           = "runfiles"
 	internal           = "internal"
 )
 
@@ -417,19 +415,11 @@
 			return
 		}
 		if p.properties.Is_internal != nil && *p.properties.Is_internal {
-			// pkg_path starts from "internal/" implicitly.
 			pkgPath = filepath.Join(internal, pkgPath)
-		} else {
-			// pkg_path starts from "runfiles/" implicitly.
-			pkgPath = filepath.Join(runFiles, pkgPath)
 		}
 	} else {
 		if p.properties.Is_internal != nil && *p.properties.Is_internal {
-			// pkg_path starts from "runfiles/" implicitly.
 			pkgPath = internal
-		} else {
-			// pkg_path starts from "runfiles/" implicitly.
-			pkgPath = runFiles
 		}
 	}
 
@@ -520,7 +510,9 @@
 		sort.Strings(keys)
 
 		parArgs := []string{}
-		parArgs = append(parArgs, `-P `+pkgPath)
+		if pkgPath != "" {
+			parArgs = append(parArgs, `-P `+pkgPath)
+		}
 		implicits := android.Paths{}
 		for _, k := range keys {
 			parArgs = append(parArgs, `-C `+k)
@@ -582,39 +574,46 @@
 		destToPyData[path.dest] = path.src.String()
 	}
 
+	seen := make(map[android.Module]bool)
+
 	// visit all its dependencies in depth first.
-	ctx.VisitDepsDepthFirst(func(module android.Module) {
-		if ctx.OtherModuleDependencyTag(module) != pythonLibTag {
-			return
+	ctx.WalkDeps(func(child, parent android.Module) bool {
+		if ctx.OtherModuleDependencyTag(child) != pythonLibTag {
+			return false
 		}
+		if seen[child] {
+			return false
+		}
+		seen[child] = true
 		// Python modules only can depend on Python libraries.
-		if !isPythonLibModule(module) {
+		if !isPythonLibModule(child) {
 			panic(fmt.Errorf(
 				"the dependency %q of module %q is not Python library!",
-				ctx.ModuleName(), ctx.OtherModuleName(module)))
+				ctx.ModuleName(), ctx.OtherModuleName(child)))
 		}
-		if dep, ok := module.(PythonDependency); ok {
+		if dep, ok := child.(PythonDependency); ok {
 			srcs := dep.GetSrcsPathMappings()
 			for _, path := range srcs {
 				if !fillInMap(ctx, destToPySrcs,
-					path.dest, path.src.String(), ctx.ModuleName(), ctx.OtherModuleName(module)) {
+					path.dest, path.src.String(), ctx.ModuleName(), ctx.OtherModuleName(child)) {
 					continue
 				}
 			}
 			data := dep.GetDataPathMappings()
 			for _, path := range data {
 				fillInMap(ctx, destToPyData,
-					path.dest, path.src.String(), ctx.ModuleName(), ctx.OtherModuleName(module))
+					path.dest, path.src.String(), ctx.ModuleName(), ctx.OtherModuleName(child))
 			}
 			p.depsSrcsZips = append(p.depsSrcsZips, dep.GetSrcsZip())
 		}
+		return true
 	})
 }
 
 func fillInMap(ctx android.ModuleContext, m map[string]string,
 	key, value, curModule, otherModule string) bool {
 	if oldValue, found := m[key]; found {
-		ctx.ModuleErrorf("found two files to be placed at the same runfiles location %q."+
+		ctx.ModuleErrorf("found two files to be placed at the same location within zip %q."+
 			" First file: in module %s at path %q."+
 			" Second file: in module %s at path %q.",
 			key, curModule, oldValue, otherModule, value)
diff --git a/python/python_test.go b/python/python_test.go
index 60a1c82..e5fe126 100644
--- a/python/python_test.go
+++ b/python/python_test.go
@@ -44,7 +44,7 @@
 	badIdentifierErrTemplate = moduleVariantErrTemplate +
 		"srcs: the path %q contains invalid token %q."
 	dupRunfileErrTemplate = moduleVariantErrTemplate +
-		"found two files to be placed at the same runfiles location %q." +
+		"found two files to be placed at the same location within zip %q." +
 		" First file: in module %s at path %q." +
 		" Second file: in module %s at path %q."
 	noSrcFileErr      = moduleVariantErrTemplate + "doesn't have any source files!"
@@ -175,11 +175,11 @@
 			},
 			errors: []string{
 				fmt.Sprintf(badIdentifierErrTemplate, "dir/Blueprints:4:11",
-					"lib1", "PY3", "runfiles/a/b/c/-e/f/file1.py", "-e"),
+					"lib1", "PY3", "a/b/c/-e/f/file1.py", "-e"),
 				fmt.Sprintf(badIdentifierErrTemplate, "dir/Blueprints:4:11",
-					"lib1", "PY3", "runfiles/a/b/c/.file1.py", ".file1"),
+					"lib1", "PY3", "a/b/c/.file1.py", ".file1"),
 				fmt.Sprintf(badIdentifierErrTemplate, "dir/Blueprints:4:11",
-					"lib1", "PY3", "runfiles/a/b/c/123/file1.py", "123"),
+					"lib1", "PY3", "a/b/c/123/file1.py", "123"),
 			},
 		},
 		{
@@ -212,7 +212,7 @@
 			},
 			errors: []string{
 				fmt.Sprintf(dupRunfileErrTemplate, "dir/Blueprints:9:6",
-					"lib2", "PY3", "runfiles/a/b/c/file1.py", "lib2", "dir/file1.py",
+					"lib2", "PY3", "a/b/c/file1.py", "lib2", "dir/file1.py",
 					"lib1", "dir/c/file1.py"),
 			},
 		},
@@ -307,10 +307,10 @@
 					name:          "bin",
 					actualVersion: "PY3",
 					pyRunfiles: []string{
-						"runfiles/e/default.py",
-						"runfiles/e/bin.py",
-						"runfiles/e/default_py3.py",
-						"runfiles/e/file4.py",
+						"e/default.py",
+						"e/bin.py",
+						"e/default_py3.py",
+						"e/file4.py",
 					},
 					srcsZip: "@prefix@/.intermediates/dir/bin/PY3/bin.py.srcszip",
 					depsSrcsZips: []string{
diff --git a/python/scripts/stub_template_host.txt b/python/scripts/stub_template_host.txt
index b90a28b..e686211 100644
--- a/python/scripts/stub_template_host.txt
+++ b/python/scripts/stub_template_host.txt
@@ -11,15 +11,12 @@
 PYTHON_BINARY = '%interpreter%'
 MAIN_FILE = '%main%'
 PYTHON_PATH = 'PYTHONPATH'
-ZIP_RUNFILES_DIRECTORY_NAME = 'runfiles'
 
 def SearchPathEnv(name):
   search_path = os.getenv('PATH', os.defpath).split(os.pathsep)
   for directory in search_path:
     if directory == '': continue
     path = os.path.join(directory, name)
-    if os.path.islink(path):
-      path = os.path.realpath(path)
     # Check if path is actual executable file.
     if os.path.isfile(path) and os.access(path, os.X_OK):
       return path
@@ -38,7 +35,7 @@
   temp_dir = tempfile.mkdtemp("", "Soong.python_")
   zf = zipfile.ZipFile(os.path.dirname(__file__))
   zf.extractall(temp_dir)
-  return os.path.join(temp_dir, ZIP_RUNFILES_DIRECTORY_NAME)
+  return temp_dir
 
 def Main():
   args = sys.argv[1:]
@@ -85,7 +82,7 @@
   except:
     raise
   finally:
-    shutil.rmtree(os.path.dirname(runfiles_path), True)
+    shutil.rmtree(runfiles_path, True)
 
 if __name__ == '__main__':
   Main()
diff --git a/scripts/build-ndk-prebuilts.sh b/scripts/build-ndk-prebuilts.sh
index d150451..e3552a0 100755
--- a/scripts/build-ndk-prebuilts.sh
+++ b/scripts/build-ndk-prebuilts.sh
@@ -48,7 +48,7 @@
     "Platform_sdk_version": ${PLATFORM_SDK_VERSION},
     "Platform_version_active_codenames": ${PLATFORM_VERSION_ALL_CODENAMES},
 
-    "DeviceName": "flounder",
+    "DeviceName": "generic_arm64",
     "DeviceArch": "arm64",
     "DeviceArchVariant": "armv8-a",
     "DeviceCpuVariant": "denver64",
diff --git a/scripts/gen-kotlin-build-file.sh b/scripts/gen-kotlin-build-file.sh
index f077a0c..1e03f72 100755
--- a/scripts/gen-kotlin-build-file.sh
+++ b/scripts/gen-kotlin-build-file.sh
@@ -16,7 +16,7 @@
 
 # Generates kotlinc module xml file to standard output based on rsp files
 
-if [ -z "$1" ]; then
+if [[ -z "$1" ]]; then
   echo "usage: $0 <classpath> <outDir> <rspFiles>..." >&2
   exit 1
 fi
@@ -30,24 +30,36 @@
 out_dir=$2
 shift 2
 
-# Path in the build file are relative to the build file, we need to make them absolute.
-prefix=`pwd`
+# Path in the build file may be relative to the build file, we need to make them
+# absolute
+prefix="$(pwd)"
+
+get_abs_path () {
+  local file="$1"
+  if [[ "${file:0:1}" == '/' ]] ; then
+    echo "${file}"
+  else
+    echo "${prefix}/${file}"
+  fi
+}
 
 # Print preamble
 echo "<modules><module name=\"name\" type=\"java-production\" outputDir=\"${out_dir}\">"
 
 # Print classpath entries
-for file in $(echo $classpath | tr ":" "\n"); do
-  echo "  <classpath path=\"${prefix}/${file}\"/>"
+for file in $(echo "$classpath" | tr ":" "\n"); do
+  path="$(get_abs_path "$file")"
+  echo "  <classpath path=\"${path}\"/>"
 done
 
 # For each rsp file, print source entries
 while (( "$#" )); do
-  for file in $(cat $1); do
+  for file in $(cat "$1"); do
+    path="$(get_abs_path "$file")"
     if [[ $file == *.java ]]; then
-      echo "  <javaSourceRoots path=\"${prefix}/${file}\"/>"
+      echo "  <javaSourceRoots path=\"${path}\"/>"
     elif [[ $file == *.kt ]]; then
-      echo "  <sources path=\"${prefix}/${file}\"/>"
+      echo "  <sources path=\"${path}\"/>"
     else
       echo "Unknown source file type ${file}"
       exit 1
diff --git a/scripts/manifest_fixer.py b/scripts/manifest_fixer.py
new file mode 100755
index 0000000..6af0ca9
--- /dev/null
+++ b/scripts/manifest_fixer.py
@@ -0,0 +1,247 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# 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.
+#
+"""A tool for inserting values from the build system into a manifest."""
+
+from __future__ import print_function
+import argparse
+import sys
+from xml.dom import minidom
+
+
+android_ns = 'http://schemas.android.com/apk/res/android'
+
+
+def get_children_with_tag(parent, tag_name):
+  children = []
+  for child in  parent.childNodes:
+    if child.nodeType == minidom.Node.ELEMENT_NODE and \
+       child.tagName == tag_name:
+      children.append(child)
+  return children
+
+
+def find_child_with_attribute(element, tag_name, namespace_uri,
+                              attr_name, value):
+  for child in get_children_with_tag(element, tag_name):
+    attr = child.getAttributeNodeNS(namespace_uri, attr_name)
+    if attr is not None and attr.value == value:
+      return child
+  return None
+
+
+def parse_args():
+  """Parse commandline arguments."""
+
+  parser = argparse.ArgumentParser()
+  parser.add_argument('--minSdkVersion', default='', dest='min_sdk_version',
+                      help='specify minSdkVersion used by the build system')
+  parser.add_argument('--uses-library', dest='uses_libraries', action='append',
+                      help='specify additional <uses-library> tag to add')
+  parser.add_argument('input', help='input AndroidManifest.xml file')
+  parser.add_argument('output', help='output AndroidManifest.xml file')
+  return parser.parse_args()
+
+
+def parse_manifest(doc):
+  """Get the manifest element."""
+
+  manifest = doc.documentElement
+  if manifest.tagName != 'manifest':
+    raise RuntimeError('expected manifest tag at root')
+  return manifest
+
+
+def ensure_manifest_android_ns(doc):
+  """Make sure the manifest tag defines the android namespace."""
+
+  manifest = parse_manifest(doc)
+
+  ns = manifest.getAttributeNodeNS(minidom.XMLNS_NAMESPACE, 'android')
+  if ns is None:
+    attr = doc.createAttributeNS(minidom.XMLNS_NAMESPACE, 'xmlns:android')
+    attr.value = android_ns
+    manifest.setAttributeNode(attr)
+  elif ns.value != android_ns:
+    raise RuntimeError('manifest tag has incorrect android namespace ' +
+                       ns.value)
+
+
+def as_int(s):
+  try:
+    i = int(s)
+  except ValueError:
+    return s, False
+  return i, True
+
+
+def compare_version_gt(a, b):
+  """Compare two SDK versions.
+
+  Compares a and b, treating codenames like 'Q' as higher
+  than numerical versions like '28'.
+
+  Returns True if a > b
+
+  Args:
+    a: value to compare
+    b: value to compare
+  Returns:
+    True if a is a higher version than b
+  """
+
+  a, a_is_int = as_int(a.upper())
+  b, b_is_int = as_int(b.upper())
+
+  if a_is_int == b_is_int:
+    # Both are codenames or both are versions, compare directly
+    return a > b
+  else:
+    # One is a codename, the other is not.  Return true if
+    # b is an integer version
+    return b_is_int
+
+
+def get_indent(element, default_level):
+  indent = ''
+  if element is not None and element.nodeType == minidom.Node.TEXT_NODE:
+    text = element.nodeValue
+    indent = text[:len(text)-len(text.lstrip())]
+  if not indent or indent == '\n':
+    # 1 indent = 4 space
+    indent = '\n' + (' ' * default_level * 4)
+  return indent
+
+
+def raise_min_sdk_version(doc, requested):
+  """Ensure the manifest contains a <uses-sdk> tag with a minSdkVersion.
+
+  Args:
+    doc: The XML document.  May be modified by this function.
+    requested: The requested minSdkVersion attribute.
+  Raises:
+    RuntimeError: invalid manifest
+  """
+
+  manifest = parse_manifest(doc)
+
+  # Get or insert the uses-sdk element
+  uses_sdk = get_children_with_tag(manifest, 'uses-sdk')
+  if len(uses_sdk) > 1:
+    raise RuntimeError('found multiple uses-sdk elements')
+  elif len(uses_sdk) == 1:
+    element = uses_sdk[0]
+  else:
+    element = doc.createElement('uses-sdk')
+    indent = get_indent(manifest.firstChild, 1)
+    manifest.insertBefore(element, manifest.firstChild)
+
+    # Insert an indent before uses-sdk to line it up with the indentation of the
+    # other children of the <manifest> tag.
+    manifest.insertBefore(doc.createTextNode(indent), manifest.firstChild)
+
+  # Get or insert the minSdkVersion attribute
+  min_attr = element.getAttributeNodeNS(android_ns, 'minSdkVersion')
+  if min_attr is None:
+    min_attr = doc.createAttributeNS(android_ns, 'android:minSdkVersion')
+    min_attr.value = '1'
+    element.setAttributeNode(min_attr)
+
+  # Update the value of the minSdkVersion attribute if necessary
+  if compare_version_gt(requested, min_attr.value):
+    min_attr.value = requested
+
+
+def add_uses_libraries(doc, new_uses_libraries):
+  """Add additional <uses-library> tags with android:required=true.
+
+  Args:
+    doc: The XML document. May be modified by this function.
+    new_uses_libraries: The names of libraries to be added by this function.
+  Raises:
+    RuntimeError: Invalid manifest
+  """
+
+  manifest = parse_manifest(doc)
+  elems = get_children_with_tag(manifest, 'application')
+  application = elems[0] if len(elems) == 1 else None
+  if len(elems) > 1:
+    raise RuntimeError('found multiple <application> tags')
+  elif not elems:
+    application = doc.createElement('application')
+    indent = get_indent(manifest.firstChild, 1)
+    first = manifest.firstChild
+    manifest.insertBefore(doc.createTextNode(indent), first)
+    manifest.insertBefore(application, first)
+
+  indent = get_indent(application.firstChild, 2)
+
+  last = application.lastChild
+  if last is not None and last.nodeType != minidom.Node.TEXT_NODE:
+    last = None
+
+  for name in new_uses_libraries:
+    if find_child_with_attribute(application, 'uses-library', android_ns,
+                                 'name', name) is not None:
+      # If the uses-library tag of the same 'name' attribute value exists,
+      # respect it.
+      continue
+
+    ul = doc.createElement('uses-library')
+    ul.setAttributeNS(android_ns, 'android:name', name)
+    ul.setAttributeNS(android_ns, 'android:required', 'true')
+
+    application.insertBefore(doc.createTextNode(indent), last)
+    application.insertBefore(ul, last)
+
+  # align the closing tag with the opening tag if it's not
+  # indented
+  if application.lastChild.nodeType != minidom.Node.TEXT_NODE:
+    indent = get_indent(application.previousSibling, 1)
+    application.appendChild(doc.createTextNode(indent))
+
+
+def write_xml(f, doc):
+  f.write('<?xml version="1.0" encoding="utf-8"?>\n')
+  for node in doc.childNodes:
+    f.write(node.toxml(encoding='utf-8') + '\n')
+
+
+def main():
+  """Program entry point."""
+  try:
+    args = parse_args()
+
+    doc = minidom.parse(args.input)
+
+    ensure_manifest_android_ns(doc)
+
+    if args.min_sdk_version:
+      raise_min_sdk_version(doc, args.min_sdk_version)
+
+    if args.uses_libraries:
+      add_uses_libraries(doc, args.uses_libraries)
+
+    with open(args.output, 'wb') as f:
+      write_xml(f, doc)
+
+  # pylint: disable=broad-except
+  except Exception as err:
+    print('error: ' + str(err), file=sys.stderr)
+    sys.exit(-1)
+
+if __name__ == '__main__':
+  main()
diff --git a/scripts/manifest_fixer_test.py b/scripts/manifest_fixer_test.py
new file mode 100755
index 0000000..54a3784
--- /dev/null
+++ b/scripts/manifest_fixer_test.py
@@ -0,0 +1,249 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# 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.
+#
+"""Unit tests for manifest_fixer_test.py."""
+
+import StringIO
+import sys
+import unittest
+from xml.dom import minidom
+
+import manifest_fixer
+
+sys.dont_write_bytecode = True
+
+
+class CompareVersionGtTest(unittest.TestCase):
+  """Unit tests for compare_version_gt function."""
+
+  def test_sdk(self):
+    """Test comparing sdk versions."""
+    self.assertTrue(manifest_fixer.compare_version_gt('28', '27'))
+    self.assertFalse(manifest_fixer.compare_version_gt('27', '28'))
+    self.assertFalse(manifest_fixer.compare_version_gt('28', '28'))
+
+  def test_codename(self):
+    """Test comparing codenames."""
+    self.assertTrue(manifest_fixer.compare_version_gt('Q', 'P'))
+    self.assertFalse(manifest_fixer.compare_version_gt('P', 'Q'))
+    self.assertFalse(manifest_fixer.compare_version_gt('Q', 'Q'))
+
+  def test_sdk_codename(self):
+    """Test comparing sdk versions with codenames."""
+    self.assertTrue(manifest_fixer.compare_version_gt('Q', '28'))
+    self.assertFalse(manifest_fixer.compare_version_gt('28', 'Q'))
+
+  def test_compare_numeric(self):
+    """Test that numbers are compared in numeric and not lexicographic order."""
+    self.assertTrue(manifest_fixer.compare_version_gt('18', '8'))
+
+
+class RaiseMinSdkVersionTest(unittest.TestCase):
+  """Unit tests for raise_min_sdk_version function."""
+
+  def raise_min_sdk_version_test(self, input_manifest, min_sdk_version):
+    doc = minidom.parseString(input_manifest)
+    manifest_fixer.raise_min_sdk_version(doc, min_sdk_version)
+    output = StringIO.StringIO()
+    manifest_fixer.write_xml(output, doc)
+    return output.getvalue()
+
+  manifest_tmpl = (
+      '<?xml version="1.0" encoding="utf-8"?>\n'
+      '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n'
+      '%s'
+      '</manifest>\n')
+
+  def uses_sdk(self, v, extra=''):
+    if extra:
+      extra = ' ' + extra
+    return '    <uses-sdk android:minSdkVersion="%s"%s/>\n' % (v, extra)
+
+  def test_no_uses_sdk(self):
+    """Tests inserting a uses-sdk element into a manifest."""
+
+    manifest_input = self.manifest_tmpl % ''
+    expected = self.manifest_tmpl % self.uses_sdk('28')
+    output = self.raise_min_sdk_version_test(manifest_input, '28')
+    self.assertEqual(output, expected)
+
+  def test_no_min(self):
+    """Tests inserting a minSdkVersion attribute into a uses-sdk element."""
+
+    manifest_input = self.manifest_tmpl % '    <uses-sdk extra="foo"/>\n'
+    expected = self.manifest_tmpl % self.uses_sdk('28', 'extra="foo"')
+    output = self.raise_min_sdk_version_test(manifest_input, '28')
+    self.assertEqual(output, expected)
+
+  def test_raise_min(self):
+    """Tests inserting a minSdkVersion attribute into a uses-sdk element."""
+
+    manifest_input = self.manifest_tmpl % self.uses_sdk('27')
+    expected = self.manifest_tmpl % self.uses_sdk('28')
+    output = self.raise_min_sdk_version_test(manifest_input, '28')
+    self.assertEqual(output, expected)
+
+  def test_raise(self):
+    """Tests raising a minSdkVersion attribute."""
+
+    manifest_input = self.manifest_tmpl % self.uses_sdk('27')
+    expected = self.manifest_tmpl % self.uses_sdk('28')
+    output = self.raise_min_sdk_version_test(manifest_input, '28')
+    self.assertEqual(output, expected)
+
+  def test_no_raise_min(self):
+    """Tests a minSdkVersion that doesn't need raising."""
+
+    manifest_input = self.manifest_tmpl % self.uses_sdk('28')
+    expected = manifest_input
+    output = self.raise_min_sdk_version_test(manifest_input, '27')
+    self.assertEqual(output, expected)
+
+  def test_raise_codename(self):
+    """Tests raising a minSdkVersion attribute to a codename."""
+
+    manifest_input = self.manifest_tmpl % self.uses_sdk('28')
+    expected = self.manifest_tmpl % self.uses_sdk('P')
+    output = self.raise_min_sdk_version_test(manifest_input, 'P')
+    self.assertEqual(output, expected)
+
+  def test_no_raise_codename(self):
+    """Tests a minSdkVersion codename that doesn't need raising."""
+
+    manifest_input = self.manifest_tmpl % self.uses_sdk('P')
+    expected = manifest_input
+    output = self.raise_min_sdk_version_test(manifest_input, '28')
+    self.assertEqual(output, expected)
+
+  def test_extra(self):
+    """Tests that extra attributes and elements are maintained."""
+
+    manifest_input = self.manifest_tmpl % (
+        '    <!-- comment -->\n'
+        '    <uses-sdk android:minSdkVersion="27" extra="foo"/>\n'
+        '    <application/>\n')
+
+    expected = self.manifest_tmpl % (
+        '    <!-- comment -->\n'
+        '    <uses-sdk android:minSdkVersion="28" extra="foo"/>\n'
+        '    <application/>\n')
+
+    output = self.raise_min_sdk_version_test(manifest_input, '28')
+
+    self.assertEqual(output, expected)
+
+  def test_indent(self):
+    """Tests that an inserted element copies the existing indentation."""
+
+    manifest_input = self.manifest_tmpl % '  <!-- comment -->\n'
+
+    expected = self.manifest_tmpl % (
+        '  <uses-sdk android:minSdkVersion="28"/>\n'
+        '  <!-- comment -->\n')
+
+    output = self.raise_min_sdk_version_test(manifest_input, '28')
+
+    self.assertEqual(output, expected)
+
+
+class AddUsesLibrariesTest(unittest.TestCase):
+  """Unit tests for add_uses_libraries function."""
+
+  def run_test(self, input_manifest, new_uses_libraries):
+    doc = minidom.parseString(input_manifest)
+    manifest_fixer.add_uses_libraries(doc, new_uses_libraries)
+    output = StringIO.StringIO()
+    manifest_fixer.write_xml(output, doc)
+    return output.getvalue()
+
+  manifest_tmpl = (
+      '<?xml version="1.0" encoding="utf-8"?>\n'
+      '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n'
+      '    <application>\n'
+      '%s'
+      '    </application>\n'
+      '</manifest>\n')
+
+  def uses_libraries(self, name_required_pairs):
+    ret = ''
+    for name, required in name_required_pairs:
+      ret += (
+          '        <uses-library android:name="%s" android:required="%s"/>\n'
+      ) % (name, required)
+
+    return ret
+
+  def test_empty(self):
+    """Empty new_uses_libraries must not touch the manifest."""
+    manifest_input = self.manifest_tmpl % self.uses_libraries([
+        ('foo', 'true'),
+        ('bar', 'false')])
+    expected = manifest_input
+    output = self.run_test(manifest_input, [])
+    self.assertEqual(output, expected)
+
+  def test_not_overwrite(self):
+    """new_uses_libraries must not overwrite existing tags."""
+    manifest_input = self.manifest_tmpl % self.uses_libraries([
+        ('foo', 'true'),
+        ('bar', 'false')])
+    expected = manifest_input
+    output = self.run_test(manifest_input, ['foo', 'bar'])
+    self.assertEqual(output, expected)
+
+  def test_add(self):
+    """New names are added with 'required:true'."""
+    manifest_input = self.manifest_tmpl % self.uses_libraries([
+        ('foo', 'true'),
+        ('bar', 'false')])
+    expected = self.manifest_tmpl % self.uses_libraries([
+        ('foo', 'true'),
+        ('bar', 'false'),
+        ('baz', 'true'),
+        ('qux', 'true')])
+    output = self.run_test(manifest_input, ['bar', 'baz', 'qux'])
+    self.assertEqual(output, expected)
+
+  def test_no_application(self):
+    """When there is no <application> tag, the tag is added."""
+    manifest_input = (
+        '<?xml version="1.0" encoding="utf-8"?>\n'
+        '<manifest xmlns:android='
+        '"http://schemas.android.com/apk/res/android">\n'
+        '</manifest>\n')
+    expected = self.manifest_tmpl % self.uses_libraries([
+        ('foo', 'true'),
+        ('bar', 'true')])
+    output = self.run_test(manifest_input, ['foo', 'bar'])
+    self.assertEqual(output, expected)
+
+  def test_empty_application(self):
+    """Even when here is an empty <application/> tag, the libs are added."""
+    manifest_input = (
+        '<?xml version="1.0" encoding="utf-8"?>\n'
+        '<manifest xmlns:android='
+        '"http://schemas.android.com/apk/res/android">\n'
+        '    <application/>\n'
+        '</manifest>\n')
+    expected = self.manifest_tmpl % self.uses_libraries([
+        ('foo', 'true'),
+        ('bar', 'true')])
+    output = self.run_test(manifest_input, ['foo', 'bar'])
+    self.assertEqual(output, expected)
+
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/scripts/microfactory.bash b/scripts/microfactory.bash
index 65ba55d..4bb6058 100644
--- a/scripts/microfactory.bash
+++ b/scripts/microfactory.bash
@@ -59,7 +59,7 @@
     BUILDDIR=$(getoutdir) \
       SRCDIR=${TOP} \
       BLUEPRINTDIR=${TOP}/build/blueprint \
-      EXTRA_ARGS="-pkg-path android/soong=${TOP}/build/soong" \
+      EXTRA_ARGS="-pkg-path android/soong=${TOP}/build/soong -pkg-path github.com/golang/protobuf=${TOP}/external/golang-protobuf" \
       build_go $@
 }
 
diff --git a/scripts/strip.sh b/scripts/strip.sh
index 318c7ad..432582f 100755
--- a/scripts/strip.sh
+++ b/scripts/strip.sh
@@ -17,15 +17,17 @@
 # Script to handle the various ways soong may need to strip binaries
 # Inputs:
 #  Environment:
+#   CLANG_BIN: path to the clang bin directory
 #   CROSS_COMPILE: prefix added to readelf, objcopy tools
 #   XZ: path to the xz binary
 #  Arguments:
 #   -i ${file}: input file (required)
 #   -o ${file}: output file (required)
 #   -d ${file}: deps file (required)
-#   --keep-symbols
-#   --keep-mini-debug-info
 #   --add-gnu-debuglink
+#   --keep-mini-debug-info
+#   --keep-symbols
+#   --use-llvm-strip
 
 OPTSTRING=d:i:o:-:
 
@@ -33,25 +35,52 @@
     cat <<EOF
 Usage: strip.sh [options] -i in-file -o out-file -d deps-file
 Options:
-        --keep-symbols          Keep symbols in out-file
-        --keep-mini-debug-info  Keep compressed debug info in out-file
         --add-gnu-debuglink     Add a gnu-debuglink section to out-file
+        --keep-mini-debug-info  Keep compressed debug info in out-file
+        --keep-symbols          Keep symbols in out-file
+        --use-llvm-strip        Use llvm-{strip,objcopy} instead of strip/objcopy
 EOF
     exit 1
 }
 
+# With --use-llvm-strip, GNU strip is replaced with llvm-strip to work around
+# old GNU strip bug on lld output files, b/80093681.
+# Similary, calls to objcopy are replaced with llvm-objcopy,
+# with some exceptions.
+
 do_strip() {
-    "${CROSS_COMPILE}strip" --strip-all "${infile}" -o "${outfile}.tmp"
+    # ${CROSS_COMPILE}strip --strip-all does not strip .ARM.attributes,
+    # so we tell llvm-strip to keep it too.
+    if [ ! -z "${use_llvm_strip}" ]; then
+        "${CLANG_BIN}/llvm-strip" --strip-all -keep=.ARM.attributes "${infile}" "${outfile}.tmp"
+    else
+        "${CROSS_COMPILE}strip" --strip-all "${infile}" -o "${outfile}.tmp"
+    fi
 }
 
 do_strip_keep_symbols() {
-    "${CROSS_COMPILE}objcopy" "${infile}" "${outfile}.tmp" \
-	`"${CROSS_COMPILE}readelf" -S "${infile}" | awk '/.debug_/ {print "-R " $2}' | xargs`
+    # Maybe we should replace this objcopy with llvm-objcopy, but
+    # we have not found a use case that is broken by objcopy yet.
+    REMOVE_SECTIONS=`"${CROSS_COMPILE}readelf" -S "${infile}" | awk '/.debug_/ {print "--remove-section " $2}' | xargs`
+    if [ ! -z "${use_llvm_strip}" ]; then
+        "${CROSS_COMPILE}objcopy" "${infile}" "${outfile}.tmp" ${REMOVE_SECTIONS}
+    else
+        "${CLANG_BIN}/llvm-objcopy" "${infile}" "${outfile}.tmp" ${REMOVE_SECTIONS}
+    fi
 }
 
 do_strip_keep_mini_debug_info() {
     rm -f "${outfile}.dynsyms" "${outfile}.funcsyms" "${outfile}.keep_symbols" "${outfile}.debug" "${outfile}.mini_debuginfo" "${outfile}.mini_debuginfo.xz"
-    if "${CROSS_COMPILE}strip" --strip-all -R .comment "${infile}" -o "${outfile}.tmp"; then
+    if [ ! -z "${use_llvm_strip}" ]; then
+        "${CLANG_BIN}/llvm-strip" --strip-all -keep=.ARM.attributes -remove-section=.comment "${infile}" "${outfile}.tmp"
+    else
+        "${CROSS_COMPILE}strip" --strip-all -R .comment "${infile}" -o "${outfile}.tmp"
+    fi
+    if [ "$?" == "0" ]; then
+        # Current prebult llvm-objcopy does not support the following flags:
+        #    --only-keep-debug --rename-section --keep-symbols
+        # For the following use cases, ${CROSS_COMPILE}objcopy does fine with lld linked files,
+        # except the --add-section flag.
         "${CROSS_COMPILE}objcopy" --only-keep-debug "${infile}" "${outfile}.debug"
         "${CROSS_COMPILE}nm" -D "${infile}" --format=posix --defined-only | awk '{ print $$1 }' | sort >"${outfile}.dynsyms"
         "${CROSS_COMPILE}nm" "${infile}" --format=posix --defined-only | awk '{ if ($$2 == "T" || $$2 == "t" || $$2 == "D") print $$1 }' | sort > "${outfile}.funcsyms"
@@ -61,14 +90,22 @@
         "${CROSS_COMPILE}objcopy" -S --remove-section .gdb_index --remove-section .comment --keep-symbols="${outfile}.keep_symbols" "${outfile}.mini_debuginfo"
         "${CROSS_COMPILE}objcopy" --rename-section saved_debug_frame=.debug_frame "${outfile}.mini_debuginfo"
         "${XZ}" "${outfile}.mini_debuginfo"
-        "${CROSS_COMPILE}objcopy" --add-section .gnu_debugdata="${outfile}.mini_debuginfo.xz" "${outfile}.tmp"
+        if [ ! -z "${use_llvm_strip}" ]; then
+            "${CLANG_BIN}/llvm-objcopy" --add-section .gnu_debugdata="${outfile}.mini_debuginfo.xz" "${outfile}.tmp"
+        else
+            "${CROSS_COMPILE}objcopy" --add-section .gnu_debugdata="${outfile}.mini_debuginfo.xz" "${outfile}.tmp"
+        fi
     else
         cp -f "${infile}" "${outfile}.tmp"
     fi
 }
 
 do_add_gnu_debuglink() {
-    "${CROSS_COMPILE}objcopy" --add-gnu-debuglink="${infile}" "${outfile}.tmp"
+    if [ ! -z "${use_llvm_strip}" ]; then
+        "${CLANG_BIN}/llvm-objcopy" --add-gnu-debuglink="${infile}" "${outfile}.tmp"
+    else
+        "${CROSS_COMPILE}objcopy" --add-gnu-debuglink="${infile}" "${outfile}.tmp"
+    fi
 }
 
 while getopts $OPTSTRING opt; do
@@ -78,9 +115,10 @@
 	o) outfile="${OPTARG}" ;;
 	-)
 	    case "${OPTARG}" in
-		keep-symbols) keep_symbols=true ;;
-		keep-mini-debug-info) keep_mini_debug_info=true ;;
 		add-gnu-debuglink) add_gnu_debuglink=true ;;
+		keep-mini-debug-info) keep_mini_debug_info=true ;;
+		keep-symbols) keep_symbols=true ;;
+		use-llvm-strip) use_llvm_strip=true ;;
 		*) echo "Unknown option --${OPTARG}"; usage ;;
 	    esac;;
 	?) usage ;;
@@ -130,12 +168,18 @@
 rm -f "${outfile}"
 mv "${outfile}.tmp" "${outfile}"
 
+if [ ! -z "${use_llvm_strip}" ]; then
+  USED_STRIP_OBJCOPY="${CLANG_BIN}/llvm-strip ${CLANG_BIN}/llvm-objcopy"
+else
+  USED_STRIP_OBJCOPY="${CROSS_COMPILE}strip"
+fi
+
 cat <<EOF > "${depsfile}"
 ${outfile}: \
   ${infile} \
   ${CROSS_COMPILE}nm \
   ${CROSS_COMPILE}objcopy \
   ${CROSS_COMPILE}readelf \
-  ${CROSS_COMPILE}strip
+  ${USED_STRIP_OBJCOPY}
 
 EOF
diff --git a/scripts/toc.sh b/scripts/toc.sh
index 7b2224c..bd6425b 100755
--- a/scripts/toc.sh
+++ b/scripts/toc.sh
@@ -39,8 +39,8 @@
 }
 
 do_macho() {
-    otool -l "${infile}" | grep LC_ID_DYLIB -A 5 > "${outfile}.tmp"
-    nm -gP "${infile}" | cut -f1-2 -d" " | grep -v 'U$' >> "${outfile}.tmp"
+    "${CROSS_COMPILE}/otool" -l "${infile}" | grep LC_ID_DYLIB -A 5 > "${outfile}.tmp"
+    "${CROSS_COMPILE}/nm" -gP "${infile}" | cut -f1-2 -d" " | grep -v 'U$' >> "${outfile}.tmp"
 }
 
 
diff --git a/ui/build/Android.bp b/ui/build/Android.bp
index 5809894..a48a314 100644
--- a/ui/build/Android.bp
+++ b/ui/build/Android.bp
@@ -13,10 +13,25 @@
 // limitations under the License.
 
 bootstrap_go_package {
+    name: "soong-ui-build-paths",
+    pkgPath: "android/soong/ui/build/paths",
+    srcs: [
+        "paths/config.go",
+        "paths/logs.go",
+    ],
+    testSrcs: [
+        "paths/logs_test.go",
+    ],
+}
+
+bootstrap_go_package {
     name: "soong-ui-build",
     pkgPath: "android/soong/ui/build",
     deps: [
+        "soong-ui-build-paths",
         "soong-ui-logger",
+        "soong-ui-status",
+        "soong-ui-terminal",
         "soong-ui-tracer",
         "soong-shared",
         "soong-finder",
@@ -33,6 +48,7 @@
         "finder.go",
         "kati.go",
         "ninja.go",
+        "path.go",
         "proc_sync.go",
         "signal.go",
         "soong.go",
@@ -48,13 +64,11 @@
     darwin: {
         srcs: [
             "sandbox_darwin.go",
-            "util_darwin.go"
         ],
     },
     linux: {
         srcs: [
             "sandbox_linux.go",
-            "util_linux.go"
         ],
     },
 }
diff --git a/ui/build/build.go b/ui/build/build.go
index 78eb6a3..96cfdbb 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -40,7 +40,6 @@
 {{if .HasKatiSuffix}}include {{.KatiNinjaFile}}
 {{end -}}
 include {{.SoongNinjaFile}}
-build {{.CombinedNinjaFile}}: phony {{.SoongNinjaFile}}
 `))
 
 func createCombinedBuildNinjaFile(ctx Context, config Config) {
@@ -106,9 +105,7 @@
 func help(ctx Context, config Config, what int) {
 	cmd := Command(ctx, config, "help.sh", "build/make/help.sh")
 	cmd.Sandbox = dumpvarsSandbox
-	cmd.Stdout = ctx.Stdout()
-	cmd.Stderr = ctx.Stderr()
-	cmd.RunOrFatal()
+	cmd.RunAndPrintOrFatal()
 }
 
 // Build the tree. The 'what' argument can be used to chose which components of
@@ -140,6 +137,8 @@
 
 	ensureEmptyDirectoriesExist(ctx, config.TempDir())
 
+	SetupPath(ctx, config)
+
 	if what&BuildProductConfig != 0 {
 		// Run make for product config
 		runMakeProductConfig(ctx, config)
diff --git a/ui/build/cleanbuild.go b/ui/build/cleanbuild.go
index f2de2cd..24a8c7a 100644
--- a/ui/build/cleanbuild.go
+++ b/ui/build/cleanbuild.go
@@ -107,6 +107,7 @@
 		productOut("system"),
 		productOut("system_other"),
 		productOut("vendor"),
+		productOut("product"),
 		productOut("oem"),
 		productOut("obj/FAKE"),
 		productOut("breakpad"),
diff --git a/ui/build/config.go b/ui/build/config.go
index 5622dff..1f7656e 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -50,7 +50,10 @@
 	targetDevice    string
 	targetDeviceDir string
 
+	pdkBuild       bool
 	brokenDupRules bool
+
+	pathReplaced bool
 }
 
 const srcDirFileCheck = "build/soong/root.bp"
@@ -115,6 +118,18 @@
 
 		// Set in envsetup.sh, reset in makefiles
 		"ANDROID_JAVA_TOOLCHAIN",
+
+		// Set by envsetup.sh, but shouldn't be used inside the build because envsetup.sh is optional
+		"ANDROID_BUILD_TOP",
+		"ANDROID_HOST_OUT",
+		"ANDROID_PRODUCT_OUT",
+		"ANDROID_HOST_OUT_TESTCASES",
+		"ANDROID_TARGET_OUT_TESTCASES",
+		"ANDROID_TOOLCHAIN",
+		"ANDROID_TOOLCHAIN_2ND_ARCH",
+		"ANDROID_DEV_SCRIPTS",
+		"ANDROID_EMULATOR_PREBUILTS",
+		"ANDROID_PRE_BUILD_PATHS",
 	)
 
 	// Tell python not to spam the source tree with .pyc files.
@@ -161,19 +176,7 @@
 		if override, ok := ret.environ.Get("OVERRIDE_ANDROID_JAVA_HOME"); ok {
 			return override
 		}
-		v, ok := ret.environ.Get("EXPERIMENTAL_USE_OPENJDK9")
-		if !ok {
-			v2, ok2 := ret.environ.Get("RUN_ERROR_PRONE")
-			if ok2 && (v2 == "true") {
-				v = "false"
-			} else {
-				v = "1.8"
-			}
-		}
-		if v != "false" {
-			return java9Home
-		}
-		return java8Home
+		return java9Home
 	}()
 	absJavaHome := absPath(ctx, javaHome)
 
@@ -577,3 +580,11 @@
 func (c *configImpl) TargetDeviceDir() string {
 	return c.targetDeviceDir
 }
+
+func (c *configImpl) SetPdkBuild(pdk bool) {
+	c.pdkBuild = pdk
+}
+
+func (c *configImpl) IsPdkBuild() bool {
+	return c.pdkBuild
+}
diff --git a/ui/build/config_test.go b/ui/build/config_test.go
index e4eab94..242e3af 100644
--- a/ui/build/config_test.go
+++ b/ui/build/config_test.go
@@ -22,13 +22,14 @@
 	"testing"
 
 	"android/soong/ui/logger"
+	"android/soong/ui/terminal"
 )
 
 func testContext() Context {
 	return Context{&ContextImpl{
-		Context:        context.Background(),
-		Logger:         logger.New(&bytes.Buffer{}),
-		StdioInterface: NewCustomStdio(&bytes.Buffer{}, &bytes.Buffer{}, &bytes.Buffer{}),
+		Context: context.Background(),
+		Logger:  logger.New(&bytes.Buffer{}),
+		Writer:  terminal.NewWriter(terminal.NewCustomStdio(&bytes.Buffer{}, &bytes.Buffer{}, &bytes.Buffer{})),
 	}}
 }
 
diff --git a/ui/build/context.go b/ui/build/context.go
index 0636631..c8b00c3 100644
--- a/ui/build/context.go
+++ b/ui/build/context.go
@@ -16,45 +16,14 @@
 
 import (
 	"context"
-	"io"
-	"os"
-	"time"
 
 	"android/soong/ui/logger"
+	"android/soong/ui/status"
+	"android/soong/ui/terminal"
 	"android/soong/ui/tracer"
 )
 
-type StdioInterface interface {
-	Stdin() io.Reader
-	Stdout() io.Writer
-	Stderr() io.Writer
-}
-
-type StdioImpl struct{}
-
-func (StdioImpl) Stdin() io.Reader  { return os.Stdin }
-func (StdioImpl) Stdout() io.Writer { return os.Stdout }
-func (StdioImpl) Stderr() io.Writer { return os.Stderr }
-
-var _ StdioInterface = StdioImpl{}
-
-type customStdio struct {
-	stdin  io.Reader
-	stdout io.Writer
-	stderr io.Writer
-}
-
-func NewCustomStdio(stdin io.Reader, stdout, stderr io.Writer) StdioInterface {
-	return customStdio{stdin, stdout, stderr}
-}
-
-func (c customStdio) Stdin() io.Reader  { return c.stdin }
-func (c customStdio) Stdout() io.Writer { return c.stdout }
-func (c customStdio) Stderr() io.Writer { return c.stderr }
-
-var _ StdioInterface = customStdio{}
-
-// Context combines a context.Context, logger.Logger, and StdIO redirection.
+// Context combines a context.Context, logger.Logger, and terminal.Writer.
 // These all are agnostic of the current build, and may be used for multiple
 // builds, while the Config objects contain per-build information.
 type Context struct{ *ContextImpl }
@@ -62,7 +31,8 @@
 	context.Context
 	logger.Logger
 
-	StdioInterface
+	Writer terminal.Writer
+	Status *status.Status
 
 	Thread tracer.Thread
 	Tracer tracer.Tracer
@@ -88,28 +58,3 @@
 		c.Tracer.Complete(name, c.Thread, begin, end)
 	}
 }
-
-// ImportNinjaLog imports a .ninja_log file into the tracer.
-func (c ContextImpl) ImportNinjaLog(filename string, startOffset time.Time) {
-	if c.Tracer != nil {
-		c.Tracer.ImportNinjaLog(c.Thread, filename, startOffset)
-	}
-}
-
-func (c ContextImpl) IsTerminal() bool {
-	if term, ok := os.LookupEnv("TERM"); ok {
-		return term != "dumb" && isTerminal(c.Stdout()) && isTerminal(c.Stderr())
-	}
-	return false
-}
-
-func (c ContextImpl) IsErrTerminal() bool {
-	if term, ok := os.LookupEnv("TERM"); ok {
-		return term != "dumb" && isTerminal(c.Stderr())
-	}
-	return false
-}
-
-func (c ContextImpl) TermWidth() (int, bool) {
-	return termWidth(c.Stdout())
-}
diff --git a/ui/build/dumpvars.go b/ui/build/dumpvars.go
index cc9e742..06bd74f 100644
--- a/ui/build/dumpvars.go
+++ b/ui/build/dumpvars.go
@@ -18,6 +18,8 @@
 	"bytes"
 	"fmt"
 	"strings"
+
+	"android/soong/ui/status"
 )
 
 // DumpMakeVars can be used to extract the values of Make variables after the
@@ -60,7 +62,7 @@
 	}
 	cmd.StartOrFatal()
 	// TODO: error out when Stderr contains any content
-	katiRewriteOutput(ctx, pipe)
+	status.KatiReader(ctx.Status.StartTool(), pipe)
 	cmd.WaitOrFatal()
 
 	ret := make(map[string]string, len(vars))
@@ -162,6 +164,11 @@
 
 		// Whether --werror_overriding_commands will work
 		"BUILD_BROKEN_DUP_RULES",
+
+		// Not used, but useful to be in the soong.log
+		"BUILD_BROKEN_ANDROIDMK_EXPORTS",
+		"BUILD_BROKEN_DUP_COPY_HEADERS",
+		"BUILD_BROKEN_PHONY_TARGETS",
 	}, exportEnvVars...), BannerVars...)
 
 	make_vars, err := dumpMakeVars(ctx, config, config.Arguments(), allVars, true)
@@ -170,7 +177,7 @@
 	}
 
 	// Print the banner like make does
-	fmt.Fprintln(ctx.Stdout(), Banner(make_vars))
+	ctx.Writer.Print(Banner(make_vars))
 
 	// Populate the environment
 	env := config.Environment()
@@ -187,5 +194,6 @@
 	config.SetTargetDevice(make_vars["TARGET_DEVICE"])
 	config.SetTargetDeviceDir(make_vars["TARGET_DEVICE_DIR"])
 
+	config.SetPdkBuild(make_vars["TARGET_BUILD_PDK"] == "true")
 	config.SetBuildBrokenDupRules(make_vars["BUILD_BROKEN_DUP_RULES"] != "false")
 }
diff --git a/ui/build/exec.go b/ui/build/exec.go
index 90fb19d..5c312bc 100644
--- a/ui/build/exec.go
+++ b/ui/build/exec.go
@@ -122,3 +122,20 @@
 	c.reportError(err)
 	return ret
 }
+
+// RunAndPrintOrFatal will run the command, then after finishing
+// print any output, then handling any errors with a call to
+// ctx.Fatal
+func (c *Cmd) RunAndPrintOrFatal() {
+	ret, err := c.CombinedOutput()
+	st := c.ctx.Status.StartTool()
+	if len(ret) > 0 {
+		if err != nil {
+			st.Error(string(ret))
+		} else {
+			st.Print(string(ret))
+		}
+	}
+	st.Finish()
+	c.reportError(err)
+}
diff --git a/ui/build/kati.go b/ui/build/kati.go
index 81edd32..7cfa1cf 100644
--- a/ui/build/kati.go
+++ b/ui/build/kati.go
@@ -15,15 +15,14 @@
 package build
 
 import (
-	"bufio"
 	"crypto/md5"
 	"fmt"
-	"io"
 	"io/ioutil"
 	"path/filepath"
-	"regexp"
 	"strconv"
 	"strings"
+
+	"android/soong/ui/status"
 )
 
 var spaceSlashReplacer = strings.NewReplacer("/", "_", " ", "_")
@@ -77,10 +76,17 @@
 		"--color_warnings",
 		"--gen_all_targets",
 		"--werror_find_emulator",
+		"--no_builtin_rules",
+		"--werror_suffix_rules",
 		"--kati_stats",
 		"-f", "build/make/core/main.mk",
 	}
 
+	// PDK builds still uses a few implicit rules
+	if !config.IsPdkBuild() {
+		args = append(args, "--werror_implicit_rules")
+	}
+
 	if !config.BuildBrokenDupRules() {
 		args = append(args, "--werror_overriding_commands")
 	}
@@ -110,77 +116,10 @@
 	cmd.Stderr = cmd.Stdout
 
 	cmd.StartOrFatal()
-	katiRewriteOutput(ctx, pipe)
+	status.KatiReader(ctx.Status.StartTool(), pipe)
 	cmd.WaitOrFatal()
 }
 
-var katiIncludeRe = regexp.MustCompile(`^(\[\d+/\d+] )?including [^ ]+ ...$`)
-var katiLogRe = regexp.MustCompile(`^\*kati\*: `)
-
-func katiRewriteOutput(ctx Context, pipe io.ReadCloser) {
-	haveBlankLine := true
-	smartTerminal := ctx.IsTerminal()
-	errSmartTerminal := ctx.IsErrTerminal()
-
-	scanner := bufio.NewScanner(pipe)
-	for scanner.Scan() {
-		line := scanner.Text()
-		verbose := katiIncludeRe.MatchString(line)
-
-		// Only put kati debug/stat lines in our verbose log
-		if katiLogRe.MatchString(line) {
-			ctx.Verbose(line)
-			continue
-		}
-
-		// For verbose lines, write them on the current line without a newline,
-		// then overwrite them if the next thing we're printing is another
-		// verbose line.
-		if smartTerminal && verbose {
-			// Limit line width to the terminal width, otherwise we'll wrap onto
-			// another line and we won't delete the previous line.
-			//
-			// Run this on every line in case the window has been resized while
-			// we're printing. This could be optimized to only re-run when we
-			// get SIGWINCH if it ever becomes too time consuming.
-			if max, ok := termWidth(ctx.Stdout()); ok {
-				if len(line) > max {
-					// Just do a max. Ninja elides the middle, but that's
-					// more complicated and these lines aren't that important.
-					line = line[:max]
-				}
-			}
-
-			// Move to the beginning on the line, print the output, then clear
-			// the rest of the line.
-			fmt.Fprint(ctx.Stdout(), "\r", line, "\x1b[K")
-			haveBlankLine = false
-			continue
-		} else if smartTerminal && !haveBlankLine {
-			// If we've previously written a verbose message, send a newline to save
-			// that message instead of overwriting it.
-			fmt.Fprintln(ctx.Stdout())
-			haveBlankLine = true
-		} else if !errSmartTerminal {
-			// Most editors display these as garbage, so strip them out.
-			line = string(stripAnsiEscapes([]byte(line)))
-		}
-
-		// Assume that non-verbose lines are important enough for stderr
-		fmt.Fprintln(ctx.Stderr(), line)
-	}
-
-	// Save our last verbose line.
-	if !haveBlankLine {
-		fmt.Fprintln(ctx.Stdout())
-	}
-
-	if err := scanner.Err(); err != nil {
-		ctx.Println("Error from kati parser:", err)
-		io.Copy(ctx.Stderr(), pipe)
-	}
-}
-
 func runKatiCleanSpec(ctx Context, config Config) {
 	ctx.BeginTrace("kati cleanspec")
 	defer ctx.EndTrace()
@@ -213,6 +152,6 @@
 	cmd.Stderr = cmd.Stdout
 
 	cmd.StartOrFatal()
-	katiRewriteOutput(ctx, pipe)
+	status.KatiReader(ctx.Status.StartTool(), pipe)
 	cmd.WaitOrFatal()
 }
diff --git a/ui/build/ninja.go b/ui/build/ninja.go
index 96b5e9d..c48fe0f 100644
--- a/ui/build/ninja.go
+++ b/ui/build/ninja.go
@@ -21,15 +21,21 @@
 	"strconv"
 	"strings"
 	"time"
+
+	"android/soong/ui/status"
 )
 
 func runNinja(ctx Context, config Config) {
 	ctx.BeginTrace("ninja")
 	defer ctx.EndTrace()
 
+	fifo := filepath.Join(config.OutDir(), ".ninja_fifo")
+	status.NinjaReader(ctx, ctx.Status.StartTool(), fifo)
+
 	executable := config.PrebuiltBuildTool("ninja")
 	args := []string{
 		"-d", "keepdepfile",
+		fmt.Sprintf("--frontend=cat <&3 >%s", fifo),
 	}
 
 	args = append(args, config.NinjaArgs()...)
@@ -47,9 +53,6 @@
 
 	args = append(args, "-f", config.CombinedNinjaFile())
 
-	if config.IsVerbose() {
-		args = append(args, "-v")
-	}
 	args = append(args, "-w", "dupbuild=err")
 
 	cmd := Command(ctx, config, "ninja", executable, args...)
@@ -66,13 +69,6 @@
 		cmd.Args = append(cmd.Args, strings.Fields(extra)...)
 	}
 
-	if _, ok := cmd.Environment.Get("NINJA_STATUS"); !ok {
-		cmd.Environment.Set("NINJA_STATUS", "[%p %f/%t] ")
-	}
-
-	cmd.Stdin = ctx.Stdin()
-	cmd.Stdout = ctx.Stdout()
-	cmd.Stderr = ctx.Stderr()
 	logPath := filepath.Join(config.OutDir(), ".ninja_log")
 	ninjaHeartbeatDuration := time.Minute * 5
 	if overrideText, ok := cmd.Environment.Get("NINJA_HEARTBEAT_INTERVAL"); ok {
@@ -99,10 +95,7 @@
 		}
 	}()
 
-	startTime := time.Now()
-	defer ctx.ImportNinjaLog(logPath, startTime)
-
-	cmd.RunOrFatal()
+	cmd.RunAndPrintOrFatal()
 }
 
 type statusChecker struct {
diff --git a/ui/build/path.go b/ui/build/path.go
new file mode 100644
index 0000000..52658ef
--- /dev/null
+++ b/ui/build/path.go
@@ -0,0 +1,149 @@
+// Copyright 2018 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 build
+
+import (
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"strings"
+
+	"github.com/google/blueprint/microfactory"
+
+	"android/soong/ui/build/paths"
+)
+
+func parsePathDir(dir string) []string {
+	f, err := os.Open(dir)
+	if err != nil {
+		return nil
+	}
+	defer f.Close()
+
+	if s, err := f.Stat(); err != nil || !s.IsDir() {
+		return nil
+	}
+
+	infos, err := f.Readdir(-1)
+	if err != nil {
+		return nil
+	}
+
+	ret := make([]string, 0, len(infos))
+	for _, info := range infos {
+		if m := info.Mode(); !m.IsDir() && m&0111 != 0 {
+			ret = append(ret, info.Name())
+		}
+	}
+	return ret
+}
+
+func SetupPath(ctx Context, config Config) {
+	if config.pathReplaced {
+		return
+	}
+
+	ctx.BeginTrace("path")
+	defer ctx.EndTrace()
+
+	origPath, _ := config.Environment().Get("PATH")
+	myPath := filepath.Join(config.OutDir(), ".path")
+	interposer := myPath + "_interposer"
+
+	var cfg microfactory.Config
+	cfg.Map("android/soong", "build/soong")
+	cfg.TrimPath, _ = filepath.Abs(".")
+	if _, err := microfactory.Build(&cfg, interposer, "android/soong/cmd/path_interposer"); err != nil {
+		ctx.Fatalln("Failed to build path interposer:", err)
+	}
+
+	if err := ioutil.WriteFile(interposer+"_origpath", []byte(origPath), 0777); err != nil {
+		ctx.Fatalln("Failed to write original path:", err)
+	}
+
+	entries, err := paths.LogListener(ctx.Context, interposer+"_log")
+	if err != nil {
+		ctx.Fatalln("Failed to listen for path logs:", err)
+	}
+
+	go func() {
+		for log := range entries {
+			curPid := os.Getpid()
+			for i, proc := range log.Parents {
+				if proc.Pid == curPid {
+					log.Parents = log.Parents[i:]
+					break
+				}
+			}
+			procPrints := []string{
+				"See https://android.googlesource.com/platform/build/+/master/Changes.md#PATH_Tools for more information.",
+			}
+			if len(log.Parents) > 0 {
+				procPrints = append(procPrints, "Process tree:")
+				for i, proc := range log.Parents {
+					procPrints = append(procPrints, fmt.Sprintf("%s→ %s", strings.Repeat(" ", i), proc.Command))
+				}
+			}
+
+			config := paths.GetConfig(log.Basename)
+			if config.Error {
+				ctx.Printf("Disallowed PATH tool %q used: %#v", log.Basename, log.Args)
+				for _, line := range procPrints {
+					ctx.Println(line)
+				}
+			} else {
+				ctx.Verbosef("Unknown PATH tool %q used: %#v", log.Basename, log.Args)
+				for _, line := range procPrints {
+					ctx.Verboseln(line)
+				}
+			}
+		}
+	}()
+
+	ensureEmptyDirectoriesExist(ctx, myPath)
+
+	var execs []string
+	for _, pathEntry := range filepath.SplitList(origPath) {
+		if pathEntry == "" {
+			// Ignore the current directory
+			continue
+		}
+		// TODO(dwillemsen): remove path entries under TOP? or anything
+		// that looks like an android source dir? They won't exist on
+		// the build servers, since they're added by envsetup.sh.
+		// (Except for the JDK, which is configured in ui/build/config.go)
+
+		execs = append(execs, parsePathDir(pathEntry)...)
+	}
+
+	allowAllSymlinks := config.Environment().IsEnvTrue("TEMPORARY_DISABLE_PATH_RESTRICTIONS")
+	for _, name := range execs {
+		if !paths.GetConfig(name).Symlink && !allowAllSymlinks {
+			continue
+		}
+
+		err := os.Symlink("../.path_interposer", filepath.Join(myPath, name))
+		// Intentionally ignore existing files -- that means that we
+		// just created it, and the first one should win.
+		if err != nil && !os.IsExist(err) {
+			ctx.Fatalln("Failed to create symlink:", err)
+		}
+	}
+
+	myPath, _ = filepath.Abs(myPath)
+	config.Environment().Set("PATH", myPath)
+	config.pathReplaced = true
+}
diff --git a/ui/build/paths/config.go b/ui/build/paths/config.go
new file mode 100644
index 0000000..4275601
--- /dev/null
+++ b/ui/build/paths/config.go
@@ -0,0 +1,168 @@
+// Copyright 2018 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 paths
+
+import "runtime"
+
+type PathConfig struct {
+	// Whether to create the symlink in the new PATH for this tool.
+	Symlink bool
+
+	// Whether to log about usages of this tool to the soong.log
+	Log bool
+
+	// Whether to exit with an error instead of invoking the underlying tool.
+	Error bool
+}
+
+var Allowed = PathConfig{
+	Symlink: true,
+	Log:     false,
+	Error:   false,
+}
+
+var Forbidden = PathConfig{
+	Symlink: false,
+	Log:     true,
+	Error:   true,
+}
+
+// The configuration used if the tool is not listed in the config below.
+// Currently this will create the symlink, but log a warning. In the future,
+// I expect this to move closer to Forbidden.
+var Missing = PathConfig{
+	Symlink: true,
+	Log:     true,
+	Error:   false,
+}
+
+func GetConfig(name string) PathConfig {
+	if config, ok := Configuration[name]; ok {
+		return config
+	}
+	return Missing
+}
+
+var Configuration = map[string]PathConfig{
+	"awk":       Allowed,
+	"basename":  Allowed,
+	"bash":      Allowed,
+	"bc":        Allowed,
+	"bzip2":     Allowed,
+	"cat":       Allowed,
+	"chmod":     Allowed,
+	"cmp":       Allowed,
+	"comm":      Allowed,
+	"cp":        Allowed,
+	"cut":       Allowed,
+	"date":      Allowed,
+	"dd":        Allowed,
+	"diff":      Allowed,
+	"dirname":   Allowed,
+	"echo":      Allowed,
+	"egrep":     Allowed,
+	"env":       Allowed,
+	"expr":      Allowed,
+	"find":      Allowed,
+	"getconf":   Allowed,
+	"getopt":    Allowed,
+	"git":       Allowed,
+	"grep":      Allowed,
+	"gzip":      Allowed,
+	"head":      Allowed,
+	"hexdump":   Allowed,
+	"hostname":  Allowed,
+	"id":        Allowed,
+	"jar":       Allowed,
+	"java":      Allowed,
+	"javap":     Allowed,
+	"ln":        Allowed,
+	"ls":        Allowed,
+	"m4":        Allowed,
+	"make":      Allowed,
+	"md5sum":    Allowed,
+	"mkdir":     Allowed,
+	"mktemp":    Allowed,
+	"mv":        Allowed,
+	"openssl":   Allowed,
+	"patch":     Allowed,
+	"perl":      Allowed,
+	"pgrep":     Allowed,
+	"pkill":     Allowed,
+	"pstree":    Allowed,
+	"pwd":       Allowed,
+	"python":    Allowed,
+	"python2.7": Allowed,
+	"python3":   Allowed,
+	"readlink":  Allowed,
+	"realpath":  Allowed,
+	"rm":        Allowed,
+	"rmdir":     Allowed,
+	"rsync":     Allowed,
+	"runalarm":  Allowed,
+	"sed":       Allowed,
+	"setsid":    Allowed,
+	"sh":        Allowed,
+	"sha1sum":   Allowed,
+	"sha256sum": Allowed,
+	"sha512sum": Allowed,
+	"sleep":     Allowed,
+	"sort":      Allowed,
+	"stat":      Allowed,
+	"sum":       Allowed,
+	"tar":       Allowed,
+	"tail":      Allowed,
+	"touch":     Allowed,
+	"tr":        Allowed,
+	"true":      Allowed,
+	"uname":     Allowed,
+	"uniq":      Allowed,
+	"unzip":     Allowed,
+	"wc":        Allowed,
+	"which":     Allowed,
+	"whoami":    Allowed,
+	"xargs":     Allowed,
+	"xmllint":   Allowed,
+	"xz":        Allowed,
+	"zip":       Allowed,
+	"zipinfo":   Allowed,
+
+	// Host toolchain is removed. In-tree toolchain should be used instead.
+	// GCC also can't find cc1 with this implementation.
+	"ar":         Forbidden,
+	"as":         Forbidden,
+	"cc":         Forbidden,
+	"clang":      Forbidden,
+	"clang++":    Forbidden,
+	"gcc":        Forbidden,
+	"g++":        Forbidden,
+	"ld":         Forbidden,
+	"ld.bfd":     Forbidden,
+	"ld.gold":    Forbidden,
+	"pkg-config": Forbidden,
+
+	// We've got prebuilts of these
+	//"dtc":  Forbidden,
+	//"lz4":  Forbidden,
+	//"lz4c": Forbidden,
+}
+
+func init() {
+	if runtime.GOOS == "darwin" {
+		Configuration["md5"] = Allowed
+		Configuration["sw_vers"] = Allowed
+		Configuration["xcrun"] = Allowed
+	}
+}
diff --git a/ui/build/paths/logs.go b/ui/build/paths/logs.go
new file mode 100644
index 0000000..6c24968
--- /dev/null
+++ b/ui/build/paths/logs.go
@@ -0,0 +1,211 @@
+// Copyright 2018 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 paths
+
+import (
+	"context"
+	"encoding/gob"
+	"fmt"
+	"io/ioutil"
+	"net"
+	"os"
+	"path/filepath"
+	"runtime"
+	"sync"
+	"syscall"
+	"time"
+)
+
+type LogProcess struct {
+	Pid     int
+	Command string
+}
+
+type LogEntry struct {
+	Basename string
+	Args     []string
+	Parents  []LogProcess
+}
+
+const timeoutDuration = time.Duration(100) * time.Millisecond
+
+type socketAddrFunc func(string) (string, func(), error)
+
+func procFallback(name string) (string, func(), error) {
+	d, err := os.Open(filepath.Dir(name))
+	if err != nil {
+		return "", func() {}, err
+	}
+
+	return fmt.Sprintf("/proc/self/fd/%d/%s", d.Fd(), filepath.Base(name)), func() {
+		d.Close()
+	}, nil
+}
+
+func tmpFallback(name string) (addr string, cleanup func(), err error) {
+	d, err := ioutil.TempDir("/tmp", "log_sock")
+	if err != nil {
+		cleanup = func() {}
+		return
+	}
+	cleanup = func() {
+		os.RemoveAll(d)
+	}
+
+	dir := filepath.Dir(name)
+
+	absDir, err := filepath.Abs(dir)
+	if err != nil {
+		return
+	}
+
+	err = os.Symlink(absDir, filepath.Join(d, "d"))
+	if err != nil {
+		return
+	}
+
+	addr = filepath.Join(d, "d", filepath.Base(name))
+
+	return
+}
+
+func getSocketAddr(name string) (string, func(), error) {
+	maxNameLen := len(syscall.RawSockaddrUnix{}.Path)
+
+	if len(name) < maxNameLen {
+		return name, func() {}, nil
+	}
+
+	if runtime.GOOS == "linux" {
+		addr, cleanup, err := procFallback(name)
+		if err == nil {
+			if len(addr) < maxNameLen {
+				return addr, cleanup, nil
+			}
+		}
+		cleanup()
+	}
+
+	addr, cleanup, err := tmpFallback(name)
+	if err == nil {
+		if len(addr) < maxNameLen {
+			return addr, cleanup, nil
+		}
+	}
+	cleanup()
+
+	return name, func() {}, fmt.Errorf("Path to socket is still over size limit, fallbacks failed.")
+}
+
+func dial(name string, lookup socketAddrFunc, timeout time.Duration) (net.Conn, error) {
+	socket, cleanup, err := lookup(name)
+	defer cleanup()
+	if err != nil {
+		return nil, err
+	}
+
+	dialer := &net.Dialer{
+		Timeout: timeout,
+	}
+	return dialer.Dial("unix", socket)
+}
+
+func listen(name string, lookup socketAddrFunc) (net.Listener, error) {
+	socket, cleanup, err := lookup(name)
+	defer cleanup()
+	if err != nil {
+		return nil, err
+	}
+
+	return net.Listen("unix", socket)
+}
+
+func SendLog(logSocket string, entry *LogEntry, done chan interface{}) {
+	sendLog(logSocket, getSocketAddr, timeoutDuration, entry, done)
+}
+
+func sendLog(logSocket string, lookup socketAddrFunc, timeout time.Duration, entry *LogEntry, done chan interface{}) {
+	defer close(done)
+
+	conn, err := dial(logSocket, lookup, timeout)
+	if err != nil {
+		return
+	}
+	defer conn.Close()
+
+	if timeout != 0 {
+		conn.SetDeadline(time.Now().Add(timeout))
+	}
+
+	enc := gob.NewEncoder(conn)
+	enc.Encode(entry)
+}
+
+func LogListener(ctx context.Context, logSocket string) (chan *LogEntry, error) {
+	return logListener(ctx, logSocket, getSocketAddr)
+}
+
+func logListener(ctx context.Context, logSocket string, lookup socketAddrFunc) (chan *LogEntry, error) {
+	ret := make(chan *LogEntry, 5)
+
+	if err := os.Remove(logSocket); err != nil && !os.IsNotExist(err) {
+		return nil, err
+	}
+
+	ln, err := listen(logSocket, lookup)
+	if err != nil {
+		return nil, err
+	}
+
+	go func() {
+		for {
+			select {
+			case <-ctx.Done():
+				ln.Close()
+			}
+		}
+	}()
+
+	go func() {
+		var wg sync.WaitGroup
+		defer func() {
+			wg.Wait()
+			close(ret)
+		}()
+
+		for {
+			conn, err := ln.Accept()
+			if err != nil {
+				ln.Close()
+				break
+			}
+			conn.SetDeadline(time.Now().Add(timeoutDuration))
+			wg.Add(1)
+
+			go func() {
+				defer wg.Done()
+				defer conn.Close()
+
+				dec := gob.NewDecoder(conn)
+				entry := &LogEntry{}
+				if err := dec.Decode(entry); err != nil {
+					return
+				}
+				ret <- entry
+			}()
+		}
+	}()
+	return ret, nil
+}
diff --git a/ui/build/paths/logs_test.go b/ui/build/paths/logs_test.go
new file mode 100644
index 0000000..3b1005f
--- /dev/null
+++ b/ui/build/paths/logs_test.go
@@ -0,0 +1,154 @@
+// Copyright 2018 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 paths
+
+import (
+	"context"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"reflect"
+	"runtime"
+	"strings"
+	"testing"
+)
+
+func TestSendLog(t *testing.T) {
+	t.Run("Short name", func(t *testing.T) {
+		d, err := ioutil.TempDir("", "s")
+		if err != nil {
+			t.Fatal(err)
+		}
+		defer os.RemoveAll(d)
+		f := filepath.Join(d, "s")
+
+		testSendLog(t, f, getSocketAddr)
+	})
+
+	testLongName := func(t *testing.T, lookup socketAddrFunc) {
+		d, err := ioutil.TempDir("", strings.Repeat("s", 150))
+		if err != nil {
+			t.Fatal(err)
+		}
+		defer os.RemoveAll(d)
+		f := filepath.Join(d, strings.Repeat("s", 10))
+
+		testSendLog(t, f, lookup)
+	}
+
+	// Using a name longer than the ~100 limit of the underlying calls to bind, etc
+	t.Run("Long name", func(t *testing.T) {
+		testLongName(t, getSocketAddr)
+	})
+
+	if runtime.GOOS == "linux" {
+		t.Run("Long name proc fallback", func(t *testing.T) {
+			testLongName(t, procFallback)
+		})
+	}
+
+	t.Run("Long name tmp fallback", func(t *testing.T) {
+		testLongName(t, tmpFallback)
+	})
+}
+
+func testSendLog(t *testing.T, socket string, lookup socketAddrFunc) {
+	recv, err := logListener(context.Background(), socket, lookup)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	go func() {
+		for i := 0; i < 10; i++ {
+			sendLog(socket, lookup, 0, &LogEntry{
+				Basename: "test",
+				Args:     []string{"foo", "bar"},
+			}, make(chan interface{}))
+		}
+	}()
+
+	count := 0
+	for {
+		entry := <-recv
+		if entry == nil {
+			if count != 10 {
+				t.Errorf("Expected 10 logs, got %d", count)
+			}
+			return
+		}
+
+		ref := LogEntry{
+			Basename: "test",
+			Args:     []string{"foo", "bar"},
+		}
+		if !reflect.DeepEqual(ref, *entry) {
+			t.Fatalf("Bad log entry: %v", entry)
+		}
+		count++
+
+		if count == 10 {
+			return
+		}
+	}
+}
+
+func TestSendLogError(t *testing.T) {
+	d, err := ioutil.TempDir("", "log_socket")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer os.RemoveAll(d)
+
+	// Missing log sockets should not block waiting for the timeout to elapse
+	t.Run("Missing file", func(t *testing.T) {
+		sendLog(filepath.Join(d, "missing"), getSocketAddr, 0, &LogEntry{}, make(chan interface{}))
+	})
+
+	// Non-sockets should not block waiting for the timeout to elapse
+	t.Run("Regular file", func(t *testing.T) {
+		f := filepath.Join(d, "file")
+		if fp, err := os.Create(f); err == nil {
+			fp.Close()
+		} else {
+			t.Fatal(err)
+		}
+
+		sendLog(f, getSocketAddr, 0, &LogEntry{}, make(chan interface{}))
+	})
+
+	// If the reader is stuck, we should be able to make progress
+	t.Run("Reader not reading", func(t *testing.T) {
+		f := filepath.Join(d, "sock1")
+
+		ln, err := listen(f, getSocketAddr)
+		if err != nil {
+			t.Fatal(err)
+		}
+		defer ln.Close()
+
+		done := make(chan bool, 1)
+		go func() {
+			for i := 0; i < 10; i++ {
+				sendLog(f, getSocketAddr, timeoutDuration, &LogEntry{
+					// Ensure a relatively large payload
+					Basename: strings.Repeat(" ", 100000),
+				}, make(chan interface{}))
+			}
+			done <- true
+		}()
+
+		<-done
+	})
+}
diff --git a/ui/build/sandbox/darwin/global.sb b/ui/build/sandbox/darwin/global.sb
index 47d0c43..e32b64b 100644
--- a/ui/build/sandbox/darwin/global.sb
+++ b/ui/build/sandbox/darwin/global.sb
@@ -35,6 +35,12 @@
     (global-name-regex #"^com\.apple\.distributed_notifications") ; xcodebuild in Soong
 )
 
+; Allow suid /bin/ps to function
+(allow process-exec (literal "/bin/ps") (with no-sandbox))
+
+; Allow path_interposer unix domain socket without logging
+(allow network-outbound (literal (string-append (param "OUT_DIR") "/.path_interposer_log")))
+
 ; Allow executing any file
 (allow process-exec*)
 (allow process-fork)
diff --git a/ui/build/soong.go b/ui/build/soong.go
index cbb75c7..6c94079 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -15,12 +15,15 @@
 package build
 
 import (
+	"fmt"
 	"os"
 	"path/filepath"
 	"strconv"
-	"time"
+	"strings"
 
 	"github.com/google/blueprint/microfactory"
+
+	"android/soong/ui/status"
 )
 
 func runSoong(ctx Context, config Config) {
@@ -41,9 +44,8 @@
 		cmd.Environment.Set("SRCDIR", ".")
 		cmd.Environment.Set("TOPNAME", "Android.bp")
 		cmd.Sandbox = soongSandbox
-		cmd.Stdout = ctx.Stdout()
-		cmd.Stderr = ctx.Stderr()
-		cmd.RunOrFatal()
+
+		cmd.RunAndPrintOrFatal()
 	}()
 
 	func() {
@@ -56,12 +58,18 @@
 			if _, err := os.Stat(envTool); err == nil {
 				cmd := Command(ctx, config, "soong_env", envTool, envFile)
 				cmd.Sandbox = soongSandbox
-				cmd.Stdout = ctx.Stdout()
-				cmd.Stderr = ctx.Stderr()
+
+				var buf strings.Builder
+				cmd.Stdout = &buf
+				cmd.Stderr = &buf
 				if err := cmd.Run(); err != nil {
 					ctx.Verboseln("soong_env failed, forcing manifest regeneration")
 					os.Remove(envFile)
 				}
+
+				if buf.Len() > 0 {
+					ctx.Verboseln(buf.String())
+				}
 			} else {
 				ctx.Verboseln("Missing soong_env tool, forcing manifest regeneration")
 				os.Remove(envFile)
@@ -71,41 +79,47 @@
 		}
 	}()
 
+	var cfg microfactory.Config
+	cfg.Map("github.com/google/blueprint", "build/blueprint")
+
+	cfg.TrimPath = absPath(ctx, ".")
+
 	func() {
 		ctx.BeginTrace("minibp")
 		defer ctx.EndTrace()
 
-		var cfg microfactory.Config
-		cfg.Map("github.com/google/blueprint", "build/blueprint")
-
-		cfg.TrimPath = absPath(ctx, ".")
-
 		minibp := filepath.Join(config.SoongOutDir(), ".minibootstrap/minibp")
 		if _, err := microfactory.Build(&cfg, minibp, "github.com/google/blueprint/bootstrap/minibp"); err != nil {
 			ctx.Fatalln("Failed to build minibp:", err)
 		}
 	}()
 
+	func() {
+		ctx.BeginTrace("bpglob")
+		defer ctx.EndTrace()
+
+		bpglob := filepath.Join(config.SoongOutDir(), ".minibootstrap/bpglob")
+		if _, err := microfactory.Build(&cfg, bpglob, "github.com/google/blueprint/bootstrap/bpglob"); err != nil {
+			ctx.Fatalln("Failed to build bpglob:", err)
+		}
+	}()
+
 	ninja := func(name, file string) {
 		ctx.BeginTrace(name)
 		defer ctx.EndTrace()
 
+		fifo := filepath.Join(config.OutDir(), ".ninja_fifo")
+		status.NinjaReader(ctx, ctx.Status.StartTool(), fifo)
+
 		cmd := Command(ctx, config, "soong "+name,
 			config.PrebuiltBuildTool("ninja"),
 			"-d", "keepdepfile",
 			"-w", "dupbuild=err",
 			"-j", strconv.Itoa(config.Parallel()),
+			fmt.Sprintf("--frontend=cat <&3 >%s", fifo),
 			"-f", filepath.Join(config.SoongOutDir(), file))
-		if config.IsVerbose() {
-			cmd.Args = append(cmd.Args, "-v")
-		}
 		cmd.Sandbox = soongSandbox
-		cmd.Stdin = ctx.Stdin()
-		cmd.Stdout = ctx.Stdout()
-		cmd.Stderr = ctx.Stderr()
-
-		defer ctx.ImportNinjaLog(filepath.Join(config.OutDir(), ".ninja_log"), time.Now())
-		cmd.RunOrFatal()
+		cmd.RunAndPrintOrFatal()
 	}
 
 	ninja("minibootstrap", ".minibootstrap/build.ninja")
diff --git a/ui/build/test_build.go b/ui/build/test_build.go
index 940f0c8..4bc4c97 100644
--- a/ui/build/test_build.go
+++ b/ui/build/test_build.go
@@ -18,6 +18,7 @@
 	"bufio"
 	"path/filepath"
 	"runtime"
+	"sort"
 	"strings"
 )
 
@@ -56,7 +57,7 @@
 	bootstrapDir := filepath.Join(outDir, "soong", ".bootstrap")
 	miniBootstrapDir := filepath.Join(outDir, "soong", ".minibootstrap")
 
-	var danglingRules []string
+	danglingRules := make(map[string]bool)
 
 	scanner := bufio.NewScanner(stdout)
 	for scanner.Scan() {
@@ -70,16 +71,22 @@
 			// full build rules in the primary build.ninja file.
 			continue
 		}
-		danglingRules = append(danglingRules, line)
+		danglingRules[line] = true
 	}
 
 	cmd.WaitOrFatal()
 
-	if len(danglingRules) > 0 {
+	var danglingRulesList []string
+	for rule := range danglingRules {
+		danglingRulesList = append(danglingRulesList, rule)
+	}
+	sort.Strings(danglingRulesList)
+
+	if len(danglingRulesList) > 0 {
 		ctx.Println("Dependencies in out found with no rule to create them:")
-		for _, dep := range danglingRules {
-			ctx.Println(dep)
+		for _, dep := range danglingRulesList {
+			ctx.Println("  ", dep)
 		}
-		ctx.Fatal("")
+		ctx.Fatal("stopping")
 	}
 }
diff --git a/ui/build/util.go b/ui/build/util.go
index f698ccd..0676a86 100644
--- a/ui/build/util.go
+++ b/ui/build/util.go
@@ -15,13 +15,9 @@
 package build
 
 import (
-	"bytes"
-	"io"
 	"os"
 	"path/filepath"
 	"strings"
-	"syscall"
-	"unsafe"
 )
 
 func absPath(ctx Context, p string) string {
@@ -62,9 +58,24 @@
 func ensureEmptyDirectoriesExist(ctx Context, dirs ...string) {
 	// remove all the directories
 	for _, dir := range dirs {
-		err := os.RemoveAll(dir)
-		if err != nil {
-			ctx.Fatalf("Error removing %s: %q\n", dir, err)
+		seenErr := map[string]bool{}
+		for {
+			err := os.RemoveAll(dir)
+			if err == nil {
+				break
+			}
+
+			if pathErr, ok := err.(*os.PathError); !ok ||
+				dir == pathErr.Path || seenErr[pathErr.Path] {
+
+				ctx.Fatalf("Error removing %s: %q\n", dir, err)
+			} else {
+				seenErr[pathErr.Path] = true
+				err = os.Chmod(filepath.Dir(pathErr.Path), 0700)
+				if err != nil {
+					ctx.Fatal(err)
+				}
+			}
 		}
 	}
 	// recreate all the directories
@@ -102,81 +113,3 @@
 	}
 	return str[:idx], str[idx+1:], true
 }
-
-func isTerminal(w io.Writer) bool {
-	if f, ok := w.(*os.File); ok {
-		var termios syscall.Termios
-		_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, f.Fd(),
-			ioctlGetTermios, uintptr(unsafe.Pointer(&termios)),
-			0, 0, 0)
-		return err == 0
-	}
-	return false
-}
-
-func termWidth(w io.Writer) (int, bool) {
-	if f, ok := w.(*os.File); ok {
-		var winsize struct {
-			ws_row, ws_column    uint16
-			ws_xpixel, ws_ypixel uint16
-		}
-		_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, f.Fd(),
-			syscall.TIOCGWINSZ, uintptr(unsafe.Pointer(&winsize)),
-			0, 0, 0)
-		return int(winsize.ws_column), err == 0
-	}
-	return 0, false
-}
-
-// stripAnsiEscapes strips ANSI control codes from a byte array in place.
-func stripAnsiEscapes(input []byte) []byte {
-	// read represents the remaining part of input that needs to be processed.
-	read := input
-	// write represents where we should be writing in input.
-	// It will share the same backing store as input so that we make our modifications
-	// in place.
-	write := input
-
-	// advance will copy count bytes from read to write and advance those slices
-	advance := func(write, read []byte, count int) ([]byte, []byte) {
-		copy(write, read[:count])
-		return write[count:], read[count:]
-	}
-
-	for {
-		// Find the next escape sequence
-		i := bytes.IndexByte(read, 0x1b)
-		// If it isn't found, or if there isn't room for <ESC>[, finish
-		if i == -1 || i+1 >= len(read) {
-			copy(write, read)
-			break
-		}
-
-		// Not a CSI code, continue searching
-		if read[i+1] != '[' {
-			write, read = advance(write, read, i+1)
-			continue
-		}
-
-		// Found a CSI code, advance up to the <ESC>
-		write, read = advance(write, read, i)
-
-		// Find the end of the CSI code
-		i = bytes.IndexFunc(read, func(r rune) bool {
-			return (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z')
-		})
-		if i == -1 {
-			// We didn't find the end of the code, just remove the rest
-			i = len(read) - 1
-		}
-
-		// Strip off the end marker too
-		i = i + 1
-
-		// Skip the reader forward and reduce final length by that amount
-		read = read[i:]
-		input = input[:len(input)-i]
-	}
-
-	return input
-}
diff --git a/ui/build/util_test.go b/ui/build/util_test.go
index e85eada..89bfc77 100644
--- a/ui/build/util_test.go
+++ b/ui/build/util_test.go
@@ -14,49 +14,38 @@
 
 package build
 
-import "testing"
+import (
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"testing"
 
-func TestStripAnsiEscapes(t *testing.T) {
-	testcases := []struct {
-		input  string
-		output string
-	}{
-		{
-			"",
-			"",
-		},
-		{
-			"This is a test",
-			"This is a test",
-		},
-		{
-			"interrupted: \x1b[12",
-			"interrupted: ",
-		},
-		{
-			"other \x1bescape \x1b",
-			"other \x1bescape \x1b",
-		},
-		{ // from pretty-error macro
-			"\x1b[1mart/Android.mk: \x1b[31merror:\x1b[0m\x1b[1m art: test error \x1b[0m",
-			"art/Android.mk: error: art: test error ",
-		},
-		{ // from envsetup.sh make wrapper
-			"\x1b[0;31m#### make failed to build some targets (2 seconds) ####\x1b[00m",
-			"#### make failed to build some targets (2 seconds) ####",
-		},
-		{ // from clang (via ninja testcase)
-			"\x1b[1maffixmgr.cxx:286:15: \x1b[0m\x1b[0;1;35mwarning: \x1b[0m\x1b[1musing the result... [-Wparentheses]\x1b[0m",
-			"affixmgr.cxx:286:15: warning: using the result... [-Wparentheses]",
-		},
+	"android/soong/ui/logger"
+)
+
+func TestEnsureEmptyDirs(t *testing.T) {
+	ctx := testContext()
+	defer logger.Recover(func(err error) {
+		t.Error(err)
+	})
+
+	tmpDir, err := ioutil.TempDir("", "")
+	if err != nil {
+		t.Fatal(err)
 	}
-	for _, tc := range testcases {
-		got := string(stripAnsiEscapes([]byte(tc.input)))
-		if got != tc.output {
-			t.Errorf("output strings didn't match\n"+
-				"input: %#v\n"+
-				" want: %#v\n"+
-				"  got: %#v", tc.input, tc.output, got)
+	defer func() {
+		err := os.RemoveAll(tmpDir)
+		if err != nil {
+			t.Errorf("Error removing tmpDir: %v", err)
 		}
+	}()
+
+	ensureEmptyDirectoriesExist(ctx, filepath.Join(tmpDir, "a/b"))
+
+	err = os.Chmod(filepath.Join(tmpDir, "a"), 0555)
+	if err != nil {
+		t.Fatalf("Failed to chown: %v", err)
 	}
+
+	ensureEmptyDirectoriesExist(ctx, filepath.Join(tmpDir, "a"))
 }
diff --git a/ui/status/Android.bp b/ui/status/Android.bp
new file mode 100644
index 0000000..76caaef
--- /dev/null
+++ b/ui/status/Android.bp
@@ -0,0 +1,42 @@
+// Copyright 2018 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.
+
+bootstrap_go_package {
+    name: "soong-ui-status",
+    pkgPath: "android/soong/ui/status",
+    deps: [
+        "golang-protobuf-proto",
+        "soong-ui-logger",
+        "soong-ui-status-ninja_frontend",
+    ],
+    srcs: [
+        "kati.go",
+        "log.go",
+        "ninja.go",
+        "status.go",
+    ],
+    testSrcs: [
+        "kati_test.go",
+        "status_test.go",
+    ],
+}
+
+bootstrap_go_package {
+    name: "soong-ui-status-ninja_frontend",
+    pkgPath: "android/soong/ui/status/ninja_frontend",
+    deps: ["golang-protobuf-proto"],
+    srcs: [
+        "ninja_frontend/frontend.pb.go",
+    ],
+}
diff --git a/ui/status/kati.go b/ui/status/kati.go
new file mode 100644
index 0000000..552a9e9
--- /dev/null
+++ b/ui/status/kati.go
@@ -0,0 +1,138 @@
+// Copyright 2018 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 status
+
+import (
+	"bufio"
+	"fmt"
+	"io"
+	"regexp"
+	"strconv"
+	"strings"
+)
+
+var katiError = regexp.MustCompile(`^(\033\[1m)?[^ ]+:[0-9]+: (\033\[31m)?error:`)
+var katiIncludeRe = regexp.MustCompile(`^(\[(\d+)/(\d+)] )?((including [^ ]+|initializing build system|finishing build rules|writing build rules) ...)$`)
+var katiLogRe = regexp.MustCompile(`^\*kati\*: `)
+var katiNinjaMissing = regexp.MustCompile("^[^ ]+ is missing, regenerating...$")
+
+type katiOutputParser struct {
+	st ToolStatus
+
+	count int
+	total int
+	extra int
+
+	action   *Action
+	buf      strings.Builder
+	hasError bool
+}
+
+func (k *katiOutputParser) flushAction() {
+	if k.action == nil {
+		return
+	}
+
+	var err error
+	if k.hasError {
+		err = fmt.Errorf("makefile error")
+	}
+
+	k.st.FinishAction(ActionResult{
+		Action: k.action,
+		Output: k.buf.String(),
+		Error:  err,
+	})
+
+	k.buf.Reset()
+	k.hasError = false
+}
+
+func (k *katiOutputParser) parseLine(line string) {
+	// Only put kati debug/stat lines in our verbose log
+	if katiLogRe.MatchString(line) {
+		k.st.Verbose(line)
+		return
+	}
+
+	if matches := katiIncludeRe.FindStringSubmatch(line); len(matches) > 0 {
+		k.flushAction()
+		k.count += 1
+
+		matches := katiIncludeRe.FindStringSubmatch(line)
+		if matches[2] != "" {
+			idx, err := strconv.Atoi(matches[2])
+
+			if err == nil && idx+k.extra != k.count {
+				k.extra = k.count - idx
+				k.st.SetTotalActions(k.total + k.extra)
+			}
+		} else {
+			k.extra += 1
+			k.st.SetTotalActions(k.total + k.extra)
+		}
+
+		if matches[3] != "" {
+			tot, err := strconv.Atoi(matches[3])
+
+			if err == nil && tot != k.total {
+				k.total = tot
+				k.st.SetTotalActions(k.total + k.extra)
+			}
+		}
+
+		k.action = &Action{
+			Description: matches[4],
+		}
+		k.st.StartAction(k.action)
+	} else if k.action != nil {
+		if katiError.MatchString(line) {
+			k.hasError = true
+		}
+		k.buf.WriteString(line)
+		k.buf.WriteString("\n")
+	} else {
+		// Before we've started executing actions from Kati
+		if line == "No need to regenerate ninja file" || katiNinjaMissing.MatchString(line) {
+			k.st.Status(line)
+		} else {
+			k.st.Print(line)
+		}
+	}
+}
+
+// KatiReader reads the output from Kati, and turns it into Actions and
+// messages that are passed into the ToolStatus API.
+func KatiReader(st ToolStatus, pipe io.ReadCloser) {
+	parser := &katiOutputParser{
+		st: st,
+	}
+
+	scanner := bufio.NewScanner(pipe)
+	for scanner.Scan() {
+		parser.parseLine(scanner.Text())
+	}
+
+	parser.flushAction()
+
+	if err := scanner.Err(); err != nil {
+		var buf strings.Builder
+		io.Copy(&buf, pipe)
+		st.Print(fmt.Sprintf("Error from kati parser: %s", err))
+		st.Print(buf.String())
+	}
+
+	st.Finish()
+}
diff --git a/ui/status/kati_test.go b/ui/status/kati_test.go
new file mode 100644
index 0000000..f2cb813
--- /dev/null
+++ b/ui/status/kati_test.go
@@ -0,0 +1,175 @@
+// Copyright 2018 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 status
+
+import (
+	"testing"
+)
+
+type lastOutput struct {
+	counterOutput
+
+	action *Action
+	result ActionResult
+
+	msgLevel MsgLevel
+	msg      string
+}
+
+func (l *lastOutput) StartAction(a *Action, c Counts) {
+	l.action = a
+	l.counterOutput.StartAction(a, c)
+}
+func (l *lastOutput) FinishAction(r ActionResult, c Counts) {
+	l.result = r
+	l.counterOutput.FinishAction(r, c)
+}
+func (l *lastOutput) Message(level MsgLevel, msg string) {
+	l.msgLevel = level
+	l.msg = msg
+}
+func (l *lastOutput) Flush() {}
+
+func TestKatiNormalCase(t *testing.T) {
+	status := &Status{}
+	output := &lastOutput{}
+	status.AddOutput(output)
+
+	parser := &katiOutputParser{
+		st: status.StartTool(),
+	}
+
+	msg := "*kati*: verbose msg"
+	parser.parseLine(msg)
+	output.Expect(t, Counts{})
+
+	if output.msgLevel != VerboseLvl {
+		t.Errorf("Expected verbose message, but got %d", output.msgLevel)
+	}
+	if output.msg != msg {
+		t.Errorf("unexpected message contents:\nwant: %q\n got: %q\n", msg, output.msg)
+	}
+
+	parser.parseLine("out/build-aosp_arm.ninja is missing, regenerating...")
+	output.Expect(t, Counts{})
+
+	parser.parseLine("[1/1] initializing build system ...")
+	output.Expect(t, Counts{
+		TotalActions:    1,
+		RunningActions:  1,
+		StartedActions:  1,
+		FinishedActions: 0,
+	})
+
+	parser.parseLine("[2/5] including out/soong/Android-aosp_arm.mk ...")
+	output.Expect(t, Counts{
+		TotalActions:    5,
+		RunningActions:  1,
+		StartedActions:  2,
+		FinishedActions: 1,
+	})
+
+	parser.parseLine("[3/5] including a ...")
+	msg = "a random message"
+	parser.parseLine(msg)
+
+	// Start the next line to flush the previous result
+	parser.parseLine("[4/5] finishing build rules ...")
+
+	msg += "\n"
+	if output.result.Output != msg {
+		t.Errorf("output for action did not match:\nwant: %q\n got: %q\n", msg, output.result.Output)
+	}
+
+	parser.parseLine("[5/5] writing build rules ...")
+	parser.parseLine("*kati*: verbose msg")
+	parser.flushAction()
+
+	if output.result.Output != "" {
+		t.Errorf("expected no output for last action, but got %q", output.result.Output)
+	}
+
+	output.Expect(t, Counts{
+		TotalActions:    5,
+		RunningActions:  0,
+		StartedActions:  5,
+		FinishedActions: 5,
+	})
+}
+
+func TestKatiExtraIncludes(t *testing.T) {
+	status := &Status{}
+	output := &lastOutput{}
+	status.AddOutput(output)
+
+	parser := &katiOutputParser{
+		st: status.StartTool(),
+	}
+
+	parser.parseLine("[1/1] initializing build system ...")
+	parser.parseLine("[2/5] including out/soong/Android-aosp_arm.mk ...")
+	output.Expect(t, Counts{
+		TotalActions:    5,
+		RunningActions:  1,
+		StartedActions:  2,
+		FinishedActions: 1,
+	})
+
+	parser.parseLine("including a ...")
+
+	output.Expect(t, Counts{
+		TotalActions:    6,
+		RunningActions:  1,
+		StartedActions:  3,
+		FinishedActions: 2,
+	})
+
+	parser.parseLine("including b ...")
+
+	output.Expect(t, Counts{
+		TotalActions:    7,
+		RunningActions:  1,
+		StartedActions:  4,
+		FinishedActions: 3,
+	})
+
+	parser.parseLine("[3/5] finishing build rules ...")
+
+	output.Expect(t, Counts{
+		TotalActions:    7,
+		RunningActions:  1,
+		StartedActions:  5,
+		FinishedActions: 4,
+	})
+}
+
+func TestKatiFailOnError(t *testing.T) {
+	status := &Status{}
+	output := &lastOutput{}
+	status.AddOutput(output)
+
+	parser := &katiOutputParser{
+		st: status.StartTool(),
+	}
+
+	parser.parseLine("[1/1] initializing build system ...")
+	parser.parseLine("[2/5] inclduing out/soong/Android-aosp_arm.mk ...")
+	parser.parseLine("build/make/tools/Android.mk:19: error: testing")
+	parser.flushAction()
+
+	if output.result.Error == nil {
+		t.Errorf("Expected the last action to be marked as an error")
+	}
+}
diff --git a/ui/status/log.go b/ui/status/log.go
new file mode 100644
index 0000000..921aa44
--- /dev/null
+++ b/ui/status/log.go
@@ -0,0 +1,136 @@
+// Copyright 2018 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 status
+
+import (
+	"android/soong/ui/logger"
+	"compress/gzip"
+	"fmt"
+	"io"
+	"strings"
+)
+
+type verboseLog struct {
+	w io.WriteCloser
+}
+
+func NewVerboseLog(log logger.Logger, filename string) StatusOutput {
+	if !strings.HasSuffix(filename, ".gz") {
+		filename += ".gz"
+	}
+
+	f, err := logger.CreateFileWithRotation(filename, 5)
+	if err != nil {
+		log.Println("Failed to create verbose log file:", err)
+		return nil
+	}
+
+	w := gzip.NewWriter(f)
+
+	return &verboseLog{
+		w: w,
+	}
+}
+
+func (v *verboseLog) StartAction(action *Action, counts Counts) {}
+
+func (v *verboseLog) FinishAction(result ActionResult, counts Counts) {
+	cmd := result.Command
+	if cmd == "" {
+		cmd = result.Description
+	}
+
+	fmt.Fprintf(v.w, "[%d/%d] %s\n", counts.FinishedActions, counts.TotalActions, cmd)
+
+	if result.Error != nil {
+		fmt.Fprintf(v.w, "FAILED: %s\n", strings.Join(result.Outputs, " "))
+	}
+
+	if result.Output != "" {
+		fmt.Fprintln(v.w, result.Output)
+	}
+}
+
+func (v *verboseLog) Flush() {
+	v.w.Close()
+}
+
+func (v *verboseLog) Message(level MsgLevel, message string) {
+	fmt.Fprintf(v.w, "%s%s\n", level.Prefix(), message)
+}
+
+type errorLog struct {
+	w io.WriteCloser
+
+	empty bool
+}
+
+func NewErrorLog(log logger.Logger, filename string) StatusOutput {
+	f, err := logger.CreateFileWithRotation(filename, 5)
+	if err != nil {
+		log.Println("Failed to create error log file:", err)
+		return nil
+	}
+
+	return &errorLog{
+		w:     f,
+		empty: true,
+	}
+}
+
+func (e *errorLog) StartAction(action *Action, counts Counts) {}
+
+func (e *errorLog) FinishAction(result ActionResult, counts Counts) {
+	if result.Error == nil {
+		return
+	}
+
+	cmd := result.Command
+	if cmd == "" {
+		cmd = result.Description
+	}
+
+	if !e.empty {
+		fmt.Fprintf(e.w, "\n\n")
+	}
+	e.empty = false
+
+	fmt.Fprintf(e.w, "FAILED: %s\n", result.Description)
+	if len(result.Outputs) > 0 {
+		fmt.Fprintf(e.w, "Outputs: %s\n", strings.Join(result.Outputs, " "))
+	}
+	fmt.Fprintf(e.w, "Error: %s\n", result.Error)
+	if result.Command != "" {
+		fmt.Fprintf(e.w, "Command: %s\n", result.Command)
+	}
+	fmt.Fprintf(e.w, "Output:\n%s\n", result.Output)
+}
+
+func (e *errorLog) Flush() {
+	e.w.Close()
+}
+
+func (e *errorLog) Message(level MsgLevel, message string) {
+	if level < ErrorLvl {
+		return
+	}
+
+	if !e.empty {
+		fmt.Fprintf(e.w, "\n\n")
+	}
+	e.empty = false
+
+	fmt.Fprintf(e.w, "error: %s\n", message)
+}
diff --git a/ui/status/ninja.go b/ui/status/ninja.go
new file mode 100644
index 0000000..7d330f9
--- /dev/null
+++ b/ui/status/ninja.go
@@ -0,0 +1,151 @@
+// Copyright 2018 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 status
+
+import (
+	"bufio"
+	"fmt"
+	"io"
+	"os"
+	"syscall"
+
+	"github.com/golang/protobuf/proto"
+
+	"android/soong/ui/logger"
+	"android/soong/ui/status/ninja_frontend"
+)
+
+// NinjaReader reads the protobuf frontend format from ninja and translates it
+// into calls on the ToolStatus API.
+func NinjaReader(ctx logger.Logger, status ToolStatus, fifo string) {
+	os.Remove(fifo)
+
+	err := syscall.Mkfifo(fifo, 0666)
+	if err != nil {
+		ctx.Fatalf("Failed to mkfifo(%q): %v", fifo, err)
+	}
+
+	go ninjaReader(status, fifo)
+}
+
+func ninjaReader(status ToolStatus, fifo string) {
+	f, err := os.Open(fifo)
+	if err != nil {
+		status.Error(fmt.Sprintf("Failed to open fifo:", err))
+	}
+	defer f.Close()
+
+	r := bufio.NewReader(f)
+
+	running := map[uint32]*Action{}
+
+	for {
+		size, err := readVarInt(r)
+		if err != nil {
+			if err != io.EOF {
+				status.Error(fmt.Sprintf("Got error reading from ninja: %s", err))
+			}
+			return
+		}
+
+		buf := make([]byte, size)
+		_, err = io.ReadFull(r, buf)
+		if err != nil {
+			if err == io.EOF {
+				status.Print(fmt.Sprintf("Missing message of size %d from ninja\n", size))
+			} else {
+				status.Error(fmt.Sprintf("Got error reading from ninja: %s", err))
+			}
+			return
+		}
+
+		msg := &ninja_frontend.Status{}
+		err = proto.Unmarshal(buf, msg)
+		if err != nil {
+			status.Print(fmt.Sprintf("Error reading message from ninja: %v", err))
+			continue
+		}
+
+		// Ignore msg.BuildStarted
+		if msg.TotalEdges != nil {
+			status.SetTotalActions(int(msg.TotalEdges.GetTotalEdges()))
+		}
+		if msg.EdgeStarted != nil {
+			action := &Action{
+				Description: msg.EdgeStarted.GetDesc(),
+				Outputs:     msg.EdgeStarted.Outputs,
+				Command:     msg.EdgeStarted.GetCommand(),
+			}
+			status.StartAction(action)
+			running[msg.EdgeStarted.GetId()] = action
+		}
+		if msg.EdgeFinished != nil {
+			if started, ok := running[msg.EdgeFinished.GetId()]; ok {
+				delete(running, msg.EdgeFinished.GetId())
+
+				var err error
+				exitCode := int(msg.EdgeFinished.GetStatus())
+				if exitCode != 0 {
+					err = fmt.Errorf("exited with code: %d", exitCode)
+				}
+
+				status.FinishAction(ActionResult{
+					Action: started,
+					Output: msg.EdgeFinished.GetOutput(),
+					Error:  err,
+				})
+			}
+		}
+		if msg.Message != nil {
+			message := "ninja: " + msg.Message.GetMessage()
+			switch msg.Message.GetLevel() {
+			case ninja_frontend.Status_Message_INFO:
+				status.Status(message)
+			case ninja_frontend.Status_Message_WARNING:
+				status.Print("warning: " + message)
+			case ninja_frontend.Status_Message_ERROR:
+				status.Error(message)
+			default:
+				status.Print(message)
+			}
+		}
+		if msg.BuildFinished != nil {
+			status.Finish()
+		}
+	}
+}
+
+func readVarInt(r *bufio.Reader) (int, error) {
+	ret := 0
+	shift := uint(0)
+
+	for {
+		b, err := r.ReadByte()
+		if err != nil {
+			return 0, err
+		}
+
+		ret += int(b&0x7f) << (shift * 7)
+		if b&0x80 == 0 {
+			break
+		}
+		shift += 1
+		if shift > 4 {
+			return 0, fmt.Errorf("Expected varint32 length-delimited message")
+		}
+	}
+
+	return ret, nil
+}
diff --git a/ui/status/ninja_frontend/README b/ui/status/ninja_frontend/README
new file mode 100644
index 0000000..8c4b451
--- /dev/null
+++ b/ui/status/ninja_frontend/README
@@ -0,0 +1,3 @@
+This comes from https://android.googlesource.com/platform/external/ninja/+/master/src/frontend.proto
+
+The only difference is the specification of a go_package. To regenerate frontend.pb.go, run regen.sh.
diff --git a/ui/status/ninja_frontend/frontend.pb.go b/ui/status/ninja_frontend/frontend.pb.go
new file mode 100644
index 0000000..7c05eed
--- /dev/null
+++ b/ui/status/ninja_frontend/frontend.pb.go
@@ -0,0 +1,510 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: frontend.proto
+
+package ninja_frontend
+
+import proto "github.com/golang/protobuf/proto"
+import fmt "fmt"
+import math "math"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
+
+type Status_Message_Level int32
+
+const (
+	Status_Message_INFO    Status_Message_Level = 0
+	Status_Message_WARNING Status_Message_Level = 1
+	Status_Message_ERROR   Status_Message_Level = 2
+)
+
+var Status_Message_Level_name = map[int32]string{
+	0: "INFO",
+	1: "WARNING",
+	2: "ERROR",
+}
+var Status_Message_Level_value = map[string]int32{
+	"INFO":    0,
+	"WARNING": 1,
+	"ERROR":   2,
+}
+
+func (x Status_Message_Level) Enum() *Status_Message_Level {
+	p := new(Status_Message_Level)
+	*p = x
+	return p
+}
+func (x Status_Message_Level) String() string {
+	return proto.EnumName(Status_Message_Level_name, int32(x))
+}
+func (x *Status_Message_Level) UnmarshalJSON(data []byte) error {
+	value, err := proto.UnmarshalJSONEnum(Status_Message_Level_value, data, "Status_Message_Level")
+	if err != nil {
+		return err
+	}
+	*x = Status_Message_Level(value)
+	return nil
+}
+func (Status_Message_Level) EnumDescriptor() ([]byte, []int) {
+	return fileDescriptor_frontend_5a49d9b15a642005, []int{0, 5, 0}
+}
+
+type Status struct {
+	TotalEdges           *Status_TotalEdges    `protobuf:"bytes,1,opt,name=total_edges,json=totalEdges" json:"total_edges,omitempty"`
+	BuildStarted         *Status_BuildStarted  `protobuf:"bytes,2,opt,name=build_started,json=buildStarted" json:"build_started,omitempty"`
+	BuildFinished        *Status_BuildFinished `protobuf:"bytes,3,opt,name=build_finished,json=buildFinished" json:"build_finished,omitempty"`
+	EdgeStarted          *Status_EdgeStarted   `protobuf:"bytes,4,opt,name=edge_started,json=edgeStarted" json:"edge_started,omitempty"`
+	EdgeFinished         *Status_EdgeFinished  `protobuf:"bytes,5,opt,name=edge_finished,json=edgeFinished" json:"edge_finished,omitempty"`
+	Message              *Status_Message       `protobuf:"bytes,6,opt,name=message" json:"message,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}              `json:"-"`
+	XXX_unrecognized     []byte                `json:"-"`
+	XXX_sizecache        int32                 `json:"-"`
+}
+
+func (m *Status) Reset()         { *m = Status{} }
+func (m *Status) String() string { return proto.CompactTextString(m) }
+func (*Status) ProtoMessage()    {}
+func (*Status) Descriptor() ([]byte, []int) {
+	return fileDescriptor_frontend_5a49d9b15a642005, []int{0}
+}
+func (m *Status) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_Status.Unmarshal(m, b)
+}
+func (m *Status) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_Status.Marshal(b, m, deterministic)
+}
+func (dst *Status) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Status.Merge(dst, src)
+}
+func (m *Status) XXX_Size() int {
+	return xxx_messageInfo_Status.Size(m)
+}
+func (m *Status) XXX_DiscardUnknown() {
+	xxx_messageInfo_Status.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Status proto.InternalMessageInfo
+
+func (m *Status) GetTotalEdges() *Status_TotalEdges {
+	if m != nil {
+		return m.TotalEdges
+	}
+	return nil
+}
+
+func (m *Status) GetBuildStarted() *Status_BuildStarted {
+	if m != nil {
+		return m.BuildStarted
+	}
+	return nil
+}
+
+func (m *Status) GetBuildFinished() *Status_BuildFinished {
+	if m != nil {
+		return m.BuildFinished
+	}
+	return nil
+}
+
+func (m *Status) GetEdgeStarted() *Status_EdgeStarted {
+	if m != nil {
+		return m.EdgeStarted
+	}
+	return nil
+}
+
+func (m *Status) GetEdgeFinished() *Status_EdgeFinished {
+	if m != nil {
+		return m.EdgeFinished
+	}
+	return nil
+}
+
+func (m *Status) GetMessage() *Status_Message {
+	if m != nil {
+		return m.Message
+	}
+	return nil
+}
+
+type Status_TotalEdges struct {
+	// New value for total edges in the build.
+	TotalEdges           *uint32  `protobuf:"varint,1,opt,name=total_edges,json=totalEdges" json:"total_edges,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *Status_TotalEdges) Reset()         { *m = Status_TotalEdges{} }
+func (m *Status_TotalEdges) String() string { return proto.CompactTextString(m) }
+func (*Status_TotalEdges) ProtoMessage()    {}
+func (*Status_TotalEdges) Descriptor() ([]byte, []int) {
+	return fileDescriptor_frontend_5a49d9b15a642005, []int{0, 0}
+}
+func (m *Status_TotalEdges) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_Status_TotalEdges.Unmarshal(m, b)
+}
+func (m *Status_TotalEdges) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_Status_TotalEdges.Marshal(b, m, deterministic)
+}
+func (dst *Status_TotalEdges) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Status_TotalEdges.Merge(dst, src)
+}
+func (m *Status_TotalEdges) XXX_Size() int {
+	return xxx_messageInfo_Status_TotalEdges.Size(m)
+}
+func (m *Status_TotalEdges) XXX_DiscardUnknown() {
+	xxx_messageInfo_Status_TotalEdges.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Status_TotalEdges proto.InternalMessageInfo
+
+func (m *Status_TotalEdges) GetTotalEdges() uint32 {
+	if m != nil && m.TotalEdges != nil {
+		return *m.TotalEdges
+	}
+	return 0
+}
+
+type Status_BuildStarted struct {
+	// Number of jobs Ninja will run in parallel.
+	Parallelism *uint32 `protobuf:"varint,1,opt,name=parallelism" json:"parallelism,omitempty"`
+	// Verbose value passed to ninja.
+	Verbose              *bool    `protobuf:"varint,2,opt,name=verbose" json:"verbose,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *Status_BuildStarted) Reset()         { *m = Status_BuildStarted{} }
+func (m *Status_BuildStarted) String() string { return proto.CompactTextString(m) }
+func (*Status_BuildStarted) ProtoMessage()    {}
+func (*Status_BuildStarted) Descriptor() ([]byte, []int) {
+	return fileDescriptor_frontend_5a49d9b15a642005, []int{0, 1}
+}
+func (m *Status_BuildStarted) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_Status_BuildStarted.Unmarshal(m, b)
+}
+func (m *Status_BuildStarted) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_Status_BuildStarted.Marshal(b, m, deterministic)
+}
+func (dst *Status_BuildStarted) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Status_BuildStarted.Merge(dst, src)
+}
+func (m *Status_BuildStarted) XXX_Size() int {
+	return xxx_messageInfo_Status_BuildStarted.Size(m)
+}
+func (m *Status_BuildStarted) XXX_DiscardUnknown() {
+	xxx_messageInfo_Status_BuildStarted.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Status_BuildStarted proto.InternalMessageInfo
+
+func (m *Status_BuildStarted) GetParallelism() uint32 {
+	if m != nil && m.Parallelism != nil {
+		return *m.Parallelism
+	}
+	return 0
+}
+
+func (m *Status_BuildStarted) GetVerbose() bool {
+	if m != nil && m.Verbose != nil {
+		return *m.Verbose
+	}
+	return false
+}
+
+type Status_BuildFinished struct {
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *Status_BuildFinished) Reset()         { *m = Status_BuildFinished{} }
+func (m *Status_BuildFinished) String() string { return proto.CompactTextString(m) }
+func (*Status_BuildFinished) ProtoMessage()    {}
+func (*Status_BuildFinished) Descriptor() ([]byte, []int) {
+	return fileDescriptor_frontend_5a49d9b15a642005, []int{0, 2}
+}
+func (m *Status_BuildFinished) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_Status_BuildFinished.Unmarshal(m, b)
+}
+func (m *Status_BuildFinished) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_Status_BuildFinished.Marshal(b, m, deterministic)
+}
+func (dst *Status_BuildFinished) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Status_BuildFinished.Merge(dst, src)
+}
+func (m *Status_BuildFinished) XXX_Size() int {
+	return xxx_messageInfo_Status_BuildFinished.Size(m)
+}
+func (m *Status_BuildFinished) XXX_DiscardUnknown() {
+	xxx_messageInfo_Status_BuildFinished.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Status_BuildFinished proto.InternalMessageInfo
+
+type Status_EdgeStarted struct {
+	// Edge identification number, unique to a Ninja run.
+	Id *uint32 `protobuf:"varint,1,opt,name=id" json:"id,omitempty"`
+	// Edge start time in milliseconds since Ninja started.
+	StartTime *uint32 `protobuf:"varint,2,opt,name=start_time,json=startTime" json:"start_time,omitempty"`
+	// List of edge inputs.
+	Inputs []string `protobuf:"bytes,3,rep,name=inputs" json:"inputs,omitempty"`
+	// List of edge outputs.
+	Outputs []string `protobuf:"bytes,4,rep,name=outputs" json:"outputs,omitempty"`
+	// Description field from the edge.
+	Desc *string `protobuf:"bytes,5,opt,name=desc" json:"desc,omitempty"`
+	// Command field from the edge.
+	Command *string `protobuf:"bytes,6,opt,name=command" json:"command,omitempty"`
+	// Edge uses console.
+	Console              *bool    `protobuf:"varint,7,opt,name=console" json:"console,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *Status_EdgeStarted) Reset()         { *m = Status_EdgeStarted{} }
+func (m *Status_EdgeStarted) String() string { return proto.CompactTextString(m) }
+func (*Status_EdgeStarted) ProtoMessage()    {}
+func (*Status_EdgeStarted) Descriptor() ([]byte, []int) {
+	return fileDescriptor_frontend_5a49d9b15a642005, []int{0, 3}
+}
+func (m *Status_EdgeStarted) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_Status_EdgeStarted.Unmarshal(m, b)
+}
+func (m *Status_EdgeStarted) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_Status_EdgeStarted.Marshal(b, m, deterministic)
+}
+func (dst *Status_EdgeStarted) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Status_EdgeStarted.Merge(dst, src)
+}
+func (m *Status_EdgeStarted) XXX_Size() int {
+	return xxx_messageInfo_Status_EdgeStarted.Size(m)
+}
+func (m *Status_EdgeStarted) XXX_DiscardUnknown() {
+	xxx_messageInfo_Status_EdgeStarted.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Status_EdgeStarted proto.InternalMessageInfo
+
+func (m *Status_EdgeStarted) GetId() uint32 {
+	if m != nil && m.Id != nil {
+		return *m.Id
+	}
+	return 0
+}
+
+func (m *Status_EdgeStarted) GetStartTime() uint32 {
+	if m != nil && m.StartTime != nil {
+		return *m.StartTime
+	}
+	return 0
+}
+
+func (m *Status_EdgeStarted) GetInputs() []string {
+	if m != nil {
+		return m.Inputs
+	}
+	return nil
+}
+
+func (m *Status_EdgeStarted) GetOutputs() []string {
+	if m != nil {
+		return m.Outputs
+	}
+	return nil
+}
+
+func (m *Status_EdgeStarted) GetDesc() string {
+	if m != nil && m.Desc != nil {
+		return *m.Desc
+	}
+	return ""
+}
+
+func (m *Status_EdgeStarted) GetCommand() string {
+	if m != nil && m.Command != nil {
+		return *m.Command
+	}
+	return ""
+}
+
+func (m *Status_EdgeStarted) GetConsole() bool {
+	if m != nil && m.Console != nil {
+		return *m.Console
+	}
+	return false
+}
+
+type Status_EdgeFinished struct {
+	// Edge identification number, unique to a Ninja run.
+	Id *uint32 `protobuf:"varint,1,opt,name=id" json:"id,omitempty"`
+	// Edge end time in milliseconds since Ninja started.
+	EndTime *uint32 `protobuf:"varint,2,opt,name=end_time,json=endTime" json:"end_time,omitempty"`
+	// Exit status (0 for success).
+	Status *int32 `protobuf:"zigzag32,3,opt,name=status" json:"status,omitempty"`
+	// Edge output, may contain ANSI codes.
+	Output               *string  `protobuf:"bytes,4,opt,name=output" json:"output,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *Status_EdgeFinished) Reset()         { *m = Status_EdgeFinished{} }
+func (m *Status_EdgeFinished) String() string { return proto.CompactTextString(m) }
+func (*Status_EdgeFinished) ProtoMessage()    {}
+func (*Status_EdgeFinished) Descriptor() ([]byte, []int) {
+	return fileDescriptor_frontend_5a49d9b15a642005, []int{0, 4}
+}
+func (m *Status_EdgeFinished) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_Status_EdgeFinished.Unmarshal(m, b)
+}
+func (m *Status_EdgeFinished) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_Status_EdgeFinished.Marshal(b, m, deterministic)
+}
+func (dst *Status_EdgeFinished) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Status_EdgeFinished.Merge(dst, src)
+}
+func (m *Status_EdgeFinished) XXX_Size() int {
+	return xxx_messageInfo_Status_EdgeFinished.Size(m)
+}
+func (m *Status_EdgeFinished) XXX_DiscardUnknown() {
+	xxx_messageInfo_Status_EdgeFinished.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Status_EdgeFinished proto.InternalMessageInfo
+
+func (m *Status_EdgeFinished) GetId() uint32 {
+	if m != nil && m.Id != nil {
+		return *m.Id
+	}
+	return 0
+}
+
+func (m *Status_EdgeFinished) GetEndTime() uint32 {
+	if m != nil && m.EndTime != nil {
+		return *m.EndTime
+	}
+	return 0
+}
+
+func (m *Status_EdgeFinished) GetStatus() int32 {
+	if m != nil && m.Status != nil {
+		return *m.Status
+	}
+	return 0
+}
+
+func (m *Status_EdgeFinished) GetOutput() string {
+	if m != nil && m.Output != nil {
+		return *m.Output
+	}
+	return ""
+}
+
+type Status_Message struct {
+	// Message priority level (INFO, WARNING, or ERROR).
+	Level *Status_Message_Level `protobuf:"varint,1,opt,name=level,enum=ninja.Status_Message_Level,def=0" json:"level,omitempty"`
+	// Info/warning/error message from Ninja.
+	Message              *string  `protobuf:"bytes,2,opt,name=message" json:"message,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *Status_Message) Reset()         { *m = Status_Message{} }
+func (m *Status_Message) String() string { return proto.CompactTextString(m) }
+func (*Status_Message) ProtoMessage()    {}
+func (*Status_Message) Descriptor() ([]byte, []int) {
+	return fileDescriptor_frontend_5a49d9b15a642005, []int{0, 5}
+}
+func (m *Status_Message) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_Status_Message.Unmarshal(m, b)
+}
+func (m *Status_Message) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_Status_Message.Marshal(b, m, deterministic)
+}
+func (dst *Status_Message) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Status_Message.Merge(dst, src)
+}
+func (m *Status_Message) XXX_Size() int {
+	return xxx_messageInfo_Status_Message.Size(m)
+}
+func (m *Status_Message) XXX_DiscardUnknown() {
+	xxx_messageInfo_Status_Message.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Status_Message proto.InternalMessageInfo
+
+const Default_Status_Message_Level Status_Message_Level = Status_Message_INFO
+
+func (m *Status_Message) GetLevel() Status_Message_Level {
+	if m != nil && m.Level != nil {
+		return *m.Level
+	}
+	return Default_Status_Message_Level
+}
+
+func (m *Status_Message) GetMessage() string {
+	if m != nil && m.Message != nil {
+		return *m.Message
+	}
+	return ""
+}
+
+func init() {
+	proto.RegisterType((*Status)(nil), "ninja.Status")
+	proto.RegisterType((*Status_TotalEdges)(nil), "ninja.Status.TotalEdges")
+	proto.RegisterType((*Status_BuildStarted)(nil), "ninja.Status.BuildStarted")
+	proto.RegisterType((*Status_BuildFinished)(nil), "ninja.Status.BuildFinished")
+	proto.RegisterType((*Status_EdgeStarted)(nil), "ninja.Status.EdgeStarted")
+	proto.RegisterType((*Status_EdgeFinished)(nil), "ninja.Status.EdgeFinished")
+	proto.RegisterType((*Status_Message)(nil), "ninja.Status.Message")
+	proto.RegisterEnum("ninja.Status_Message_Level", Status_Message_Level_name, Status_Message_Level_value)
+}
+
+func init() { proto.RegisterFile("frontend.proto", fileDescriptor_frontend_5a49d9b15a642005) }
+
+var fileDescriptor_frontend_5a49d9b15a642005 = []byte{
+	// 496 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x53, 0xd1, 0x6e, 0xd3, 0x30,
+	0x14, 0xa5, 0x69, 0xd3, 0x34, 0x37, 0x6d, 0x28, 0x96, 0x40, 0x59, 0x10, 0xa2, 0xda, 0xd3, 0x78,
+	0x20, 0x48, 0xbc, 0x20, 0x10, 0x12, 0xa2, 0xd2, 0x06, 0x43, 0xd0, 0x49, 0xde, 0x24, 0x24, 0x5e,
+	0xaa, 0x74, 0xf6, 0x86, 0x51, 0xe2, 0x54, 0xb1, 0xbb, 0x5f, 0xe0, 0x7f, 0x78, 0xe0, 0xfb, 0x90,
+	0xaf, 0xed, 0x2c, 0x65, 0x7b, 0xcb, 0xf1, 0x3d, 0xe7, 0xde, 0x73, 0x8f, 0x1d, 0x48, 0xaf, 0xda,
+	0x46, 0x6a, 0x2e, 0x59, 0xb1, 0x6d, 0x1b, 0xdd, 0x90, 0x50, 0x0a, 0xf9, 0xab, 0x3c, 0xfc, 0x13,
+	0xc1, 0xf8, 0x5c, 0x97, 0x7a, 0xa7, 0xc8, 0x5b, 0x48, 0x74, 0xa3, 0xcb, 0x6a, 0xcd, 0xd9, 0x35,
+	0x57, 0xd9, 0x60, 0x31, 0x38, 0x4a, 0x5e, 0x67, 0x05, 0xf2, 0x0a, 0xcb, 0x29, 0x2e, 0x0c, 0xe1,
+	0xd8, 0xd4, 0x29, 0xe8, 0xee, 0x9b, 0x7c, 0x80, 0xd9, 0x66, 0x27, 0x2a, 0xb6, 0x56, 0xba, 0x6c,
+	0x35, 0x67, 0x59, 0x80, 0xe2, 0x7c, 0x5f, 0xbc, 0x34, 0x94, 0x73, 0xcb, 0xa0, 0xd3, 0x4d, 0x0f,
+	0x91, 0x25, 0xa4, 0xb6, 0xc1, 0x95, 0x90, 0x42, 0xfd, 0xe4, 0x2c, 0x1b, 0x62, 0x87, 0xa7, 0xf7,
+	0x74, 0x38, 0x71, 0x14, 0x6a, 0x67, 0x7a, 0x48, 0xde, 0xc3, 0xd4, 0x38, 0xef, 0x3c, 0x8c, 0xb0,
+	0xc3, 0xc1, 0x7e, 0x07, 0xe3, 0xd7, 0x5b, 0x48, 0xf8, 0x2d, 0x30, 0x2b, 0xa0, 0xba, 0x33, 0x10,
+	0xde, 0xb7, 0x82, 0x91, 0x77, 0xf3, 0x71, 0x5c, 0x37, 0xfe, 0x15, 0x44, 0x35, 0x57, 0xaa, 0xbc,
+	0xe6, 0xd9, 0x18, 0xa5, 0x8f, 0xf7, 0xa5, 0xdf, 0x6c, 0x91, 0x7a, 0x56, 0xfe, 0x12, 0xe0, 0x36,
+	0x4e, 0xf2, 0xfc, 0x6e, 0xfa, 0xb3, 0x7e, 0xc6, 0xf9, 0x17, 0x98, 0xf6, 0x03, 0x24, 0x0b, 0x48,
+	0xb6, 0x65, 0x5b, 0x56, 0x15, 0xaf, 0x84, 0xaa, 0x9d, 0xa0, 0x7f, 0x44, 0x32, 0x88, 0x6e, 0x78,
+	0xbb, 0x69, 0x14, 0xc7, 0xfb, 0x98, 0x50, 0x0f, 0xf3, 0x87, 0x30, 0xdb, 0x8b, 0x32, 0xff, 0x3b,
+	0x80, 0xa4, 0x17, 0x0d, 0x49, 0x21, 0x10, 0xcc, 0xf5, 0x0c, 0x04, 0x23, 0xcf, 0x00, 0x30, 0xd6,
+	0xb5, 0x16, 0xb5, 0xed, 0x36, 0xa3, 0x31, 0x9e, 0x5c, 0x88, 0x9a, 0x93, 0x27, 0x30, 0x16, 0x72,
+	0xbb, 0xd3, 0x2a, 0x1b, 0x2e, 0x86, 0x47, 0x31, 0x75, 0xc8, 0x38, 0x68, 0x76, 0x1a, 0x0b, 0x23,
+	0x2c, 0x78, 0x48, 0x08, 0x8c, 0x18, 0x57, 0x97, 0x98, 0x72, 0x4c, 0xf1, 0xdb, 0xb0, 0x2f, 0x9b,
+	0xba, 0x2e, 0x25, 0xc3, 0x04, 0x63, 0xea, 0xa1, 0xad, 0x48, 0xd5, 0x54, 0x3c, 0x8b, 0xec, 0x26,
+	0x0e, 0xe6, 0x02, 0xa6, 0xfd, 0x3b, 0xb9, 0x63, 0xfc, 0x00, 0x26, 0x5c, 0xb2, 0xbe, 0xed, 0x88,
+	0x4b, 0xe6, 0x4d, 0x2b, 0xbc, 0x1a, 0x7c, 0x6b, 0x8f, 0xa8, 0x43, 0xe6, 0xdc, 0xba, 0xc4, 0x17,
+	0x14, 0x53, 0x87, 0xf2, 0xdf, 0x03, 0x88, 0xdc, 0x25, 0x92, 0x37, 0x10, 0x56, 0xfc, 0x86, 0x57,
+	0x38, 0x29, 0xfd, 0xff, 0x99, 0x3a, 0x56, 0xf1, 0xd5, 0x50, 0xde, 0x8d, 0x4e, 0x57, 0x27, 0x67,
+	0xd4, 0xf2, 0xcd, 0x26, 0xfe, 0x95, 0x04, 0x76, 0x47, 0x07, 0x0f, 0x5f, 0x40, 0x88, 0x7c, 0x32,
+	0x01, 0x54, 0xcc, 0x1f, 0x90, 0x04, 0xa2, 0xef, 0x1f, 0xe9, 0xea, 0x74, 0xf5, 0x69, 0x3e, 0x20,
+	0x31, 0x84, 0xc7, 0x94, 0x9e, 0xd1, 0x79, 0xb0, 0x24, 0x9f, 0x87, 0x3f, 0x52, 0x9c, 0xb8, 0xf6,
+	0x7f, 0xf5, 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x2e, 0x8c, 0xef, 0xcb, 0xe0, 0x03, 0x00, 0x00,
+}
diff --git a/ui/status/ninja_frontend/frontend.proto b/ui/status/ninja_frontend/frontend.proto
new file mode 100644
index 0000000..13fd535
--- /dev/null
+++ b/ui/status/ninja_frontend/frontend.proto
@@ -0,0 +1,84 @@
+// 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.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package ninja;
+option go_package = "ninja_frontend";
+
+message Status {
+  message TotalEdges {
+    // New value for total edges in the build.
+    optional uint32 total_edges = 1;
+  }
+
+  message BuildStarted {
+    // Number of jobs Ninja will run in parallel.
+    optional uint32 parallelism = 1;
+    // Verbose value passed to ninja.
+    optional bool verbose = 2;
+  }
+
+  message BuildFinished {
+  }
+
+  message EdgeStarted {
+    // Edge identification number, unique to a Ninja run.
+    optional uint32 id = 1;
+    // Edge start time in milliseconds since Ninja started.
+    optional uint32 start_time = 2;
+    // List of edge inputs.
+    repeated string inputs = 3;
+    // List of edge outputs.
+    repeated string outputs = 4;
+    // Description field from the edge.
+    optional string desc = 5;
+    // Command field from the edge.
+    optional string command = 6;
+    // Edge uses console.
+    optional bool console = 7;
+  }
+
+  message EdgeFinished {
+    // Edge identification number, unique to a Ninja run.
+    optional uint32 id = 1;
+    // Edge end time in milliseconds since Ninja started.
+    optional uint32 end_time = 2;
+    // Exit status (0 for success).
+    optional sint32 status = 3;
+    // Edge output, may contain ANSI codes.
+    optional string output = 4;
+  }
+
+  message Message {
+    enum Level {
+      INFO = 0;
+      WARNING = 1;
+      ERROR = 2;
+    }
+    // Message priority level (INFO, WARNING, or ERROR).
+    optional Level level = 1 [default = INFO];
+    // Info/warning/error message from Ninja.
+    optional string message = 2;
+  }
+
+  optional TotalEdges total_edges = 1;
+  optional BuildStarted build_started = 2;
+  optional BuildFinished build_finished = 3;
+  optional EdgeStarted edge_started = 4;
+  optional EdgeFinished edge_finished = 5;
+  optional Message message = 6;
+}
diff --git a/ui/status/ninja_frontend/regen.sh b/ui/status/ninja_frontend/regen.sh
new file mode 100755
index 0000000..d270731
--- /dev/null
+++ b/ui/status/ninja_frontend/regen.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+aprotoc --go_out=paths=source_relative:. frontend.proto
diff --git a/ui/status/status.go b/ui/status/status.go
new file mode 100644
index 0000000..c851d7f
--- /dev/null
+++ b/ui/status/status.go
@@ -0,0 +1,340 @@
+// Copyright 2018 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 status tracks actions run by various tools, combining the counts
+// (total actions, currently running, started, finished), and giving that to
+// multiple outputs.
+package status
+
+import (
+	"sync"
+)
+
+// Action describes an action taken (or as Ninja calls them, Edges).
+type Action struct {
+	// Description is a shorter, more readable form of the command, meant
+	// for users. It's optional, but one of either Description or Command
+	// should be set.
+	Description string
+
+	// Outputs is the (optional) list of outputs. Usually these are files,
+	// but they can be any string.
+	Outputs []string
+
+	// Command is the actual command line executed to perform the action.
+	// It's optional, but one of either Description or Command should be
+	// set.
+	Command string
+}
+
+// ActionResult describes the result of running an Action.
+type ActionResult struct {
+	// Action is a pointer to the original Action struct.
+	*Action
+
+	// Output is the output produced by the command (usually stdout&stderr
+	// for Actions that run commands)
+	Output string
+
+	// Error is nil if the Action succeeded, or set to an error if it
+	// failed.
+	Error error
+}
+
+// Counts describes the number of actions in each state
+type Counts struct {
+	// TotalActions is the total number of expected changes.  This can
+	// generally change up or down during a build, but it should never go
+	// below the number of StartedActions
+	TotalActions int
+
+	// RunningActions are the number of actions that are currently running
+	// -- the number that have called StartAction, but not FinishAction.
+	RunningActions int
+
+	// StartedActions are the number of actions that have been started with
+	// StartAction.
+	StartedActions int
+
+	// FinishedActions are the number of actions that have been finished
+	// with FinishAction.
+	FinishedActions int
+}
+
+// ToolStatus is the interface used by tools to report on their Actions, and to
+// present other information through a set of messaging functions.
+type ToolStatus interface {
+	// SetTotalActions sets the expected total number of actions that will
+	// be started by this tool.
+	//
+	// This call be will ignored if it sets a number that is less than the
+	// current number of started actions.
+	SetTotalActions(total int)
+
+	// StartAction specifies that the associated action has been started by
+	// the tool.
+	//
+	// A specific *Action should not be specified to StartAction more than
+	// once, even if the previous action has already been finished, and the
+	// contents rewritten.
+	//
+	// Do not re-use *Actions between different ToolStatus interfaces
+	// either.
+	StartAction(action *Action)
+
+	// FinishAction specifies the result of a particular Action.
+	//
+	// The *Action embedded in the ActionResult structure must have already
+	// been passed to StartAction (on this interface).
+	//
+	// Do not call FinishAction twice for the same *Action.
+	FinishAction(result ActionResult)
+
+	// Verbose takes a non-important message that is never printed to the
+	// screen, but is in the verbose build log, etc
+	Verbose(msg string)
+	// Status takes a less important message that may be printed to the
+	// screen, but overwritten by another status message. The full message
+	// will still appear in the verbose build log.
+	Status(msg string)
+	// Print takes an message and displays it to the screen and other
+	// output logs, etc.
+	Print(msg string)
+	// Error is similar to Print, but treats it similarly to a failed
+	// action, showing it in the error logs, etc.
+	Error(msg string)
+
+	// Finish marks the end of all Actions being run by this tool.
+	//
+	// SetTotalEdges, StartAction, and FinishAction should not be called
+	// after Finish.
+	Finish()
+}
+
+// MsgLevel specifies the importance of a particular log message. See the
+// descriptions in ToolStatus: Verbose, Status, Print, Error.
+type MsgLevel int
+
+const (
+	VerboseLvl MsgLevel = iota
+	StatusLvl
+	PrintLvl
+	ErrorLvl
+)
+
+func (l MsgLevel) Prefix() string {
+	switch l {
+	case VerboseLvl:
+		return "verbose: "
+	case StatusLvl:
+		return "status: "
+	case PrintLvl:
+		return ""
+	case ErrorLvl:
+		return "error: "
+	default:
+		panic("Unknown message level")
+	}
+}
+
+// StatusOutput is the interface used to get status information as a Status
+// output.
+//
+// All of the functions here are guaranteed to be called by Status while
+// holding it's internal lock, so it's safe to assume a single caller at any
+// time, and that the ordering of calls will be correct. It is not safe to call
+// back into the Status, or one of its ToolStatus interfaces.
+type StatusOutput interface {
+	// StartAction will be called once every time ToolStatus.StartAction is
+	// called. counts will include the current counters across all
+	// ToolStatus instances, including ones that have been finished.
+	StartAction(action *Action, counts Counts)
+
+	// FinishAction will be called once every time ToolStatus.FinishAction
+	// is called. counts will include the current counters across all
+	// ToolStatus instances, including ones that have been finished.
+	FinishAction(result ActionResult, counts Counts)
+
+	// Message is the equivalent of ToolStatus.Verbose/Status/Print/Error,
+	// but the level is specified as an argument.
+	Message(level MsgLevel, msg string)
+
+	// Flush is called when your outputs should be flushed / closed. No
+	// output is expected after this call.
+	Flush()
+}
+
+// Status is the multiplexer / accumulator between ToolStatus instances (via
+// StartTool) and StatusOutputs (via AddOutput). There's generally one of these
+// per build process (though tools like multiproduct_kati may have multiple
+// independent versions).
+type Status struct {
+	counts  Counts
+	outputs []StatusOutput
+
+	// Protects counts and outputs, and allows each output to
+	// expect only a single caller at a time.
+	lock sync.Mutex
+}
+
+// AddOutput attaches an output to this object. It's generally expected that an
+// output is attached to a single Status instance.
+func (s *Status) AddOutput(output StatusOutput) {
+	if output == nil {
+		return
+	}
+
+	s.lock.Lock()
+	defer s.lock.Unlock()
+
+	s.outputs = append(s.outputs, output)
+}
+
+// StartTool returns a new ToolStatus instance to report the status of a tool.
+func (s *Status) StartTool() ToolStatus {
+	return &toolStatus{
+		status: s,
+	}
+}
+
+// Finish will call Flush on all the outputs, generally flushing or closing all
+// of their outputs. Do not call any other functions on this instance or any
+// associated ToolStatus instances after this has been called.
+func (s *Status) Finish() {
+	s.lock.Lock()
+	defer s.lock.Unlock()
+
+	for _, o := range s.outputs {
+		o.Flush()
+	}
+}
+
+func (s *Status) updateTotalActions(diff int) {
+	s.lock.Lock()
+	defer s.lock.Unlock()
+
+	s.counts.TotalActions += diff
+}
+
+func (s *Status) startAction(action *Action) {
+	s.lock.Lock()
+	defer s.lock.Unlock()
+
+	s.counts.RunningActions += 1
+	s.counts.StartedActions += 1
+
+	for _, o := range s.outputs {
+		o.StartAction(action, s.counts)
+	}
+}
+
+func (s *Status) finishAction(result ActionResult) {
+	s.lock.Lock()
+	defer s.lock.Unlock()
+
+	s.counts.RunningActions -= 1
+	s.counts.FinishedActions += 1
+
+	for _, o := range s.outputs {
+		o.FinishAction(result, s.counts)
+	}
+}
+
+func (s *Status) message(level MsgLevel, msg string) {
+	s.lock.Lock()
+	defer s.lock.Unlock()
+
+	for _, o := range s.outputs {
+		o.Message(level, msg)
+	}
+}
+
+type toolStatus struct {
+	status *Status
+
+	counts Counts
+	// Protects counts
+	lock sync.Mutex
+}
+
+var _ ToolStatus = (*toolStatus)(nil)
+
+func (d *toolStatus) SetTotalActions(total int) {
+	diff := 0
+
+	d.lock.Lock()
+	if total >= d.counts.StartedActions && total != d.counts.TotalActions {
+		diff = total - d.counts.TotalActions
+		d.counts.TotalActions = total
+	}
+	d.lock.Unlock()
+
+	if diff != 0 {
+		d.status.updateTotalActions(diff)
+	}
+}
+
+func (d *toolStatus) StartAction(action *Action) {
+	totalDiff := 0
+
+	d.lock.Lock()
+	d.counts.RunningActions += 1
+	d.counts.StartedActions += 1
+
+	if d.counts.StartedActions > d.counts.TotalActions {
+		totalDiff = d.counts.StartedActions - d.counts.TotalActions
+		d.counts.TotalActions = d.counts.StartedActions
+	}
+	d.lock.Unlock()
+
+	if totalDiff != 0 {
+		d.status.updateTotalActions(totalDiff)
+	}
+	d.status.startAction(action)
+}
+
+func (d *toolStatus) FinishAction(result ActionResult) {
+	d.lock.Lock()
+	d.counts.RunningActions -= 1
+	d.counts.FinishedActions += 1
+	d.lock.Unlock()
+
+	d.status.finishAction(result)
+}
+
+func (d *toolStatus) Verbose(msg string) {
+	d.status.message(VerboseLvl, msg)
+}
+func (d *toolStatus) Status(msg string) {
+	d.status.message(StatusLvl, msg)
+}
+func (d *toolStatus) Print(msg string) {
+	d.status.message(PrintLvl, msg)
+}
+func (d *toolStatus) Error(msg string) {
+	d.status.message(ErrorLvl, msg)
+}
+
+func (d *toolStatus) Finish() {
+	d.lock.Lock()
+	defer d.lock.Unlock()
+
+	if d.counts.TotalActions != d.counts.StartedActions {
+		d.status.updateTotalActions(d.counts.StartedActions - d.counts.TotalActions)
+	}
+
+	// TODO: update status to correct running/finished edges?
+	d.counts.RunningActions = 0
+	d.counts.TotalActions = d.counts.StartedActions
+}
diff --git a/ui/status/status_test.go b/ui/status/status_test.go
new file mode 100644
index 0000000..e62785f
--- /dev/null
+++ b/ui/status/status_test.go
@@ -0,0 +1,166 @@
+// Copyright 2018 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 status
+
+import "testing"
+
+type counterOutput Counts
+
+func (c *counterOutput) StartAction(action *Action, counts Counts) {
+	*c = counterOutput(counts)
+}
+func (c *counterOutput) FinishAction(result ActionResult, counts Counts) {
+	*c = counterOutput(counts)
+}
+func (c counterOutput) Message(level MsgLevel, msg string) {}
+func (c counterOutput) Flush()                             {}
+
+func (c counterOutput) Expect(t *testing.T, counts Counts) {
+	if Counts(c) == counts {
+		return
+	}
+	t.Helper()
+
+	if c.TotalActions != counts.TotalActions {
+		t.Errorf("Expected %d total edges, but got %d", counts.TotalActions, c.TotalActions)
+	}
+	if c.RunningActions != counts.RunningActions {
+		t.Errorf("Expected %d running edges, but got %d", counts.RunningActions, c.RunningActions)
+	}
+	if c.StartedActions != counts.StartedActions {
+		t.Errorf("Expected %d started edges, but got %d", counts.StartedActions, c.StartedActions)
+	}
+	if c.FinishedActions != counts.FinishedActions {
+		t.Errorf("Expected %d finished edges, but got %d", counts.FinishedActions, c.FinishedActions)
+	}
+}
+
+func TestBasicUse(t *testing.T) {
+	status := &Status{}
+	counts := &counterOutput{}
+	status.AddOutput(counts)
+	s := status.StartTool()
+
+	s.SetTotalActions(2)
+
+	a := &Action{}
+	s.StartAction(a)
+
+	counts.Expect(t, Counts{
+		TotalActions:    2,
+		RunningActions:  1,
+		StartedActions:  1,
+		FinishedActions: 0,
+	})
+
+	s.FinishAction(ActionResult{Action: a})
+
+	counts.Expect(t, Counts{
+		TotalActions:    2,
+		RunningActions:  0,
+		StartedActions:  1,
+		FinishedActions: 1,
+	})
+
+	a = &Action{}
+	s.StartAction(a)
+
+	counts.Expect(t, Counts{
+		TotalActions:    2,
+		RunningActions:  1,
+		StartedActions:  2,
+		FinishedActions: 1,
+	})
+
+	s.FinishAction(ActionResult{Action: a})
+
+	counts.Expect(t, Counts{
+		TotalActions:    2,
+		RunningActions:  0,
+		StartedActions:  2,
+		FinishedActions: 2,
+	})
+}
+
+// For when a tool claims to have 2 actions, but finishes after one.
+func TestFinishEarly(t *testing.T) {
+	status := &Status{}
+	counts := &counterOutput{}
+	status.AddOutput(counts)
+	s := status.StartTool()
+
+	s.SetTotalActions(2)
+
+	a := &Action{}
+	s.StartAction(a)
+	s.FinishAction(ActionResult{Action: a})
+	s.Finish()
+
+	s = status.StartTool()
+	s.SetTotalActions(2)
+
+	a = &Action{}
+	s.StartAction(a)
+
+	counts.Expect(t, Counts{
+		TotalActions:    3,
+		RunningActions:  1,
+		StartedActions:  2,
+		FinishedActions: 1,
+	})
+}
+
+// For when a tool claims to have 1 action, but starts two.
+func TestExtraActions(t *testing.T) {
+	status := &Status{}
+	counts := &counterOutput{}
+	status.AddOutput(counts)
+	s := status.StartTool()
+
+	s.SetTotalActions(1)
+
+	s.StartAction(&Action{})
+	s.StartAction(&Action{})
+
+	counts.Expect(t, Counts{
+		TotalActions:    2,
+		RunningActions:  2,
+		StartedActions:  2,
+		FinishedActions: 0,
+	})
+}
+
+// When a tool calls Finish() with a running Action
+func TestRunningWhenFinished(t *testing.T) {
+	status := &Status{}
+	counts := &counterOutput{}
+	status.AddOutput(counts)
+
+	s := status.StartTool()
+	s.SetTotalActions(1)
+	s.StartAction(&Action{})
+	s.Finish()
+
+	s = status.StartTool()
+	s.SetTotalActions(1)
+	s.StartAction(&Action{})
+
+	counts.Expect(t, Counts{
+		TotalActions:    2,
+		RunningActions:  2,
+		StartedActions:  2,
+		FinishedActions: 0,
+	})
+}
diff --git a/ui/terminal/Android.bp b/ui/terminal/Android.bp
new file mode 100644
index 0000000..7104a50
--- /dev/null
+++ b/ui/terminal/Android.bp
@@ -0,0 +1,37 @@
+// Copyright 2018 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.
+
+bootstrap_go_package {
+    name: "soong-ui-terminal",
+    pkgPath: "android/soong/ui/terminal",
+    deps: ["soong-ui-status"],
+    srcs: [
+        "status.go",
+        "writer.go",
+        "util.go",
+    ],
+    testSrcs: [
+        "util_test.go",
+    ],
+    darwin: {
+        srcs: [
+            "util_darwin.go",
+        ],
+    },
+    linux: {
+        srcs: [
+            "util_linux.go",
+        ],
+    },
+}
diff --git a/ui/terminal/status.go b/ui/terminal/status.go
new file mode 100644
index 0000000..c8eb382
--- /dev/null
+++ b/ui/terminal/status.go
@@ -0,0 +1,144 @@
+// Copyright 2018 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 terminal
+
+import (
+	"fmt"
+	"strings"
+	"time"
+
+	"android/soong/ui/status"
+)
+
+type statusOutput struct {
+	writer Writer
+	format string
+
+	start time.Time
+}
+
+// NewStatusOutput returns a StatusOutput that represents the
+// current build status similarly to Ninja's built-in terminal
+// output.
+//
+// statusFormat takes nearly all the same options as NINJA_STATUS.
+// %c is currently unsupported.
+func NewStatusOutput(w Writer, statusFormat string) status.StatusOutput {
+	return &statusOutput{
+		writer: w,
+		format: statusFormat,
+
+		start: time.Now(),
+	}
+}
+
+func (s *statusOutput) Message(level status.MsgLevel, message string) {
+	if level >= status.ErrorLvl {
+		s.writer.Print(fmt.Sprintf("FAILED: %s", message))
+	} else if level > status.StatusLvl {
+		s.writer.Print(fmt.Sprintf("%s%s", level.Prefix(), message))
+	} else if level == status.StatusLvl {
+		s.writer.StatusLine(message)
+	}
+}
+
+func (s *statusOutput) StartAction(action *status.Action, counts status.Counts) {
+	if !s.writer.isSmartTerminal() {
+		return
+	}
+
+	str := action.Description
+	if str == "" {
+		str = action.Command
+	}
+
+	s.writer.StatusLine(s.progress(counts) + str)
+}
+
+func (s *statusOutput) FinishAction(result status.ActionResult, counts status.Counts) {
+	str := result.Description
+	if str == "" {
+		str = result.Command
+	}
+
+	progress := s.progress(counts) + str
+
+	if result.Error != nil {
+		hasCommand := ""
+		if result.Command != "" {
+			hasCommand = "\n"
+		}
+
+		s.writer.StatusAndMessage(progress, fmt.Sprintf("FAILED: %s\n%s%s%s",
+			strings.Join(result.Outputs, " "), result.Command, hasCommand, result.Output))
+	} else if result.Output != "" {
+		s.writer.StatusAndMessage(progress, result.Output)
+	} else {
+		s.writer.StatusLine(progress)
+	}
+}
+
+func (s *statusOutput) Flush() {}
+
+func (s *statusOutput) progress(counts status.Counts) string {
+	if s.format == "" {
+		return fmt.Sprintf("[%3d%% %d/%d] ", 100*counts.FinishedActions/counts.TotalActions, counts.FinishedActions, counts.TotalActions)
+	}
+
+	buf := &strings.Builder{}
+	for i := 0; i < len(s.format); i++ {
+		c := s.format[i]
+		if c != '%' {
+			buf.WriteByte(c)
+			continue
+		}
+
+		i = i + 1
+		if i == len(s.format) {
+			buf.WriteByte(c)
+			break
+		}
+
+		c = s.format[i]
+		switch c {
+		case '%':
+			buf.WriteByte(c)
+		case 's':
+			fmt.Fprintf(buf, "%d", counts.StartedActions)
+		case 't':
+			fmt.Fprintf(buf, "%d", counts.TotalActions)
+		case 'r':
+			fmt.Fprintf(buf, "%d", counts.RunningActions)
+		case 'u':
+			fmt.Fprintf(buf, "%d", counts.TotalActions-counts.StartedActions)
+		case 'f':
+			fmt.Fprintf(buf, "%d", counts.FinishedActions)
+		case 'o':
+			fmt.Fprintf(buf, "%.1f", float64(counts.FinishedActions)/time.Since(s.start).Seconds())
+		case 'c':
+			// TODO: implement?
+			buf.WriteRune('?')
+		case 'p':
+			fmt.Fprintf(buf, "%3d%%", 100*counts.FinishedActions/counts.TotalActions)
+		case 'e':
+			fmt.Fprintf(buf, "%.3f", time.Since(s.start).Seconds())
+		default:
+			buf.WriteString("unknown placeholder '")
+			buf.WriteByte(c)
+			buf.WriteString("'")
+		}
+	}
+	return buf.String()
+}
diff --git a/ui/terminal/util.go b/ui/terminal/util.go
new file mode 100644
index 0000000..a85a517
--- /dev/null
+++ b/ui/terminal/util.go
@@ -0,0 +1,101 @@
+// 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 terminal
+
+import (
+	"bytes"
+	"io"
+	"os"
+	"syscall"
+	"unsafe"
+)
+
+func isTerminal(w io.Writer) bool {
+	if f, ok := w.(*os.File); ok {
+		var termios syscall.Termios
+		_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, f.Fd(),
+			ioctlGetTermios, uintptr(unsafe.Pointer(&termios)),
+			0, 0, 0)
+		return err == 0
+	}
+	return false
+}
+
+func termWidth(w io.Writer) (int, bool) {
+	if f, ok := w.(*os.File); ok {
+		var winsize struct {
+			ws_row, ws_column    uint16
+			ws_xpixel, ws_ypixel uint16
+		}
+		_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, f.Fd(),
+			syscall.TIOCGWINSZ, uintptr(unsafe.Pointer(&winsize)),
+			0, 0, 0)
+		return int(winsize.ws_column), err == 0
+	}
+	return 0, false
+}
+
+// stripAnsiEscapes strips ANSI control codes from a byte array in place.
+func stripAnsiEscapes(input []byte) []byte {
+	// read represents the remaining part of input that needs to be processed.
+	read := input
+	// write represents where we should be writing in input.
+	// It will share the same backing store as input so that we make our modifications
+	// in place.
+	write := input
+
+	// advance will copy count bytes from read to write and advance those slices
+	advance := func(write, read []byte, count int) ([]byte, []byte) {
+		copy(write, read[:count])
+		return write[count:], read[count:]
+	}
+
+	for {
+		// Find the next escape sequence
+		i := bytes.IndexByte(read, 0x1b)
+		// If it isn't found, or if there isn't room for <ESC>[, finish
+		if i == -1 || i+1 >= len(read) {
+			copy(write, read)
+			break
+		}
+
+		// Not a CSI code, continue searching
+		if read[i+1] != '[' {
+			write, read = advance(write, read, i+1)
+			continue
+		}
+
+		// Found a CSI code, advance up to the <ESC>
+		write, read = advance(write, read, i)
+
+		// Find the end of the CSI code
+		i = bytes.IndexFunc(read, func(r rune) bool {
+			return (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z')
+		})
+		if i == -1 {
+			// We didn't find the end of the code, just remove the rest
+			i = len(read) - 1
+		}
+
+		// Strip off the end marker too
+		i = i + 1
+
+		// Skip the reader forward and reduce final length by that amount
+		read = read[i:]
+		input = input[:len(input)-i]
+	}
+
+	return input
+}
diff --git a/ui/build/util_darwin.go b/ui/terminal/util_darwin.go
similarity index 97%
rename from ui/build/util_darwin.go
rename to ui/terminal/util_darwin.go
index 254a9b8..109a37f 100644
--- a/ui/build/util_darwin.go
+++ b/ui/terminal/util_darwin.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package build
+package terminal
 
 import (
 	"syscall"
diff --git a/ui/build/util_linux.go b/ui/terminal/util_linux.go
similarity index 97%
rename from ui/build/util_linux.go
rename to ui/terminal/util_linux.go
index 0a4e1d2..0a3d9dd 100644
--- a/ui/build/util_linux.go
+++ b/ui/terminal/util_linux.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package build
+package terminal
 
 import (
 	"syscall"
diff --git a/ui/terminal/util_test.go b/ui/terminal/util_test.go
new file mode 100644
index 0000000..82bde7c
--- /dev/null
+++ b/ui/terminal/util_test.go
@@ -0,0 +1,64 @@
+// 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 terminal
+
+import (
+	"testing"
+)
+
+func TestStripAnsiEscapes(t *testing.T) {
+	testcases := []struct {
+		input  string
+		output string
+	}{
+		{
+			"",
+			"",
+		},
+		{
+			"This is a test",
+			"This is a test",
+		},
+		{
+			"interrupted: \x1b[12",
+			"interrupted: ",
+		},
+		{
+			"other \x1bescape \x1b",
+			"other \x1bescape \x1b",
+		},
+		{ // from pretty-error macro
+			"\x1b[1mart/Android.mk: \x1b[31merror:\x1b[0m\x1b[1m art: test error \x1b[0m",
+			"art/Android.mk: error: art: test error ",
+		},
+		{ // from envsetup.sh make wrapper
+			"\x1b[0;31m#### make failed to build some targets (2 seconds) ####\x1b[00m",
+			"#### make failed to build some targets (2 seconds) ####",
+		},
+		{ // from clang (via ninja testcase)
+			"\x1b[1maffixmgr.cxx:286:15: \x1b[0m\x1b[0;1;35mwarning: \x1b[0m\x1b[1musing the result... [-Wparentheses]\x1b[0m",
+			"affixmgr.cxx:286:15: warning: using the result... [-Wparentheses]",
+		},
+	}
+	for _, tc := range testcases {
+		got := string(stripAnsiEscapes([]byte(tc.input)))
+		if got != tc.output {
+			t.Errorf("output strings didn't match\n"+
+				"input: %#v\n"+
+				" want: %#v\n"+
+				"  got: %#v", tc.input, tc.output, got)
+		}
+	}
+}
diff --git a/ui/terminal/writer.go b/ui/terminal/writer.go
new file mode 100644
index 0000000..ebe4b2a
--- /dev/null
+++ b/ui/terminal/writer.go
@@ -0,0 +1,229 @@
+// Copyright 2018 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 terminal provides a set of interfaces that can be used to interact
+// with the terminal (including falling back when the terminal is detected to
+// be a redirect or other dumb terminal)
+package terminal
+
+import (
+	"fmt"
+	"io"
+	"os"
+	"strings"
+	"sync"
+)
+
+// Writer provides an interface to write temporary and permanent messages to
+// the terminal.
+//
+// The terminal is considered to be a dumb terminal if TERM==dumb, or if a
+// terminal isn't detected on stdout/stderr (generally because it's a pipe or
+// file). Dumb terminals will strip out all ANSI escape sequences, including
+// colors.
+type Writer interface {
+	// Print prints the string to the terminal, overwriting any current
+	// status being displayed.
+	//
+	// On a dumb terminal, the status messages will be kept.
+	Print(str string)
+
+	// Status prints the first line of the string to the terminal,
+	// overwriting any previous status line. Strings longer than the width
+	// of the terminal will be cut off.
+	//
+	// On a dumb terminal, previous status messages will remain, and the
+	// entire first line of the string will be printed.
+	StatusLine(str string)
+
+	// StatusAndMessage prints the first line of status to the terminal,
+	// similarly to StatusLine(), then prints the full msg below that. The
+	// status line is retained.
+	//
+	// There is guaranteed to be no other output in between the status and
+	// message.
+	StatusAndMessage(status, msg string)
+
+	// Finish ensures that the output ends with a newline (preserving any
+	// current status line that is current displayed).
+	//
+	// This does nothing on dumb terminals.
+	Finish()
+
+	// Write implements the io.Writer interface. This is primarily so that
+	// the logger can use this interface to print to stderr without
+	// breaking the other semantics of this interface.
+	//
+	// Try to use any of the other functions if possible.
+	Write(p []byte) (n int, err error)
+
+	isSmartTerminal() bool
+}
+
+// NewWriter creates a new Writer based on the stdio and the TERM
+// environment variable.
+func NewWriter(stdio StdioInterface) Writer {
+	w := &writerImpl{
+		stdio: stdio,
+
+		haveBlankLine: true,
+	}
+
+	if term, ok := os.LookupEnv("TERM"); ok && term != "dumb" {
+		w.smartTerminal = isTerminal(stdio.Stdout())
+	}
+	w.stripEscapes = !w.smartTerminal
+
+	return w
+}
+
+type writerImpl struct {
+	stdio StdioInterface
+
+	haveBlankLine bool
+
+	// Protecting the above, we assume that smartTerminal and stripEscapes
+	// does not change after initial setup.
+	lock sync.Mutex
+
+	smartTerminal bool
+	stripEscapes  bool
+}
+
+func (w *writerImpl) isSmartTerminal() bool {
+	return w.smartTerminal
+}
+
+func (w *writerImpl) requestLine() {
+	if !w.haveBlankLine {
+		fmt.Fprintln(w.stdio.Stdout())
+		w.haveBlankLine = true
+	}
+}
+
+func (w *writerImpl) Print(str string) {
+	if w.stripEscapes {
+		str = string(stripAnsiEscapes([]byte(str)))
+	}
+
+	w.lock.Lock()
+	defer w.lock.Unlock()
+	w.print(str)
+}
+
+func (w *writerImpl) print(str string) {
+	if !w.haveBlankLine {
+		fmt.Fprint(w.stdio.Stdout(), "\r", "\x1b[K")
+		w.haveBlankLine = true
+	}
+	fmt.Fprint(w.stdio.Stdout(), str)
+	if len(str) == 0 || str[len(str)-1] != '\n' {
+		fmt.Fprint(w.stdio.Stdout(), "\n")
+	}
+}
+
+func (w *writerImpl) StatusLine(str string) {
+	w.lock.Lock()
+	defer w.lock.Unlock()
+
+	w.statusLine(str)
+}
+
+func (w *writerImpl) statusLine(str string) {
+	if !w.smartTerminal {
+		fmt.Fprintln(w.stdio.Stdout(), str)
+		return
+	}
+
+	idx := strings.IndexRune(str, '\n')
+	if idx != -1 {
+		str = str[0:idx]
+	}
+
+	// Limit line width to the terminal width, otherwise we'll wrap onto
+	// another line and we won't delete the previous line.
+	//
+	// Run this on every line in case the window has been resized while
+	// we're printing. This could be optimized to only re-run when we get
+	// SIGWINCH if it ever becomes too time consuming.
+	if max, ok := termWidth(w.stdio.Stdout()); ok {
+		if len(str) > max {
+			// TODO: Just do a max. Ninja elides the middle, but that's
+			// more complicated and these lines aren't that important.
+			str = str[:max]
+		}
+	}
+
+	// Move to the beginning on the line, print the output, then clear
+	// the rest of the line.
+	fmt.Fprint(w.stdio.Stdout(), "\r", str, "\x1b[K")
+	w.haveBlankLine = false
+}
+
+func (w *writerImpl) StatusAndMessage(status, msg string) {
+	if w.stripEscapes {
+		msg = string(stripAnsiEscapes([]byte(msg)))
+	}
+
+	w.lock.Lock()
+	defer w.lock.Unlock()
+
+	w.statusLine(status)
+	w.requestLine()
+	w.print(msg)
+}
+
+func (w *writerImpl) Finish() {
+	w.lock.Lock()
+	defer w.lock.Unlock()
+
+	w.requestLine()
+}
+
+func (w *writerImpl) Write(p []byte) (n int, err error) {
+	w.Print(string(p))
+	return len(p), nil
+}
+
+// StdioInterface represents a set of stdin/stdout/stderr Reader/Writers
+type StdioInterface interface {
+	Stdin() io.Reader
+	Stdout() io.Writer
+	Stderr() io.Writer
+}
+
+// StdioImpl uses the OS stdin/stdout/stderr to implement StdioInterface
+type StdioImpl struct{}
+
+func (StdioImpl) Stdin() io.Reader  { return os.Stdin }
+func (StdioImpl) Stdout() io.Writer { return os.Stdout }
+func (StdioImpl) Stderr() io.Writer { return os.Stderr }
+
+var _ StdioInterface = StdioImpl{}
+
+type customStdio struct {
+	stdin  io.Reader
+	stdout io.Writer
+	stderr io.Writer
+}
+
+func NewCustomStdio(stdin io.Reader, stdout, stderr io.Writer) StdioInterface {
+	return customStdio{stdin, stdout, stderr}
+}
+
+func (c customStdio) Stdin() io.Reader  { return c.stdin }
+func (c customStdio) Stdout() io.Writer { return c.stdout }
+func (c customStdio) Stderr() io.Writer { return c.stderr }
+
+var _ StdioInterface = customStdio{}
diff --git a/ui/tracer/Android.bp b/ui/tracer/Android.bp
index 9729c7e..af588f1 100644
--- a/ui/tracer/Android.bp
+++ b/ui/tracer/Android.bp
@@ -15,10 +15,13 @@
 bootstrap_go_package {
     name: "soong-ui-tracer",
     pkgPath: "android/soong/ui/tracer",
-    deps: ["soong-ui-logger"],
+    deps: [
+        "soong-ui-logger",
+        "soong-ui-status",
+    ],
     srcs: [
         "microfactory.go",
-        "ninja.go",
+        "status.go",
         "tracer.go",
     ],
 }
diff --git a/ui/tracer/microfactory.go b/ui/tracer/microfactory.go
index acb9be4..c4c37c2 100644
--- a/ui/tracer/microfactory.go
+++ b/ui/tracer/microfactory.go
@@ -17,10 +17,48 @@
 import (
 	"bufio"
 	"os"
+	"sort"
 	"strconv"
 	"strings"
 )
 
+type eventEntry struct {
+	Name  string
+	Begin uint64
+	End   uint64
+}
+
+func (t *tracerImpl) importEvents(entries []*eventEntry) {
+	sort.Slice(entries, func(i, j int) bool {
+		return entries[i].Begin < entries[j].Begin
+	})
+
+	cpus := []uint64{}
+	for _, entry := range entries {
+		tid := -1
+		for cpu, endTime := range cpus {
+			if endTime <= entry.Begin {
+				tid = cpu
+				cpus[cpu] = entry.End
+				break
+			}
+		}
+		if tid == -1 {
+			tid = len(cpus)
+			cpus = append(cpus, entry.End)
+		}
+
+		t.writeEvent(&viewerEvent{
+			Name:  entry.Name,
+			Phase: "X",
+			Time:  entry.Begin,
+			Dur:   entry.End - entry.Begin,
+			Pid:   1,
+			Tid:   uint64(tid),
+		})
+	}
+}
+
 func (t *tracerImpl) ImportMicrofactoryLog(filename string) {
 	if _, err := os.Stat(filename); err != nil {
 		return
diff --git a/ui/tracer/ninja.go b/ui/tracer/ninja.go
deleted file mode 100644
index 1980559..0000000
--- a/ui/tracer/ninja.go
+++ /dev/null
@@ -1,131 +0,0 @@
-// Copyright 2016 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 tracer
-
-import (
-	"bufio"
-	"os"
-	"sort"
-	"strconv"
-	"strings"
-	"time"
-)
-
-type eventEntry struct {
-	Name  string
-	Begin uint64
-	End   uint64
-}
-
-func (t *tracerImpl) importEvents(entries []*eventEntry) {
-	sort.Slice(entries, func(i, j int) bool {
-		return entries[i].Begin < entries[j].Begin
-	})
-
-	cpus := []uint64{}
-	for _, entry := range entries {
-		tid := -1
-		for cpu, endTime := range cpus {
-			if endTime <= entry.Begin {
-				tid = cpu
-				cpus[cpu] = entry.End
-				break
-			}
-		}
-		if tid == -1 {
-			tid = len(cpus)
-			cpus = append(cpus, entry.End)
-		}
-
-		t.writeEvent(&viewerEvent{
-			Name:  entry.Name,
-			Phase: "X",
-			Time:  entry.Begin,
-			Dur:   entry.End - entry.Begin,
-			Pid:   1,
-			Tid:   uint64(tid),
-		})
-	}
-}
-
-// ImportNinjaLog reads a .ninja_log file from ninja and writes the events out
-// to the trace.
-//
-// startOffset is when the ninja process started, and is used to position the
-// relative times from the ninja log into the trace. It's also used to skip
-// reading the ninja log if nothing was run.
-func (t *tracerImpl) ImportNinjaLog(thread Thread, filename string, startOffset time.Time) {
-	t.Begin("ninja log import", thread)
-	defer t.End(thread)
-
-	if stat, err := os.Stat(filename); err != nil {
-		t.log.Println("Missing ninja log:", err)
-		return
-	} else if stat.ModTime().Before(startOffset) {
-		t.log.Verboseln("Ninja log not modified, not importing any entries.")
-		return
-	}
-
-	f, err := os.Open(filename)
-	if err != nil {
-		t.log.Println("Error opening ninja log:", err)
-		return
-	}
-	defer f.Close()
-
-	s := bufio.NewScanner(f)
-	header := true
-	entries := []*eventEntry{}
-	prevEnd := 0
-	offset := uint64(startOffset.UnixNano()) / 1000
-	for s.Scan() {
-		if header {
-			hdr := s.Text()
-			if hdr != "# ninja log v5" {
-				t.log.Printf("Unknown ninja log header: %q", hdr)
-				return
-			}
-			header = false
-			continue
-		}
-
-		fields := strings.Split(s.Text(), "\t")
-		begin, err := strconv.Atoi(fields[0])
-		if err != nil {
-			t.log.Printf("Unable to parse ninja entry %q: %v", s.Text(), err)
-			return
-		}
-		end, err := strconv.Atoi(fields[1])
-		if err != nil {
-			t.log.Printf("Unable to parse ninja entry %q: %v", s.Text(), err)
-			return
-		}
-		if end < prevEnd {
-			entries = nil
-		}
-		prevEnd = end
-		entries = append(entries, &eventEntry{
-			Name:  fields[3],
-			Begin: offset + uint64(begin)*1000,
-			End:   offset + uint64(end)*1000,
-		})
-	}
-	if err := s.Err(); err != nil {
-		t.log.Println("Unable to parse ninja log:", err)
-		return
-	}
-
-	t.importEvents(entries)
-}
diff --git a/ui/tracer/status.go b/ui/tracer/status.go
new file mode 100644
index 0000000..af50e2d
--- /dev/null
+++ b/ui/tracer/status.go
@@ -0,0 +1,87 @@
+// Copyright 2018 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 tracer
+
+import (
+	"android/soong/ui/status"
+	"time"
+)
+
+func (t *tracerImpl) StatusTracer() status.StatusOutput {
+	return &statusOutput{
+		tracer: t,
+
+		running: map[*status.Action]actionStatus{},
+	}
+}
+
+type actionStatus struct {
+	cpu   int
+	start time.Time
+}
+
+type statusOutput struct {
+	tracer *tracerImpl
+
+	cpus    []bool
+	running map[*status.Action]actionStatus
+}
+
+func (s *statusOutput) StartAction(action *status.Action, counts status.Counts) {
+	cpu := -1
+	for i, busy := range s.cpus {
+		if !busy {
+			cpu = i
+			s.cpus[i] = true
+			break
+		}
+	}
+
+	if cpu == -1 {
+		cpu = len(s.cpus)
+		s.cpus = append(s.cpus, true)
+	}
+
+	s.running[action] = actionStatus{
+		cpu:   cpu,
+		start: time.Now(),
+	}
+}
+
+func (s *statusOutput) FinishAction(result status.ActionResult, counts status.Counts) {
+	start, ok := s.running[result.Action]
+	if !ok {
+		return
+	}
+	delete(s.running, result.Action)
+	s.cpus[start.cpu] = false
+
+	str := result.Action.Description
+	if len(result.Action.Outputs) > 0 {
+		str = result.Action.Outputs[0]
+	}
+
+	s.tracer.writeEvent(&viewerEvent{
+		Name:  str,
+		Phase: "X",
+		Time:  uint64(start.start.UnixNano()) / 1000,
+		Dur:   uint64(time.Since(start.start).Nanoseconds()) / 1000,
+		Pid:   1,
+		Tid:   uint64(start.cpu),
+	})
+}
+
+func (s *statusOutput) Flush()                                        {}
+func (s *statusOutput) Message(level status.MsgLevel, message string) {}
diff --git a/ui/tracer/tracer.go b/ui/tracer/tracer.go
index 8705040..b8fc87b 100644
--- a/ui/tracer/tracer.go
+++ b/ui/tracer/tracer.go
@@ -31,6 +31,7 @@
 	"time"
 
 	"android/soong/ui/logger"
+	"android/soong/ui/status"
 )
 
 type Thread uint64
@@ -46,7 +47,8 @@
 	Complete(name string, thread Thread, begin, end uint64)
 
 	ImportMicrofactoryLog(filename string)
-	ImportNinjaLog(thread Thread, filename string, startOffset time.Time)
+
+	StatusTracer() status.StatusOutput
 
 	NewThread(name string) Thread
 }
diff --git a/zip/zip.go b/zip/zip.go
index b7e3764..a89fa9f 100644
--- a/zip/zip.go
+++ b/zip/zip.go
@@ -788,13 +788,16 @@
 		Name: rel,
 	}
 	fileHeader.SetModTime(z.time)
-	fileHeader.SetMode(0700 | os.ModeSymlink)
+	fileHeader.SetMode(0777 | os.ModeSymlink)
 
 	dest, err := os.Readlink(file)
 	if err != nil {
 		return err
 	}
 
+	fileHeader.UncompressedSize64 = uint64(len(dest))
+	fileHeader.CRC32 = crc32.ChecksumIEEE([]byte(dest))
+
 	ze := make(chan *zipEntry, 1)
 	futureReaders := make(chan chan io.Reader, 1)
 	futureReader := make(chan io.Reader, 1)