Merge "Add a name to API level map for O."
diff --git a/Android.bp b/Android.bp
index d1b8f00..df3babc 100644
--- a/Android.bp
+++ b/Android.bp
@@ -4,6 +4,7 @@
     "cmd/*",
     "fs",
     "finder",
+    "jar",
     "third_party/zip",
     "ui/*",
 ]
@@ -221,6 +222,7 @@
     ],
     srcs: [
         "java/config/config.go",
+        "java/config/errorprone.go",
         "java/config/makevars.go",
     ],
 }
diff --git a/android/package_ctx.go b/android/package_ctx.go
index 2bc98aa..6743fb3 100644
--- a/android/package_ctx.go
+++ b/android/package_ctx.go
@@ -16,6 +16,7 @@
 
 import (
 	"fmt"
+	"strings"
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/pathtools"
@@ -75,6 +76,25 @@
 	})
 }
 
+// SourcePathsVariable returns a Variable whose value is the source directory
+// appended with the supplied paths, joined with separator. It may only be
+// called during a Go package's initialization - either from the init()
+// function or as part of a package-scoped variable's initialization.
+func (p AndroidPackageContext) SourcePathsVariable(name, separator string, paths ...string) blueprint.Variable {
+	return p.VariableFunc(name, func(config interface{}) (string, error) {
+		ctx := &configErrorWrapper{p, config.(Config), []error{}}
+		var ret []string
+		for _, path := range paths {
+			p := safePathForSource(ctx, path)
+			if len(ctx.errors) > 0 {
+				return "", ctx.errors[0]
+			}
+			ret = append(ret, p.String())
+		}
+		return strings.Join(ret, separator), nil
+	})
+}
+
 // SourcePathVariableWithEnvOverride returns a Variable whose value is the source directory
 // appended with the supplied path, or the value of the given environment variable if it is set.
 // The environment variable is not required to point to a path inside the source tree.
diff --git a/android/variable.go b/android/variable.go
index 9cb8c63..1ecd9fb 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -94,6 +94,10 @@
 		Pdk struct {
 			Enabled *bool
 		}
+
+		Uml struct {
+			Cppflags []string
+		}
 	} `android:"arch_variant"`
 }
 
@@ -141,6 +145,7 @@
 	Device_uses_hwc2           *bool `json:",omitempty"`
 	Treble                     *bool `json:",omitempty"`
 	Pdk                        *bool `json:",omitempty"`
+	Uml                        *bool `json:",omitempty"`
 
 	IntegerOverflowExcludePaths *[]string `json:",omitempty"`
 
diff --git a/androidmk/cmd/androidmk/android.go b/androidmk/cmd/androidmk/android.go
index 4afb3b4..19ed4ec 100644
--- a/androidmk/cmd/androidmk/android.go
+++ b/androidmk/cmd/androidmk/android.go
@@ -3,6 +3,7 @@
 import (
 	mkparser "android/soong/androidmk/parser"
 	"fmt"
+	"sort"
 	"strings"
 
 	bpparser "github.com/google/blueprint/parser"
@@ -207,7 +208,9 @@
 	return lists, nil
 }
 
-func splitLocalGlobalPath(value bpparser.Expression) (string, bpparser.Expression, error) {
+// classifyLocalOrGlobalPath tells whether a file path should be interpreted relative to the current module (local)
+// or relative to the root of the source checkout (global)
+func classifyLocalOrGlobalPath(value bpparser.Expression) (string, bpparser.Expression, error) {
 	switch v := value.(type) {
 	case *bpparser.Variable:
 		if v.Name == "LOCAL_PATH" {
@@ -220,7 +223,7 @@
 		}
 	case *bpparser.Operator:
 		if v.Type() != bpparser.StringType {
-			return "", nil, fmt.Errorf("splitLocalGlobalPath expected a string, got %s", value.Type)
+			return "", nil, fmt.Errorf("classifyLocalOrGlobalPath expected a string, got %s", value.Type)
 		}
 
 		if v.Operator != '+' {
@@ -251,68 +254,53 @@
 	case *bpparser.String:
 		return "global", value, nil
 	default:
-		return "", nil, fmt.Errorf("splitLocalGlobalPath expected a string, got %s", value.Type)
+		return "", nil, fmt.Errorf("classifyLocalOrGlobalPath expected a string, got %s", value.Type)
 
 	}
 }
 
+func sortedMapKeys(inputMap map[string]string) (sortedKeys []string) {
+	keys := make([]string, 0, len(inputMap))
+	for key := range inputMap {
+		keys = append(keys, key)
+	}
+	sort.Strings(keys)
+	return keys
+}
+
+// splitAndAssign splits a Make list into components and then
+// creates the corresponding variable assignments.
+func splitAndAssign(ctx variableAssignmentContext, splitFunc listSplitFunc, namesByClassification map[string]string) error {
+	val, err := makeVariableToBlueprint(ctx.file, ctx.mkvalue, bpparser.ListType)
+	if err != nil {
+		return err
+	}
+
+	lists, err := splitBpList(val, splitFunc)
+	if err != nil {
+		return err
+	}
+
+	for _, nameClassification := range sortedMapKeys(namesByClassification) {
+		name := namesByClassification[nameClassification]
+		if component, ok := lists[nameClassification]; ok && !emptyList(component) {
+			err = setVariable(ctx.file, ctx.append, ctx.prefix, name, component, true)
+			if err != nil {
+				return err
+			}
+		}
+	}
+	return nil
+}
+
 func localIncludeDirs(ctx variableAssignmentContext) error {
-	val, err := makeVariableToBlueprint(ctx.file, ctx.mkvalue, bpparser.ListType)
-	if err != nil {
-		return err
-	}
-
-	lists, err := splitBpList(val, splitLocalGlobalPath)
-	if err != nil {
-		return err
-	}
-
-	if global, ok := lists["global"]; ok && !emptyList(global) {
-		err = setVariable(ctx.file, ctx.append, ctx.prefix, "include_dirs", global, true)
-		if err != nil {
-			return err
-		}
-	}
-
-	if local, ok := lists["local"]; ok && !emptyList(local) {
-		err = setVariable(ctx.file, ctx.append, ctx.prefix, "local_include_dirs", local, true)
-		if err != nil {
-			return err
-		}
-	}
-
-	return nil
+	return splitAndAssign(ctx, classifyLocalOrGlobalPath, map[string]string{"global": "include_dirs", "local": "local_include_dirs"})
 }
 
 func exportIncludeDirs(ctx variableAssignmentContext) error {
-	val, err := makeVariableToBlueprint(ctx.file, ctx.mkvalue, bpparser.ListType)
-	if err != nil {
-		return err
-	}
-
-	lists, err := splitBpList(val, splitLocalGlobalPath)
-	if err != nil {
-		return err
-	}
-
-	if local, ok := lists["local"]; ok && !emptyList(local) {
-		err = setVariable(ctx.file, ctx.append, ctx.prefix, "export_include_dirs", local, true)
-		if err != nil {
-			return err
-		}
-		ctx.append = true
-	}
-
 	// Add any paths that could not be converted to local relative paths to export_include_dirs
 	// anyways, they will cause an error if they don't exist and can be fixed manually.
-	if global, ok := lists["global"]; ok && !emptyList(global) {
-		err = setVariable(ctx.file, ctx.append, ctx.prefix, "export_include_dirs", global, true)
-		if err != nil {
-			return err
-		}
-	}
-
-	return nil
+	return splitAndAssign(ctx, classifyLocalOrGlobalPath, map[string]string{"global": "export_include_dirs", "local": "export_include_dirs"})
 }
 
 func stem(ctx variableAssignmentContext) error {
@@ -461,7 +449,7 @@
 }
 
 func prebuiltClass(ctx variableAssignmentContext) error {
-	class := ctx.mkvalue.Value(nil)
+	class := ctx.mkvalue.Value(ctx.file.scope)
 	if v, ok := prebuiltTypes[class]; ok {
 		ctx.file.scope.Set("BUILD_PREBUILT", v)
 	} else {
diff --git a/androidmk/parser/scope.go b/androidmk/parser/scope.go
index 5e94ea5..60efac2 100644
--- a/androidmk/parser/scope.go
+++ b/androidmk/parser/scope.go
@@ -1,6 +1,8 @@
 package parser
 
-import "strings"
+import (
+	"strings"
+)
 
 type Scope interface {
 	Get(name string) string
@@ -84,6 +86,9 @@
 	if ret, ok := v.EvalFunction(scope); ok {
 		return ret
 	}
+	if scope == nil {
+		panic("Cannot take the value of a variable in a nil scope")
+	}
 	return scope.Get(v.Name.Value(scope))
 }
 
diff --git a/cc/binary.go b/cc/binary.go
index f6e62b7..4ddaf94 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -35,6 +35,9 @@
 	// if set, add an extra objcopy --prefix-symbols= step
 	Prefix_symbols string
 
+	// local file name to pass to the linker as --version_script
+	Version_script *string `android:"arch_variant"`
+
 	// if set, install a symlink to the preferred architecture
 	Symlink_preferred_arch bool
 
@@ -231,7 +234,6 @@
 				"-Bstatic",
 				"-Wl,--gc-sections",
 			)
-
 		} else {
 			if flags.DynamicLinker == "" {
 				if binary.Properties.DynamicLinker != "" {
@@ -266,6 +268,7 @@
 				"-Wl,--gc-sections",
 				"-Wl,-z,nocopyreloc",
 			)
+
 		}
 	} else {
 		if binary.static() {
@@ -282,6 +285,7 @@
 func (binary *binaryDecorator) link(ctx ModuleContext,
 	flags Flags, deps PathDeps, objs Objects) android.Path {
 
+	versionScript := android.OptionalPathForModuleSrc(ctx, binary.Properties.Version_script)
 	fileName := binary.getStem(ctx) + flags.Toolchain.ExecutableSuffix()
 	outputFile := android.PathForModuleOut(ctx, fileName)
 	ret := outputFile
@@ -291,6 +295,15 @@
 	sharedLibs := deps.SharedLibs
 	sharedLibs = append(sharedLibs, deps.LateSharedLibs...)
 
+	if versionScript.Valid() {
+		if ctx.Darwin() {
+			ctx.PropertyErrorf("version_script", "Not supported on Darwin")
+		} else {
+			flags.LdFlags = append(flags.LdFlags, "-Wl,--version-script,"+versionScript.String())
+			linkerDeps = append(linkerDeps, versionScript.Path())
+		}
+	}
+
 	if flags.DynamicLinker != "" {
 		flags.LdFlags = append(flags.LdFlags, " -Wl,-dynamic-linker,"+flags.DynamicLinker)
 	}
diff --git a/cc/builder.go b/cc/builder.go
index 873b117..1a4f505 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -151,8 +151,10 @@
 
 	yasm = pctx.AndroidStaticRule("yasm",
 		blueprint.RuleParams{
-			Command:     "$yasmCmd $asFlags -o $out $in",
+			Command:     "$yasmCmd $asFlags -o $out $in && $yasmCmd $asFlags -M $in >$out.d",
 			CommandDeps: []string{"$yasmCmd"},
+			Depfile:     "$out.d",
+			Deps:        blueprint.DepsGCC,
 		},
 		"asFlags")
 
@@ -182,7 +184,7 @@
 	// Abidiff check turned on in advice-only mode. Builds will not fail on abi incompatibilties / extensions.
 	sAbiDiff = pctx.AndroidStaticRule("sAbiDiff",
 		blueprint.RuleParams{
-			Command:     "$sAbiDiffer -lib $libName -arch $arch -advice-only -o ${out} -new $in -old $referenceDump",
+			Command:     "$sAbiDiffer -lib $libName -arch $arch -advice-only -check-all-apis -o ${out} -new $in -old $referenceDump",
 			CommandDeps: []string{"$sAbiDiffer"},
 		},
 		"referenceDump", "libName", "arch")
diff --git a/cc/cc.go b/cc/cc.go
index cfe748b..4dafc63 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -1028,11 +1028,9 @@
 					depPaths.ReexportedFlags = append(depPaths.ReexportedFlags, flags...)
 					depPaths.ReexportedFlagsDeps = append(depPaths.ReexportedFlagsDeps, deps...)
 					// Add these re-exported flags to help header-abi-dumper to infer the abi exported by a library.
-					// Re-exported flags from shared library dependencies are not included as those shared libraries
-					// will be included in the vndk set.
-					if tag == staticExportDepTag || tag == headerExportDepTag {
-						c.sabi.Properties.ReexportedIncludeFlags = append(c.sabi.Properties.ReexportedIncludeFlags, flags...)
-					}
+					// Re-exported shared library headers must be included as well since they can help us with type information
+					// about template instantiations (instantiated from their headers).
+					c.sabi.Properties.ReexportedIncludeFlags = append(c.sabi.Properties.ReexportedIncludeFlags, flags...)
 				}
 			}
 
diff --git a/cc/config/arm_device.go b/cc/config/arm_device.go
index d50de2b..f093563 100644
--- a/cc/config/arm_device.go
+++ b/cc/config/arm_device.go
@@ -102,6 +102,11 @@
 			"-mfloat-abi=softfp",
 			"-mfpu=neon",
 		},
+		"armv8-a": []string{
+			"-march=armv8-a",
+			"-mfloat-abi=softfp",
+			"-mfpu=neon-fp-armv8",
+		},
 	}
 
 	armCpuVariantCflags = map[string][]string{
@@ -174,6 +179,7 @@
 		"armv5te",
 		"armv7-a",
 		"armv7-a-neon",
+		"armv8-a",
 		"cortex-a7",
 		"cortex-a8",
 		"cortex-a9",
@@ -188,12 +194,19 @@
 		"denver")
 
 	android.RegisterArchVariantFeatures(android.Arm, "armv7-a-neon", "neon")
+	android.RegisterArchVariantFeatures(android.Arm, "armv8-a", "neon")
 
 	// Krait and Kryo targets are not supported by GCC, but are supported by Clang,
 	// so override the definitions when building modules with Clang.
 	replaceFirst(armClangCpuVariantCflags["krait"], "-mcpu=cortex-a15", "-mcpu=krait")
 	replaceFirst(armClangCpuVariantCflags["kryo"], "-mcpu=cortex-a15", "-mcpu=krait")
 
+	// The reason we use "-march=armv8-a+crc", instead of "-march=armv8-a", for
+	// gcc is the latter would conflict with any specified/supported -mcpu!
+	// All armv8-a cores supported by gcc 4.9 support crc, so it's safe
+	// to add +crc. Besides, the use of gcc is only for legacy code.
+	replaceFirst(armArchVariantCflags["armv8-a"], "-march=armv8-a", "-march=armv8-a+crc")
+
 	pctx.StaticVariable("armGccVersion", armGccVersion)
 
 	pctx.SourcePathVariable("ArmGccRoot",
@@ -215,6 +228,7 @@
 	pctx.StaticVariable("ArmArmv5TECflags", strings.Join(armArchVariantCflags["armv5te"], " "))
 	pctx.StaticVariable("ArmArmv7ACflags", strings.Join(armArchVariantCflags["armv7-a"], " "))
 	pctx.StaticVariable("ArmArmv7ANeonCflags", strings.Join(armArchVariantCflags["armv7-a-neon"], " "))
+	pctx.StaticVariable("ArmArmv8ACflags", strings.Join(armArchVariantCflags["armv8-a"], " "))
 
 	// Cpu variant cflags
 	pctx.StaticVariable("ArmGenericCflags", strings.Join(armCpuVariantCflags[""], " "))
@@ -242,6 +256,8 @@
 		strings.Join(armClangArchVariantCflags["armv7-a"], " "))
 	pctx.StaticVariable("ArmClangArmv7ANeonCflags",
 		strings.Join(armClangArchVariantCflags["armv7-a-neon"], " "))
+	pctx.StaticVariable("ArmClangArmv8ACflags",
+		strings.Join(armClangArchVariantCflags["armv8-a"], " "))
 
 	// Clang cpu variant cflags
 	pctx.StaticVariable("ArmClangGenericCflags",
@@ -265,6 +281,7 @@
 		"armv5te":      "${config.ArmArmv5TECflags}",
 		"armv7-a":      "${config.ArmArmv7ACflags}",
 		"armv7-a-neon": "${config.ArmArmv7ANeonCflags}",
+		"armv8-a":      "${config.ArmArmv8ACflags}",
 	}
 
 	armCpuVariantCflagsVar = map[string]string{
@@ -286,6 +303,7 @@
 		"armv5te":      "${config.ArmClangArmv5TECflags}",
 		"armv7-a":      "${config.ArmClangArmv7ACflags}",
 		"armv7-a-neon": "${config.ArmClangArmv7ANeonCflags}",
+		"armv8-a":      "${config.ArmClangArmv8ACflags}",
 	}
 
 	armClangCpuVariantCflagsVar = map[string]string{
@@ -402,6 +420,11 @@
 	toolchainClangCflags[0] = "${config.ArmToolchainClangCflags}"
 	toolchainClangCflags[1] = armClangArchVariantCflagsVar[arch.ArchVariant]
 
+	toolchainCflags = append(toolchainCflags,
+		variantOrDefault(armCpuVariantCflagsVar, arch.CpuVariant))
+	toolchainClangCflags = append(toolchainClangCflags,
+		variantOrDefault(armClangCpuVariantCflagsVar, arch.CpuVariant))
+
 	switch arch.ArchVariant {
 	case "armv7-a-neon":
 		switch arch.CpuVariant {
@@ -411,15 +434,12 @@
 		default:
 			fixCortexA8 = "-Wl,--no-fix-cortex-a8"
 		}
-
-		toolchainCflags = append(toolchainCflags,
-			variantOrDefault(armCpuVariantCflagsVar, arch.CpuVariant))
-		toolchainClangCflags = append(toolchainClangCflags,
-			variantOrDefault(armClangCpuVariantCflagsVar, arch.CpuVariant))
 	case "armv7-a":
 		fixCortexA8 = "-Wl,--fix-cortex-a8"
 	case "armv5te":
 		// Nothing extra for armv5te
+	case "armv8-a":
+		// Nothing extra for armv8-a
 	default:
 		panic(fmt.Sprintf("Unknown ARM architecture version: %q", arch.ArchVariant))
 	}
diff --git a/cc/library.go b/cc/library.go
index 4a173a5..1537fd4 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -323,9 +323,7 @@
 	// from a source. We extract the include flags exported by a library.
 	// This includes the flags exported which are re-exported from static
 	// library dependencies, exported header library dependencies and
-	// generated header dependencies. Re-exported shared library include
-	// flags are not in this set since shared library dependencies will
-	// themselves be included in the vndk. -isystem headers are not included
+	// generated header dependencies. -isystem headers are not included
 	// since for bionic libraries, abi-filtering is taken care of by version
 	// scripts.
 	var exportedIncludes []string
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 0409674..7e43373 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -344,7 +344,7 @@
 	}
 
 	if Bool(sanitize.Properties.Sanitize.Coverage) {
-		flags.CFlags = append(flags.CFlags, "-fsanitize-coverage=trace-pc-guard")
+		flags.CFlags = append(flags.CFlags, "-fsanitize-coverage=trace-pc-guard,indirect-calls,trace-cmp")
 	}
 
 	if Bool(sanitize.Properties.Sanitize.Safestack) {
diff --git a/cmd/soong_zip/Android.bp b/cmd/soong_zip/Android.bp
index 10896ce..3e69336 100644
--- a/cmd/soong_zip/Android.bp
+++ b/cmd/soong_zip/Android.bp
@@ -14,7 +14,10 @@
 
 blueprint_go_binary {
     name: "soong_zip",
-    deps: ["android-archive-zip"],
+    deps: [
+        "android-archive-zip",
+        "soong-jar",
+    ],
     srcs: [
         "soong_zip.go",
         "rate_limit.go",
diff --git a/cmd/soong_zip/rate_limit.go b/cmd/soong_zip/rate_limit.go
index 9e95bc1..9cb5fdd 100644
--- a/cmd/soong_zip/rate_limit.go
+++ b/cmd/soong_zip/rate_limit.go
@@ -15,71 +15,54 @@
 package main
 
 import (
+	"fmt"
 	"runtime"
 )
 
 type RateLimit struct {
-	requests chan struct{}
-	finished chan int
-	released chan int
-	stop     chan struct{}
+	requests    chan request
+	completions chan int64
+
+	stop chan struct{}
 }
 
-// NewRateLimit starts a new rate limiter with maxExecs number of executions
-// allowed to happen at a time. If maxExecs is <= 0, it will default to the
-// number of logical CPUs on the system.
-//
-// With Finish and Release, we'll keep track of outstanding buffer sizes to be
-// written. If that size goes above maxMem, we'll prevent starting new
-// executions.
-//
-// The total memory use may be higher due to current executions. This just
-// prevents runaway memory use due to slower writes.
-func NewRateLimit(maxExecs int, maxMem int64) *RateLimit {
-	if maxExecs <= 0 {
-		maxExecs = runtime.NumCPU()
-	}
-	if maxMem <= 0 {
-		// Default to 512MB
-		maxMem = 512 * 1024 * 1024
-	}
+type request struct {
+	size     int64
+	serviced chan struct{}
+}
 
+// NewRateLimit starts a new rate limiter that permits the usage of up to <capacity> at once,
+// except when no capacity is in use, in which case the first caller is always permitted
+func NewRateLimit(capacity int64) *RateLimit {
 	ret := &RateLimit{
-		requests: make(chan struct{}),
+		requests:    make(chan request),
+		completions: make(chan int64),
 
-		// Let all of the pending executions to mark themselves as finished,
-		// even if our goroutine isn't processing input.
-		finished: make(chan int, maxExecs),
-
-		released: make(chan int),
-		stop:     make(chan struct{}),
+		stop: make(chan struct{}),
 	}
 
-	go ret.goFunc(maxExecs, maxMem)
+	go ret.monitorChannels(capacity)
 
 	return ret
 }
 
-// RequestExecution blocks until another execution can be allowed to run.
-func (r *RateLimit) RequestExecution() Execution {
-	<-r.requests
-	return r.finished
+// RequestExecution blocks until another execution of size <size> can be allowed to run.
+func (r *RateLimit) Request(size int64) {
+	request := request{
+		size:     size,
+		serviced: make(chan struct{}, 1),
+	}
+
+	// wait for the request to be received
+	r.requests <- request
+
+	// wait for the request to be accepted
+	<-request.serviced
 }
 
-type Execution chan<- int
-
-// Finish will mark your execution as finished, and allow another request to be
-// approved.
-//
-// bufferSize may be specified to count memory buffer sizes, and must be
-// matched with calls to RateLimit.Release to mark the buffers as released.
-func (e Execution) Finish(bufferSize int) {
-	e <- bufferSize
-}
-
-// Call Release when finished with a buffer recorded with Finish.
-func (r *RateLimit) Release(bufferSize int) {
-	r.released <- bufferSize
+// Finish declares the completion of an execution of size <size>
+func (r *RateLimit) Finish(size int64) {
+	r.completions <- size
 }
 
 // Stop the background goroutine
@@ -87,29 +70,83 @@
 	close(r.stop)
 }
 
-func (r *RateLimit) goFunc(maxExecs int, maxMem int64) {
-	var curExecs int
-	var curMemory int64
+// monitorChannels processes incoming requests from channels
+func (r *RateLimit) monitorChannels(capacity int64) {
+	var usedCapacity int64
+	var currentRequest *request
 
 	for {
-		var requests chan struct{}
-		if curExecs < maxExecs && curMemory < maxMem {
+		var requests chan request
+		if currentRequest == nil {
+			// If we don't already have a queued request, then we should check for a new request
 			requests = r.requests
 		}
 
 		select {
-		case requests <- struct{}{}:
-			curExecs++
-		case amount := <-r.finished:
-			curExecs--
-			curMemory += int64(amount)
-			if curExecs < 0 {
-				panic("curExecs < 0")
+		case newRequest := <-requests:
+			currentRequest = &newRequest
+		case amountCompleted := <-r.completions:
+			usedCapacity -= amountCompleted
+
+			if usedCapacity < 0 {
+				panic(fmt.Sprintf("usedCapacity < 0: %v (decreased by %v)", usedCapacity, amountCompleted))
 			}
-		case amount := <-r.released:
-			curMemory -= int64(amount)
 		case <-r.stop:
 			return
 		}
+
+		if currentRequest != nil {
+			accepted := false
+			if usedCapacity == 0 {
+				accepted = true
+			} else {
+				if capacity >= usedCapacity+currentRequest.size {
+					accepted = true
+				}
+			}
+			if accepted {
+				usedCapacity += currentRequest.size
+				currentRequest.serviced <- struct{}{}
+				currentRequest = nil
+			}
+		}
 	}
 }
+
+// A CPURateLimiter limits the number of active calls based on CPU requirements
+type CPURateLimiter struct {
+	impl *RateLimit
+}
+
+func NewCPURateLimiter(capacity int64) *CPURateLimiter {
+	if capacity <= 0 {
+		capacity = int64(runtime.NumCPU())
+	}
+	impl := NewRateLimit(capacity)
+	return &CPURateLimiter{impl: impl}
+}
+
+func (e CPURateLimiter) Request() {
+	e.impl.Request(1)
+}
+
+func (e CPURateLimiter) Finish() {
+	e.impl.Finish(1)
+}
+
+func (e CPURateLimiter) Stop() {
+	e.impl.Stop()
+}
+
+// A MemoryRateLimiter limits the number of active calls based on Memory requirements
+type MemoryRateLimiter struct {
+	*RateLimit
+}
+
+func NewMemoryRateLimiter(capacity int64) *MemoryRateLimiter {
+	if capacity <= 0 {
+		capacity = 512 * 1024 * 1024 // 512MB
+	}
+	impl := NewRateLimit(capacity)
+	return &MemoryRateLimiter{RateLimit: impl}
+}
diff --git a/cmd/soong_zip/soong_zip.go b/cmd/soong_zip/soong_zip.go
index d634dda..4eb4ebe 100644
--- a/cmd/soong_zip/soong_zip.go
+++ b/cmd/soong_zip/soong_zip.go
@@ -17,6 +17,7 @@
 import (
 	"bytes"
 	"compress/flate"
+	"errors"
 	"flag"
 	"fmt"
 	"hash/crc32"
@@ -28,10 +29,12 @@
 	"runtime"
 	"runtime/pprof"
 	"runtime/trace"
+	"sort"
 	"strings"
 	"sync"
 	"time"
 
+	"android/soong/jar"
 	"android/soong/third_party/zip"
 )
 
@@ -54,6 +57,14 @@
 	return nil
 }
 
+type byteReaderCloser struct {
+	bytes.Reader
+	io.Closer
+}
+
+// the file path in the zip at which a Java manifest file gets written
+const manifestDest = "META-INF/MANIFEST.MF"
+
 type fileArg struct {
 	pathPrefixInZip, sourcePrefixToStrip string
 	sourceFiles                          []string
@@ -135,6 +146,7 @@
 	relativeRoot = flag.String("C", "", "path to use as relative root of files in next -f or -l argument")
 	parallelJobs = flag.Int("j", runtime.NumCPU(), "number of parallel threads to use")
 	compLevel    = flag.Int("L", 5, "deflate compression level (0-9)")
+	emulateJar   = flag.Bool("jar", false, "modify the resultant .zip to emulate the output of 'jar'")
 
 	fArgs            fileArgs
 	nonDeflatedFiles = make(uniqueSet)
@@ -163,7 +175,8 @@
 	errors   chan error
 	writeOps chan chan *zipEntry
 
-	rateLimit *RateLimit
+	cpuRateLimiter    *CPURateLimiter
+	memoryRateLimiter *MemoryRateLimiter
 
 	compressorPool sync.Pool
 	compLevel      int
@@ -174,6 +187,10 @@
 
 	// List of delayed io.Reader
 	futureReaders chan chan io.Reader
+
+	// Only used for passing into the MemoryRateLimiter to ensure we
+	// release as much memory as much as we request
+	allocatedSize int64
 }
 
 func main() {
@@ -210,6 +227,10 @@
 		usage()
 	}
 
+	if *emulateJar {
+		*directories = true
+	}
+
 	w := &zipWriter{
 		time:        time.Date(2009, 1, 1, 0, 0, 0, 0, time.UTC),
 		createdDirs: make(map[string]bool),
@@ -266,6 +287,20 @@
 	return nil
 }
 
+func jarSort(mappings []pathMapping) {
+	less := func(i int, j int) (smaller bool) {
+		return jar.EntryNamesLess(mappings[i].dest, mappings[j].dest)
+	}
+	sort.SliceStable(mappings, less)
+}
+
+type readerSeekerCloser interface {
+	io.Reader
+	io.ReaderAt
+	io.Closer
+	io.Seeker
+}
+
 func (z *zipWriter) write(out string, pathMappings []pathMapping, manifest string) error {
 	f, err := os.Create(out)
 	if err != nil {
@@ -295,23 +330,34 @@
 	// The RateLimit object will put the upper bounds on the number of
 	// parallel compressions and outstanding buffers.
 	z.writeOps = make(chan chan *zipEntry, 1000)
-	z.rateLimit = NewRateLimit(*parallelJobs, 0)
-	defer z.rateLimit.Stop()
+	z.cpuRateLimiter = NewCPURateLimiter(int64(*parallelJobs))
+	z.memoryRateLimiter = NewMemoryRateLimiter(0)
+	defer func() {
+		z.cpuRateLimiter.Stop()
+		z.memoryRateLimiter.Stop()
+	}()
+
+	if manifest != "" {
+		if !*emulateJar {
+			return errors.New("must specify --jar when specifying a manifest via -m")
+		}
+		pathMappings = append(pathMappings, pathMapping{manifestDest, manifest, zip.Deflate})
+	}
+
+	if *emulateJar {
+		jarSort(pathMappings)
+	}
 
 	go func() {
 		var err error
 		defer close(z.writeOps)
 
 		for _, ele := range pathMappings {
-			err = z.writeFile(ele.dest, ele.src, ele.zipMethod)
-			if err != nil {
-				z.errors <- err
-				return
+			if *emulateJar && ele.dest == manifestDest {
+				err = z.addManifest(ele.dest, ele.src, ele.zipMethod)
+			} else {
+				err = z.addFile(ele.dest, ele.src, ele.zipMethod)
 			}
-		}
-
-		if manifest != "" {
-			err = z.writeFile("META-INF/MANIFEST.MF", manifest, zip.Deflate)
 			if err != nil {
 				z.errors <- err
 				return
@@ -357,7 +403,10 @@
 				currentWriter, err = zipw.CreateCompressedHeader(op.fh)
 			} else {
 				var zw io.Writer
-				zw, err = zipw.CreateHeader(op.fh)
+
+				op.fh.CompressedSize64 = op.fh.UncompressedSize64
+
+				zw, err = zipw.CreateHeaderAndroid(op.fh)
 				currentWriter = nopCloser{zw}
 			}
 			if err != nil {
@@ -369,6 +418,7 @@
 				currentWriter.Close()
 				currentWriter = nil
 			}
+			z.memoryRateLimiter.Finish(op.allocatedSize)
 
 		case futureReader, ok := <-readersChan:
 			if !ok {
@@ -381,12 +431,10 @@
 			currentReader = futureReader
 
 		case reader := <-currentReader:
-			var count int64
-			count, err = io.Copy(currentWriter, reader)
+			_, err = io.Copy(currentWriter, reader)
 			if err != nil {
 				return err
 			}
-			z.rateLimit.Release(int(count))
 
 			currentReader = nil
 
@@ -405,7 +453,8 @@
 	}
 }
 
-func (z *zipWriter) writeFile(dest, src string, method uint16) error {
+// imports (possibly with compression) <src> into the zip at sub-path <dest>
+func (z *zipWriter) addFile(dest, src string, method uint16) error {
 	var fileSize int64
 	var executable bool
 
@@ -425,7 +474,60 @@
 		executable = s.Mode()&0100 != 0
 	}
 
+	r, err := os.Open(src)
+	if err != nil {
+		return err
+	}
+
+	header := &zip.FileHeader{
+		Name:               dest,
+		Method:             method,
+		UncompressedSize64: uint64(fileSize),
+	}
+
+	if executable {
+		header.SetMode(0700)
+	}
+
+	return z.writeFileContents(header, r)
+}
+
+// writes the contents of r according to the specifications in header
+func (z *zipWriter) addManifest(dest string, src string, method uint16) error {
+	givenBytes, err := ioutil.ReadFile(src)
+	if err != nil {
+		return err
+	}
+
+	manifestMarker := []byte("Manifest-Version:")
+	header := append(manifestMarker, []byte(" 1.0\nCreated-By: soong_zip\n")...)
+
+	var finalBytes []byte
+	if !bytes.Contains(givenBytes, manifestMarker) {
+		finalBytes = append(append(header, givenBytes...), byte('\n'))
+	} else {
+		finalBytes = givenBytes
+	}
+
+	byteReader := bytes.NewReader(finalBytes)
+
+	reader := &byteReaderCloser{*byteReader, ioutil.NopCloser(nil)}
+
+	fileHeader := &zip.FileHeader{
+		Name:               dest,
+		Method:             zip.Store,
+		UncompressedSize64: uint64(byteReader.Len()),
+	}
+
+	return z.writeFileContents(fileHeader, reader)
+}
+
+func (z *zipWriter) writeFileContents(header *zip.FileHeader, r readerSeekerCloser) (err error) {
+
+	header.SetModTime(z.time)
+
 	if z.directories {
+		dest := header.Name
 		dir, _ := filepath.Split(dest)
 		err := z.writeDirectory(dir)
 		if err != nil {
@@ -439,26 +541,19 @@
 	// Pre-fill a zipEntry, it will be sent in the compressChan once
 	// we're sure about the Method and CRC.
 	ze := &zipEntry{
-		fh: &zip.FileHeader{
-			Name:   dest,
-			Method: method,
-
-			UncompressedSize64: uint64(fileSize),
-		},
-	}
-	ze.fh.SetModTime(z.time)
-	if executable {
-		ze.fh.SetMode(0700)
+		fh: header,
 	}
 
-	r, err := os.Open(src)
-	if err != nil {
-		return err
+	ze.allocatedSize = int64(header.UncompressedSize64)
+	z.cpuRateLimiter.Request()
+	z.memoryRateLimiter.Request(ze.allocatedSize)
+
+	fileSize := int64(header.UncompressedSize64)
+	if fileSize == 0 {
+		fileSize = int64(header.UncompressedSize)
 	}
 
-	exec := z.rateLimit.RequestExecution()
-
-	if method == zip.Deflate && fileSize >= minParallelFileSize {
+	if header.Method == zip.Deflate && fileSize >= minParallelFileSize {
 		wg := new(sync.WaitGroup)
 
 		// Allocate enough buffer to hold all readers. We'll limit
@@ -468,47 +563,53 @@
 		// Calculate the CRC in the background, since reading the entire
 		// file could take a while.
 		//
-		// We could split this up into chuncks as well, but it's faster
+		// We could split this up into chunks as well, but it's faster
 		// than the compression. Due to the Go Zip API, we also need to
 		// know the result before we can begin writing the compressed
 		// data out to the zipfile.
 		wg.Add(1)
-		go z.crcFile(r, ze, exec, compressChan, wg)
+		go z.crcFile(r, ze, compressChan, wg)
 
 		for start := int64(0); start < fileSize; start += parallelBlockSize {
 			sr := io.NewSectionReader(r, start, parallelBlockSize)
 			resultChan := make(chan io.Reader, 1)
 			ze.futureReaders <- resultChan
 
-			exec := z.rateLimit.RequestExecution()
+			z.cpuRateLimiter.Request()
 
 			last := !(start+parallelBlockSize < fileSize)
 			var dict []byte
 			if start >= windowSize {
 				dict, err = ioutil.ReadAll(io.NewSectionReader(r, start-windowSize, windowSize))
+				if err != nil {
+					return err
+				}
 			}
 
 			wg.Add(1)
-			go z.compressPartialFile(sr, dict, last, exec, resultChan, wg)
+			go z.compressPartialFile(sr, dict, last, resultChan, wg)
 		}
 
 		close(ze.futureReaders)
 
 		// Close the file handle after all readers are done
-		go func(wg *sync.WaitGroup, f *os.File) {
+		go func(wg *sync.WaitGroup, closer io.Closer) {
 			wg.Wait()
-			f.Close()
+			closer.Close()
 		}(wg, r)
 	} else {
-		go z.compressWholeFile(ze, r, exec, compressChan)
+		go func() {
+			z.compressWholeFile(ze, r, compressChan)
+			r.Close()
+		}()
 	}
 
 	return nil
 }
 
-func (z *zipWriter) crcFile(r io.Reader, ze *zipEntry, exec Execution, resultChan chan *zipEntry, wg *sync.WaitGroup) {
+func (z *zipWriter) crcFile(r io.Reader, ze *zipEntry, resultChan chan *zipEntry, wg *sync.WaitGroup) {
 	defer wg.Done()
-	defer exec.Finish(0)
+	defer z.cpuRateLimiter.Finish()
 
 	crc := crc32.NewIEEE()
 	_, err := io.Copy(crc, r)
@@ -522,7 +623,7 @@
 	close(resultChan)
 }
 
-func (z *zipWriter) compressPartialFile(r io.Reader, dict []byte, last bool, exec Execution, resultChan chan io.Reader, wg *sync.WaitGroup) {
+func (z *zipWriter) compressPartialFile(r io.Reader, dict []byte, last bool, resultChan chan io.Reader, wg *sync.WaitGroup) {
 	defer wg.Done()
 
 	result, err := z.compressBlock(r, dict, last)
@@ -531,7 +632,8 @@
 		return
 	}
 
-	exec.Finish(result.Len())
+	z.cpuRateLimiter.Finish()
+
 	resultChan <- result
 }
 
@@ -569,10 +671,7 @@
 	return buf, nil
 }
 
-func (z *zipWriter) compressWholeFile(ze *zipEntry, r *os.File, exec Execution, compressChan chan *zipEntry) {
-	var bufSize int
-
-	defer r.Close()
+func (z *zipWriter) compressWholeFile(ze *zipEntry, r io.ReadSeeker, compressChan chan *zipEntry) {
 
 	crc := crc32.NewIEEE()
 	_, err := io.Copy(crc, r)
@@ -589,13 +688,13 @@
 		return
 	}
 
-	readFile := func(r *os.File) ([]byte, error) {
-		_, err = r.Seek(0, 0)
+	readFile := func(reader io.ReadSeeker) ([]byte, error) {
+		_, err := reader.Seek(0, 0)
 		if err != nil {
 			return nil, err
 		}
 
-		buf, err := ioutil.ReadAll(r)
+		buf, err := ioutil.ReadAll(reader)
 		if err != nil {
 			return nil, err
 		}
@@ -616,7 +715,6 @@
 		}
 		if uint64(compressed.Len()) < ze.fh.UncompressedSize64 {
 			futureReader <- compressed
-			bufSize = compressed.Len()
 		} else {
 			buf, err := readFile(r)
 			if err != nil {
@@ -625,7 +723,6 @@
 			}
 			ze.fh.Method = zip.Store
 			futureReader <- bytes.NewReader(buf)
-			bufSize = int(ze.fh.UncompressedSize64)
 		}
 	} else {
 		buf, err := readFile(r)
@@ -635,16 +732,29 @@
 		}
 		ze.fh.Method = zip.Store
 		futureReader <- bytes.NewReader(buf)
-		bufSize = int(ze.fh.UncompressedSize64)
 	}
 
-	exec.Finish(bufSize)
+	z.cpuRateLimiter.Finish()
+
 	close(futureReader)
 
 	compressChan <- ze
 	close(compressChan)
 }
 
+func (z *zipWriter) addExtraField(zipHeader *zip.FileHeader, fieldHeader [2]byte, data []byte) {
+	// add the field header in little-endian order
+	zipHeader.Extra = append(zipHeader.Extra, fieldHeader[1], fieldHeader[0])
+
+	// specify the length of the data (in little-endian order)
+	dataLength := len(data)
+	lengthBytes := []byte{byte(dataLength % 256), byte(dataLength / 256)}
+	zipHeader.Extra = append(zipHeader.Extra, lengthBytes...)
+
+	// add the contents of the extra field
+	zipHeader.Extra = append(zipHeader.Extra, data...)
+}
+
 func (z *zipWriter) writeDirectory(dir string) error {
 	// clean the input
 	cleanDir := filepath.Clean(dir)
@@ -668,6 +778,11 @@
 		dirHeader.SetMode(0700 | os.ModeDir)
 		dirHeader.SetModTime(z.time)
 
+		if *emulateJar && dir == "META-INF/" {
+			// Jar files have a 0-length extra field with header "CAFE"
+			z.addExtraField(dirHeader, [2]byte{0xca, 0xfe}, []byte{})
+		}
+
 		ze := make(chan *zipEntry, 1)
 		ze <- &zipEntry{
 			fh: dirHeader,
@@ -706,11 +821,6 @@
 	futureReader <- bytes.NewBufferString(dest)
 	close(futureReader)
 
-	// We didn't ask permission to execute, since this should be very short
-	// but we still need to increment the outstanding buffer sizes, since
-	// the read will decrement the buffer size.
-	z.rateLimit.Release(-len(dest))
-
 	ze <- &zipEntry{
 		fh:            fileHeader,
 		futureReaders: futureReaders,
diff --git a/cmd/zip2zip/Android.bp b/cmd/zip2zip/Android.bp
index 476be4f..6420219 100644
--- a/cmd/zip2zip/Android.bp
+++ b/cmd/zip2zip/Android.bp
@@ -14,7 +14,10 @@
 
 blueprint_go_binary {
     name: "zip2zip",
-    deps: ["android-archive-zip"],
+    deps: [
+      "android-archive-zip",
+      "soong-jar",
+    ],
     srcs: [
         "zip2zip.go",
     ],
diff --git a/cmd/zip2zip/zip2zip.go b/cmd/zip2zip/zip2zip.go
index 815059c..f48d458 100644
--- a/cmd/zip2zip/zip2zip.go
+++ b/cmd/zip2zip/zip2zip.go
@@ -24,6 +24,7 @@
 	"strings"
 	"time"
 
+	"android/soong/jar"
 	"android/soong/third_party/zip"
 )
 
@@ -178,37 +179,7 @@
 }
 
 func jarSort(files []pair) {
-	// Treats trailing * as a prefix match
-	match := func(pattern, name string) bool {
-		if strings.HasSuffix(pattern, "*") {
-			return strings.HasPrefix(name, strings.TrimSuffix(pattern, "*"))
-		} else {
-			return name == pattern
-		}
-	}
-
-	var jarOrder = []string{
-		"META-INF/",
-		"META-INF/MANIFEST.MF",
-		"META-INF/*",
-		"*",
-	}
-
-	index := func(name string) int {
-		for i, pattern := range jarOrder {
-			if match(pattern, name) {
-				return i
-			}
-		}
-		panic(fmt.Errorf("file %q did not match any pattern", name))
-	}
-
 	sort.SliceStable(files, func(i, j int) bool {
-		diff := index(files[i].newName) - index(files[j].newName)
-		if diff == 0 {
-			return files[i].newName < files[j].newName
-		} else {
-			return diff < 0
-		}
+		return jar.EntryNamesLess(files[i].newName, files[j].newName)
 	})
 }
diff --git a/finder/finder.go b/finder/finder.go
index ffda155..2dd8e0b 100644
--- a/finder/finder.go
+++ b/finder/finder.go
@@ -1181,7 +1181,7 @@
 		// Don't recognize this error
 		return false
 	}
-	if pathErr.Err == os.ErrPermission {
+	if os.IsPermission(pathErr) {
 		// Permission errors are ignored:
 		// https://issuetracker.google.com/37553659
 		// https://github.com/google/kati/pull/116
diff --git a/jar/Android.bp b/jar/Android.bp
new file mode 100644
index 0000000..23ad536
--- /dev/null
+++ b/jar/Android.bp
@@ -0,0 +1,22 @@
+// 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.
+
+bootstrap_go_package {
+    name: "soong-jar",
+    pkgPath: "android/soong/jar",
+    srcs: [
+        "jar.go",
+    ],
+}
+
diff --git a/jar/jar.go b/jar/jar.go
new file mode 100644
index 0000000..d8f063c
--- /dev/null
+++ b/jar/jar.go
@@ -0,0 +1,55 @@
+// 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 jar
+
+import (
+	"fmt"
+	"strings"
+)
+
+// EntryNamesLess tells whether <filepathA> should precede <filepathB> in
+// the order of files with a .jar
+func EntryNamesLess(filepathA string, filepathB string) (less bool) {
+	diff := index(filepathA) - index(filepathB)
+	if diff == 0 {
+		return filepathA < filepathB
+	}
+	return diff < 0
+}
+
+// Treats trailing * as a prefix match
+func patternMatch(pattern, name string) bool {
+	if strings.HasSuffix(pattern, "*") {
+		return strings.HasPrefix(name, strings.TrimSuffix(pattern, "*"))
+	} else {
+		return name == pattern
+	}
+}
+
+var jarOrder = []string{
+	"META-INF/",
+	"META-INF/MANIFEST.MF",
+	"META-INF/*",
+	"*",
+}
+
+func index(name string) int {
+	for i, pattern := range jarOrder {
+		if patternMatch(pattern, name) {
+			return i
+		}
+	}
+	panic(fmt.Errorf("file %q did not match any pattern", name))
+}
diff --git a/java/builder.go b/java/builder.go
index efe0a6b..6912c87 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -38,7 +38,7 @@
 	javac = pctx.AndroidGomaStaticRule("javac",
 		blueprint.RuleParams{
 			Command: `rm -rf "$outDir" "$annoDir" && mkdir -p "$outDir" "$annoDir" && ` +
-				`${config.JavacWrapper}${config.JavacCmd} ${config.CommonJdkFlags} ` +
+				`${config.JavacWrapper}${config.JavacCmd} ${config.JavacHeapFlags} ${config.CommonJdkFlags} ` +
 				`$javacFlags $bootClasspath $classpath ` +
 				`-source $javaVersion -target $javaVersion ` +
 				`-d $outDir -s $annoDir @$out.rsp && ` +
@@ -49,6 +49,25 @@
 		},
 		"javacFlags", "bootClasspath", "classpath", "outDir", "annoDir", "javaVersion")
 
+	errorprone = pctx.AndroidStaticRule("errorprone",
+		blueprint.RuleParams{
+			Command: `rm -rf "$outDir" "$annoDir" && mkdir -p "$outDir" "$annoDir" && ` +
+				`${config.ErrorProneCmd}` +
+				`$javacFlags $bootClasspath $classpath ` +
+				`-source $javaVersion -target $javaVersion ` +
+				`-d $outDir -s $annoDir @$out.rsp && ` +
+				`find $outDir -type f | sort | ${config.JarArgsCmd} $outDir > $out`,
+			CommandDeps: []string{
+				"${config.JavaCmd}",
+				"${config.ErrorProneJavacJar}",
+				"${config.ErrorProneJar}",
+				"${config.JarArgsCmd}",
+			},
+			Rspfile:        "$out.rsp",
+			RspfileContent: "$in",
+		},
+		"javacFlags", "bootClasspath", "classpath", "outDir", "annoDir", "javaVersion")
+
 	jar = pctx.AndroidStaticRule("jar",
 		blueprint.RuleParams{
 			Command:     `${config.JarCmd} $operation ${out}.tmp $manifest $jarArgs && ${config.Zip2ZipCmd} -t -i ${out}.tmp -o ${out} && rm ${out}.tmp`,
@@ -144,12 +163,41 @@
 	return jarSpec{classFileList}
 }
 
+func RunErrorProne(ctx android.ModuleContext, srcFiles android.Paths, srcFileLists android.Paths,
+	flags javaBuilderFlags, deps android.Paths) android.Path {
+
+	classDir := android.PathForModuleOut(ctx, "classes-errorprone")
+	annoDir := android.PathForModuleOut(ctx, "anno-errorprone")
+	classFileList := android.PathForModuleOut(ctx, "classes-errorprone.list")
+
+	javacFlags := flags.javacFlags + android.JoinWithPrefix(srcFileLists.Strings(), "@")
+
+	deps = append(deps, srcFileLists...)
+
+	ctx.ModuleBuild(pctx, android.ModuleBuildParams{
+		Rule:        errorprone,
+		Description: "errorprone",
+		Output:      classFileList,
+		Inputs:      srcFiles,
+		Implicits:   deps,
+		Args: map[string]string{
+			"javacFlags":    javacFlags,
+			"bootClasspath": flags.bootClasspath,
+			"classpath":     flags.classpath,
+			"outDir":        classDir.String(),
+			"annoDir":       annoDir.String(),
+			"javaVersion":   flags.javaVersion,
+		},
+	})
+
+	return classFileList
+}
+
 func TransformClassesToJar(ctx android.ModuleContext, classes []jarSpec,
-	manifest android.OptionalPath) android.Path {
+	manifest android.OptionalPath, deps android.Paths) android.Path {
 
 	outputFile := android.PathForModuleOut(ctx, "classes-full-debug.jar")
 
-	deps := android.Paths{}
 	jarArgs := []string{}
 
 	for _, j := range classes {
diff --git a/java/config/config.go b/java/config/config.go
index 90d0fb5..c6c8fff 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -26,14 +26,17 @@
 var (
 	pctx = android.NewPackageContext("android/soong/java/config")
 
+	JavacHeapSize = "2048M"
+
 	DefaultLibraries = []string{"core-oj", "core-libart", "ext", "framework", "okhttp"}
 )
 
 func init() {
 	pctx.Import("github.com/google/blueprint/bootstrap")
 
+	pctx.StaticVariable("JavacHeapFlags", "-J-Xmx"+JavacHeapSize)
+
 	pctx.StaticVariable("CommonJdkFlags", strings.Join([]string{
-		`-J-Xmx2048M`,
 		`-Xmaxerrs 9999999`,
 		`-encoding UTF-8`,
 		`-sourcepath ""`,
diff --git a/java/config/errorprone.go b/java/config/errorprone.go
new file mode 100644
index 0000000..ddb9902
--- /dev/null
+++ b/java/config/errorprone.go
@@ -0,0 +1,100 @@
+// 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 config
+
+import (
+	"strings"
+)
+
+func init() {
+	pctx.SourcePathVariable("ErrorProneJavacJar", "external/error_prone/javac/javac-9-dev-r3297-4.jar")
+	pctx.SourcePathVariable("ErrorProneJar", "external/error_prone/error_prone/error_prone_core-2.0.19-with-dependencies.jar")
+	pctx.SourcePathsVariable("ErrorProneClasspath", ":",
+		"external/error_prone/error_prone/error_prone_annotations-2.0.19.jar",
+		"external/error_prone/checkerframework/dataflow-1.8.10.jar",
+		"external/error_prone/checkerframework/javacutil-1.8.10.jar",
+		"external/error_prone/jFormatString/jFormatString-3.0.0.jar")
+
+	// The checks that are fatal to the build.
+	pctx.StaticVariable("ErrorProneChecksError", strings.Join([]string{
+		"-Xep:AsyncCallableReturnsNull:ERROR",
+		"-Xep:AsyncFunctionReturnsNull:ERROR",
+		"-Xep:BundleDeserializationCast:ERROR",
+		"-Xep:CompatibleWithAnnotationMisuse:ERROR",
+		"-Xep:CompileTimeConstant:ERROR",
+		"-Xep:DaggerProvidesNull:ERROR",
+		"-Xep:DoNotCall:ERROR",
+		"-Xep:ForOverride:ERROR",
+		"-Xep:FunctionalInterfaceMethodChanged:ERROR",
+		"-Xep:FuturesGetCheckedIllegalExceptionType:ERROR",
+		"-Xep:GuiceAssistedInjectScoping:ERROR",
+		"-Xep:GuiceAssistedParameters:ERROR",
+		"-Xep:GuiceInjectOnFinalField:ERROR",
+		"-Xep:Immutable:ERROR",
+		"-Xep:ImmutableModification:ERROR",
+		"-Xep:IncompatibleArgumentType:ERROR",
+		"-Xep:IndexOfChar:ERROR",
+		"-Xep:InjectMoreThanOneScopeAnnotationOnClass:ERROR",
+		"-Xep:JavaxInjectOnAbstractMethod:ERROR",
+		"-Xep:JUnit4SetUpNotRun:ERROR",
+		"-Xep:JUnit4TearDownNotRun:ERROR",
+		"-Xep:JUnit4TestNotRun:ERROR",
+		"-Xep:JUnitAssertSameCheck:ERROR",
+		"-Xep:LiteByteStringUtf8:ERROR",
+		"-Xep:LoopConditionChecker:ERROR",
+		"-Xep:MockitoCast:ERROR",
+		"-Xep:MockitoUsage:ERROR",
+		"-Xep:MoreThanOneInjectableConstructor:ERROR",
+		"-Xep:MustBeClosedChecker:ERROR",
+		"-Xep:NonCanonicalStaticImport:ERROR",
+		"-Xep:NonFinalCompileTimeConstant:ERROR",
+		"-Xep:OptionalEquality:ERROR",
+		"-Xep:OverlappingQualifierAndScopeAnnotation:ERROR",
+		"-Xep:PackageInfo:ERROR",
+		"-Xep:PreconditionsCheckNotNull:ERROR",
+		"-Xep:PreconditionsCheckNotNullPrimitive:ERROR",
+		"-Xep:ProtoFieldNullComparison:ERROR",
+		"-Xep:ProvidesMethodOutsideOfModule:ERROR",
+		"-Xep:RestrictedApiChecker:ERROR",
+		"-Xep:SelfAssignment:ERROR",
+		"-Xep:StreamToString:ERROR",
+		"-Xep:SuppressWarningsDeprecated:ERROR",
+		"-Xep:ThrowIfUncheckedKnownChecked:ERROR",
+		"-Xep:ThrowNull:ERROR",
+		"-Xep:TypeParameterQualifier:ERROR",
+		"-Xep:UnnecessaryTypeArgument:ERROR",
+		"-Xep:UnusedAnonymousClass:ERROR",
+	}, " "))
+
+	pctx.StaticVariable("ErrorProneHeapFlags", "-Xmx"+JavacHeapSize)
+
+	pctx.StaticVariable("ErrorProneFlags", strings.Join([]string{
+		"com.google.errorprone.ErrorProneCompiler",
+		"-Xdiags:verbose",
+		"-XDcompilePolicy=simple",
+		"-XDallowBetterNullChecks=false",
+		"-XDusePolyAttribution=true",
+		"-XDuseStrictMethodClashCheck=true",
+		"-XDuseStructuralMostSpecificResolution=true",
+		"-XDuseGraphInference=true",
+		"-XDandroidCompatible=true",
+		"-XepAllErrorsAsWarnings",
+	}, " "))
+
+	pctx.StaticVariable("ErrorProneCmd",
+		"${JavaCmd} ${ErrorProneHeapFlags} -Xbootclasspath/p:${ErrorProneJavacJar} "+
+			"-cp ${ErrorProneJar}:${ErrorProneClasspath} "+
+			"${ErrorProneFlags} ${CommonJdkFlags} ${ErrorProneChecksError}")
+}
diff --git a/java/java.go b/java/java.go
index ac88020..e2e15d4 100644
--- a/java/java.go
+++ b/java/java.go
@@ -330,6 +330,8 @@
 
 	srcFileLists = append(srcFileLists, j.ExtraSrcLists...)
 
+	var extraJarDeps android.Paths
+
 	if len(srcFiles) > 0 {
 		// Compile java sources into .class files
 		classes := TransformJavaToClasses(ctx, srcFiles, srcFileLists, flags, deps)
@@ -337,6 +339,17 @@
 			return
 		}
 
+		if ctx.AConfig().IsEnvTrue("RUN_ERROR_PRONE") {
+			// 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).  Add the classes as a dependency to
+			// the jar command so the two compiles can run in parallel.
+			// TODO(ccross): Once we always compile with javac9 we may be able to conditionally
+			//    enable error-prone without affecting the output class files.
+			errorprone := RunErrorProne(ctx, srcFiles, srcFileLists, flags, deps)
+			extraJarDeps = append(extraJarDeps, errorprone)
+		}
+
 		classJarSpecs = append([]jarSpec{classes}, classJarSpecs...)
 	}
 
@@ -349,7 +362,7 @@
 	allJarSpecs = append(allJarSpecs, resourceJarSpecs...)
 
 	// Combine classes + resources into classes-full-debug.jar
-	outputFile := TransformClassesToJar(ctx, allJarSpecs, manifest)
+	outputFile := TransformClassesToJar(ctx, allJarSpecs, manifest, extraJarDeps)
 	if ctx.Failed() {
 		return
 	}
@@ -575,7 +588,7 @@
 		j.resourceJarSpecs = append(j.resourceJarSpecs, resourceJarSpec)
 	}
 
-	j.combinedClasspathFile = TransformClassesToJar(ctx, j.classJarSpecs, android.OptionalPath{})
+	j.combinedClasspathFile = TransformClassesToJar(ctx, j.classJarSpecs, android.OptionalPath{}, nil)
 
 	ctx.InstallFileName(android.PathForModuleInstall(ctx, "framework"),
 		ctx.ModuleName()+".jar", j.combinedClasspathFile)
diff --git a/third_party/zip/android.go b/third_party/zip/android.go
index f3b6055..bde3afa 100644
--- a/third_party/zip/android.go
+++ b/third_party/zip/android.go
@@ -19,6 +19,8 @@
 	"io"
 )
 
+const DataDescriptorFlag = 0x8
+
 func (w *Writer) CopyFrom(orig *File, newName string) error {
 	if w.last != nil && !w.last.closed {
 		if err := w.last.close(); err != nil {
@@ -30,7 +32,7 @@
 	fileHeader := orig.FileHeader
 	fileHeader.Name = newName
 	fh := &fileHeader
-	fh.Flags |= 0x8
+	fh.Flags |= DataDescriptorFlag
 
 	// The zip64 extras change between the Central Directory and Local File Header, while we use
 	// the same structure for both. The Local File Haeder is taken care of by us writing a data
@@ -122,7 +124,7 @@
 		return nil, errors.New("archive/zip: invalid duplicate FileHeader")
 	}
 
-	fh.Flags |= 0x8 // we will write a data descriptor
+	fh.Flags |= DataDescriptorFlag // we will write a data descriptor
 
 	fh.CreatorVersion = fh.CreatorVersion&0xff00 | zipVersion20 // preserve compatibility byte
 	fh.ReaderVersion = zipVersion20
@@ -149,6 +151,17 @@
 	return fw, nil
 }
 
+// Updated version of CreateHeader that doesn't enforce writing a data descriptor
+func (w *Writer) CreateHeaderAndroid(fh *FileHeader) (io.Writer, error) {
+	writeDataDescriptor := fh.Method != Store
+	if writeDataDescriptor {
+		fh.Flags &= DataDescriptorFlag
+	} else {
+		fh.Flags &= ^uint16(DataDescriptorFlag)
+	}
+	return w.createHeaderImpl(fh)
+}
+
 type compressedFileWriter struct {
 	fileWriter
 }
diff --git a/third_party/zip/writer.go b/third_party/zip/writer.go
index 3a9292e..4c5eb78 100644
--- a/third_party/zip/writer.go
+++ b/third_party/zip/writer.go
@@ -192,6 +192,15 @@
 	return w.CreateHeader(header)
 }
 
+// BEGIN ANDROID CHANGE separate createHeaderImpl from CreateHeader
+// Legacy version of CreateHeader
+func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) {
+	fh.Flags |= DataDescriptorFlag // writing a data descriptor
+	return w.createHeaderImpl(fh)
+}
+
+// END ANDROID CHANGE
+
 // CreateHeader adds a file to the zip file using the provided FileHeader
 // for the file metadata.
 // It returns a Writer to which the file contents should be written.
@@ -199,7 +208,10 @@
 // The file's contents must be written to the io.Writer before the next
 // call to Create, CreateHeader, or Close. The provided FileHeader fh
 // must not be modified after a call to CreateHeader.
-func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) {
+
+// BEGIN ANDROID CHANGE separate createHeaderImpl from CreateHeader
+func (w *Writer) createHeaderImpl(fh *FileHeader) (io.Writer, error) {
+	// END ANDROID CHANGE
 	if w.last != nil && !w.last.closed {
 		if err := w.last.close(); err != nil {
 			return nil, err
@@ -209,9 +221,9 @@
 		// See https://golang.org/issue/11144 confusion.
 		return nil, errors.New("archive/zip: invalid duplicate FileHeader")
 	}
-
-	fh.Flags |= 0x8 // we will write a data descriptor
-
+	// BEGIN ANDROID CHANGE move the setting of DataDescriptorFlag into CreateHeader
+	// fh.Flags |= 0x8 // we will write a data descriptor
+	// END ANDROID CHANGE
 	fh.CreatorVersion = fh.CreatorVersion&0xff00 | zipVersion20 // preserve compatibility byte
 	fh.ReaderVersion = zipVersion20
 
@@ -255,9 +267,32 @@
 	b.uint16(h.Method)
 	b.uint16(h.ModifiedTime)
 	b.uint16(h.ModifiedDate)
-	b.uint32(0) // since we are writing a data descriptor crc32,
-	b.uint32(0) // compressed size,
-	b.uint32(0) // and uncompressed size should be zero
+	// BEGIN ANDROID CHANGE populate header size fields and crc field if not writing a data descriptor
+	if h.Flags&DataDescriptorFlag != 0 {
+		// since we are writing a data descriptor, these fields should be 0
+		b.uint32(0) // crc32,
+		b.uint32(0) // compressed size,
+		b.uint32(0) // uncompressed size
+	} else {
+		b.uint32(h.CRC32)
+
+		if h.CompressedSize64 > uint32max || h.UncompressedSize64 > uint32max {
+			panic("skipping writing the data descriptor for a 64-bit value is not yet supported")
+		}
+		compressedSize := uint32(h.CompressedSize64)
+		if compressedSize == 0 {
+			compressedSize = h.CompressedSize
+		}
+
+		uncompressedSize := uint32(h.UncompressedSize64)
+		if uncompressedSize == 0 {
+			uncompressedSize = h.UncompressedSize
+		}
+
+		b.uint32(compressedSize)
+		b.uint32(uncompressedSize)
+	}
+	// END ANDROID CHANGE
 	b.uint16(uint16(len(h.Name)))
 	b.uint16(uint16(len(h.Extra)))
 	if _, err := w.Write(buf[:]); err != nil {
@@ -306,7 +341,9 @@
 	return w.rawCount.Write(p)
 }
 
-func (w *fileWriter) close() error {
+// BEGIN ANDROID CHANGE give the return value a name
+func (w *fileWriter) close() (err error) {
+	// END ANDROID CHANGE
 	if w.closed {
 		return errors.New("zip: file closed twice")
 	}
@@ -330,28 +367,32 @@
 		fh.UncompressedSize = uint32(fh.UncompressedSize64)
 	}
 
-	// Write data descriptor. This is more complicated than one would
-	// think, see e.g. comments in zipfile.c:putextended() and
-	// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7073588.
-	// The approach here is to write 8 byte sizes if needed without
-	// adding a zip64 extra in the local header (too late anyway).
-	var buf []byte
-	if fh.isZip64() {
-		buf = make([]byte, dataDescriptor64Len)
-	} else {
-		buf = make([]byte, dataDescriptorLen)
+	// BEGIN ANDROID CHANGE only write data descriptor if the flag is set
+	if fh.Flags&DataDescriptorFlag != 0 {
+		// Write data descriptor. This is more complicated than one would
+		// think, see e.g. comments in zipfile.c:putextended() and
+		// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7073588.
+		// The approach here is to write 8 byte sizes if needed without
+		// adding a zip64 extra in the local header (too late anyway).
+		var buf []byte
+		if fh.isZip64() {
+			buf = make([]byte, dataDescriptor64Len)
+		} else {
+			buf = make([]byte, dataDescriptorLen)
+		}
+		b := writeBuf(buf)
+		b.uint32(dataDescriptorSignature) // de-facto standard, required by OS X
+		b.uint32(fh.CRC32)
+		if fh.isZip64() {
+			b.uint64(fh.CompressedSize64)
+			b.uint64(fh.UncompressedSize64)
+		} else {
+			b.uint32(fh.CompressedSize)
+			b.uint32(fh.UncompressedSize)
+		}
+		_, err = w.zipw.Write(buf)
 	}
-	b := writeBuf(buf)
-	b.uint32(dataDescriptorSignature) // de-facto standard, required by OS X
-	b.uint32(fh.CRC32)
-	if fh.isZip64() {
-		b.uint64(fh.CompressedSize64)
-		b.uint64(fh.UncompressedSize64)
-	} else {
-		b.uint32(fh.CompressedSize)
-		b.uint32(fh.UncompressedSize)
-	}
-	_, err := w.zipw.Write(buf)
+	// END ANDROID CHANGE
 	return err
 }