Merge "DO NOT MERGE - Merge PPRL.190205.001 into master"
diff --git a/Android.bp b/Android.bp
index b407314..36a428a 100644
--- a/Android.bp
+++ b/Android.bp
@@ -250,6 +250,7 @@
         "java/builder.go",
         "java/dex.go",
         "java/dexpreopt.go",
+        "java/dexpreopt_bootjars.go",
         "java/droiddoc.go",
         "java/gen.go",
         "java/genrule.go",
@@ -267,6 +268,7 @@
         "java/sdk_library.go",
         "java/support_libraries.go",
         "java/system_modules.go",
+        "java/testing.go",
     ],
     testSrcs: [
         "java/app_test.go",
diff --git a/android/arch.go b/android/arch.go
index b88b275..151cabd 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -107,6 +107,7 @@
 
 var archVariants = map[ArchType][]string{
 	Arm: {
+		"armv7-a",
 		"armv7-a-neon",
 		"armv8-a",
 		"armv8-2a",
@@ -1314,6 +1315,7 @@
 
 func getMegaDeviceConfig() []archConfig {
 	return []archConfig{
+		{"arm", "armv7-a", "generic", []string{"armeabi-v7a"}},
 		{"arm", "armv7-a-neon", "generic", []string{"armeabi-v7a"}},
 		{"arm", "armv7-a-neon", "cortex-a7", []string{"armeabi-v7a"}},
 		{"arm", "armv7-a-neon", "cortex-a8", []string{"armeabi-v7a"}},
@@ -1365,7 +1367,7 @@
 
 func getNdkAbisConfig() []archConfig {
 	return []archConfig{
-		{"arm", "armv7-a-neon", "", []string{"armeabi"}},
+		{"arm", "armv7-a", "", []string{"armeabi"}},
 		{"arm64", "armv8-a", "", []string{"arm64-v8a"}},
 		{"x86", "", "", []string{"x86"}},
 		{"x86_64", "", "", []string{"x86_64"}},
diff --git a/android/config.go b/android/config.go
index 63788b7..7d2e829 100644
--- a/android/config.go
+++ b/android/config.go
@@ -779,7 +779,11 @@
 	return c.productVariables.PreoptBootJars
 }
 
-func (c *config) DisableDexPreopt(name string) bool {
+func (c *config) DisableDexPreopt() bool {
+	return Bool(c.productVariables.DisableDexPreopt)
+}
+
+func (c *config) DisableDexPreoptForModule(name string) bool {
 	return Bool(c.productVariables.DisableDexPreopt) || InList(name, c.productVariables.DisableDexPreoptModules)
 }
 
@@ -944,6 +948,8 @@
 	return "", false
 }
 
+// SecondArchIsTranslated returns true if the primary device arch is X86 or X86_64 and the device also has an arch
+// that is Arm or Arm64.
 func (c *config) SecondArchIsTranslated() bool {
 	deviceTargets := c.Targets[Android]
 	if len(deviceTargets) < 2 {
@@ -952,8 +958,7 @@
 
 	arch := deviceTargets[0].Arch
 
-	return (arch.ArchType == X86 || arch.ArchType == X86_64) &&
-		(hasArmAbi(arch) || hasArmAndroidArch(deviceTargets))
+	return (arch.ArchType == X86 || arch.ArchType == X86_64) && hasArmAndroidArch(deviceTargets)
 }
 
 func (c *config) IntegerOverflowDisabledForPath(path string) bool {
diff --git a/android/makevars.go b/android/makevars.go
index 2c2fb6f..c011ea6 100644
--- a/android/makevars.go
+++ b/android/makevars.go
@@ -90,6 +90,25 @@
 	makeVarsProviders = append(makeVarsProviders, makeVarsProvider{pctx, provider})
 }
 
+// SingletonMakeVarsProvider is a Singleton with an extra method to provide extra values to be exported to Make.
+type SingletonMakeVarsProvider interface {
+	Singleton
+
+	// MakeVars uses a MakeVarsContext to provide extra values to be exported to Make.
+	MakeVars(ctx MakeVarsContext)
+}
+
+// registerSingletonMakeVarsProvider adds a singleton that implements SingletonMakeVarsProvider to the list of
+// MakeVarsProviders to run.
+func registerSingletonMakeVarsProvider(singleton SingletonMakeVarsProvider) {
+	makeVarsProviders = append(makeVarsProviders, makeVarsProvider{pctx, SingletonmakeVarsProviderAdapter(singleton)})
+}
+
+// SingletonmakeVarsProviderAdapter converts a SingletonMakeVarsProvider to a MakeVarsProvider.
+func SingletonmakeVarsProviderAdapter(singleton SingletonMakeVarsProvider) MakeVarsProvider {
+	return func(ctx MakeVarsContext) { singleton.MakeVars(ctx) }
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 func makeVarsSingletonFunc() Singleton {
diff --git a/android/onceper.go b/android/onceper.go
index f06f428..5ad17fa 100644
--- a/android/onceper.go
+++ b/android/onceper.go
@@ -70,7 +70,7 @@
 		panic(fmt.Errorf("Get() called before Once()"))
 	}
 
-	return v
+	return once.maybeWaitFor(key, v)
 }
 
 // OnceStringSlice is the same as Once, but returns the value cast to a []string
diff --git a/android/onceper_test.go b/android/onceper_test.go
index f27799b..95303ba 100644
--- a/android/onceper_test.go
+++ b/android/onceper_test.go
@@ -16,6 +16,7 @@
 
 import (
 	"testing"
+	"time"
 )
 
 func TestOncePer_Once(t *testing.T) {
@@ -34,6 +35,21 @@
 	}
 }
 
+func TestOncePer_Once_wait(t *testing.T) {
+	once := OncePer{}
+	key := NewOnceKey("key")
+
+	ch := make(chan bool)
+
+	go once.Once(key, func() interface{} { close(ch); time.Sleep(100 * time.Millisecond); return "foo" })
+	<-ch
+	a := once.Once(key, func() interface{} { return "bar" }).(string)
+
+	if a != "foo" {
+		t.Errorf("expect %q, got %q", "foo", a)
+	}
+}
+
 func TestOncePer_Get(t *testing.T) {
 	once := OncePer{}
 	key := NewOnceKey("key")
@@ -65,6 +81,21 @@
 	once.Get(key)
 }
 
+func TestOncePer_Get_wait(t *testing.T) {
+	once := OncePer{}
+	key := NewOnceKey("key")
+
+	ch := make(chan bool)
+
+	go once.Once(key, func() interface{} { close(ch); time.Sleep(100 * time.Millisecond); return "foo" })
+	<-ch
+	a := once.Get(key).(string)
+
+	if a != "foo" {
+		t.Errorf("expect %q, got %q", "foo", a)
+	}
+}
+
 func TestOncePer_OnceStringSlice(t *testing.T) {
 	once := OncePer{}
 	key := NewOnceKey("key")
diff --git a/android/paths.go b/android/paths.go
index 31500ab..3366db1 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -677,6 +677,15 @@
 	return OutputPath{basePath{path, ctx.Config(), ""}}
 }
 
+// PathsForOutput returns Paths rooted from buildDir
+func PathsForOutput(ctx PathContext, paths []string) WritablePaths {
+	ret := make(WritablePaths, len(paths))
+	for i, path := range paths {
+		ret[i] = PathForOutput(ctx, path)
+	}
+	return ret
+}
+
 func (p OutputPath) writablePath() {}
 
 func (p OutputPath) String() string {
@@ -707,6 +716,18 @@
 	return ret
 }
 
+// InSameDir creates a new OutputPath from the directory of the current OutputPath joined with the elements in paths.
+func (p OutputPath) InSameDir(ctx PathContext, paths ...string) OutputPath {
+	path, err := validatePath(paths...)
+	if err != nil {
+		reportPathError(ctx, err)
+	}
+
+	ret := PathForOutput(ctx, filepath.Dir(p.path), path)
+	ret.rel = p.rel
+	return ret
+}
+
 // PathForIntermediates returns an OutputPath representing the top-level
 // intermediates directory.
 func PathForIntermediates(ctx PathContext, paths ...string) OutputPath {
@@ -1019,6 +1040,14 @@
 	return p.path
 }
 
+type testWritablePath struct {
+	testPath
+}
+
+func (p testPath) writablePath() {}
+
+// PathForTesting returns a Path constructed from joining the elements of paths with '/'.  It should only be used from
+// within tests.
 func PathForTesting(paths ...string) Path {
 	p, err := validateSafePath(paths...)
 	if err != nil {
@@ -1027,7 +1056,8 @@
 	return testPath{basePath{path: p, rel: p}}
 }
 
-func PathsForTesting(strs []string) Paths {
+// PathsForTesting returns a Path constructed from each element in strs. It should only be used from within tests.
+func PathsForTesting(strs ...string) Paths {
 	p := make(Paths, len(strs))
 	for i, s := range strs {
 		p[i] = PathForTesting(s)
@@ -1036,6 +1066,45 @@
 	return p
 }
 
+// WritablePathForTesting returns a Path constructed from joining the elements of paths with '/'.  It should only be
+// used from within tests.
+func WritablePathForTesting(paths ...string) WritablePath {
+	p, err := validateSafePath(paths...)
+	if err != nil {
+		panic(err)
+	}
+	return testWritablePath{testPath{basePath{path: p, rel: p}}}
+}
+
+// WritablePathsForTesting returns a Path constructed from each element in strs. It should only be used from within
+// tests.
+func WritablePathsForTesting(strs ...string) WritablePaths {
+	p := make(WritablePaths, len(strs))
+	for i, s := range strs {
+		p[i] = WritablePathForTesting(s)
+	}
+
+	return p
+}
+
+type testPathContext struct {
+	config Config
+	fs     pathtools.FileSystem
+}
+
+func (x *testPathContext) Fs() pathtools.FileSystem   { return x.fs }
+func (x *testPathContext) Config() Config             { return x.config }
+func (x *testPathContext) AddNinjaFileDeps(...string) {}
+
+// PathContextForTesting returns a PathContext that can be used in tests, for example to create an OutputPath with
+// PathForOutput.
+func PathContextForTesting(config Config, fs map[string][]byte) PathContext {
+	return &testPathContext{
+		config: config,
+		fs:     pathtools.MockFs(fs),
+	}
+}
+
 // Rel performs the same function as filepath.Rel, but reports errors to a PathContext, and reports an error if
 // targetPath is not inside basePath.
 func Rel(ctx PathContext, basePath string, targetPath string) string {
diff --git a/android/paths_test.go b/android/paths_test.go
index 1972591..3b6d2ec 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -703,3 +703,15 @@
 	// Output:
 	// out/system/framework/boot.art out/system/framework/boot.oat
 }
+
+func ExampleOutputPath_FileInSameDir() {
+	ctx := &configErrorWrapper{
+		config: TestConfig("out", nil),
+	}
+	p := PathForOutput(ctx, "system/framework/boot.art")
+	p2 := p.InSameDir(ctx, "oat", "arm", "boot.vdex")
+	fmt.Println(p, p2)
+
+	// Output:
+	// out/system/framework/boot.art out/system/framework/oat/arm/boot.vdex
+}
diff --git a/android/register.go b/android/register.go
index 19745fe..93c2870 100644
--- a/android/register.go
+++ b/android/register.go
@@ -58,6 +58,9 @@
 func SingletonFactoryAdaptor(factory SingletonFactory) blueprint.SingletonFactory {
 	return func() blueprint.Singleton {
 		singleton := factory()
+		if makevars, ok := singleton.(SingletonMakeVarsProvider); ok {
+			registerSingletonMakeVarsProvider(makevars)
+		}
 		return singletonAdaptor{singleton}
 	}
 }
diff --git a/android/util.go b/android/util.go
index 92ab845..8fc159d 100644
--- a/android/util.go
+++ b/android/util.go
@@ -20,6 +20,11 @@
 	"strings"
 )
 
+// CopyOf returns a new slice that has the same contents as s.
+func CopyOf(s []string) []string {
+	return append([]string(nil), s...)
+}
+
 func JoinWithPrefix(strs []string, prefix string) string {
 	if len(strs) == 0 {
 		return ""
diff --git a/android/util_test.go b/android/util_test.go
index 1c791b2..2e5eb07 100644
--- a/android/util_test.go
+++ b/android/util_test.go
@@ -15,6 +15,7 @@
 package android
 
 import (
+	"fmt"
 	"reflect"
 	"testing"
 )
@@ -359,3 +360,47 @@
 		})
 	}
 }
+
+func ExampleCopyOf() {
+	a := []string{"1", "2", "3"}
+	b := CopyOf(a)
+	a[0] = "-1"
+	fmt.Printf("a = %q\n", a)
+	fmt.Printf("b = %q\n", b)
+
+	// Output:
+	// a = ["-1" "2" "3"]
+	// b = ["1" "2" "3"]
+}
+
+func ExampleCopyOf_append() {
+	a := make([]string, 1, 2)
+	a[0] = "foo"
+
+	fmt.Println("Without CopyOf:")
+	b := append(a, "bar")
+	c := append(a, "baz")
+	fmt.Printf("a = %q\n", a)
+	fmt.Printf("b = %q\n", b)
+	fmt.Printf("c = %q\n", c)
+
+	a = make([]string, 1, 2)
+	a[0] = "foo"
+
+	fmt.Println("With CopyOf:")
+	b = append(CopyOf(a), "bar")
+	c = append(CopyOf(a), "baz")
+	fmt.Printf("a = %q\n", a)
+	fmt.Printf("b = %q\n", b)
+	fmt.Printf("c = %q\n", c)
+
+	// Output:
+	// Without CopyOf:
+	// a = ["foo"]
+	// b = ["foo" "baz"]
+	// c = ["foo" "baz"]
+	// With CopyOf:
+	// a = ["foo"]
+	// b = ["foo" "bar"]
+	// c = ["foo" "baz"]
+}
diff --git a/apex/apex.go b/apex/apex.go
index 3b06a99..408415e 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -379,6 +379,13 @@
 	outputFiles      map[apexPackaging]android.WritablePath
 	installDir       android.OutputPath
 
+	public_key_file   android.Path
+	private_key_file  android.Path
+	bundle_public_key bool
+
+	container_certificate_file android.Path
+	container_private_key_file android.Path
+
 	// list of files to be included in this apex
 	filesInfo []apexFile
 
@@ -635,10 +642,6 @@
 func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	filesInfo := []apexFile{}
 
-	var keyFile android.Path
-	var pubKeyFile android.Path
-	var certificate java.Certificate
-
 	if a.properties.Payload_type == nil || *a.properties.Payload_type == "image" {
 		a.apexTypes = imageApex
 	} else if *a.properties.Payload_type == "zip" {
@@ -704,20 +707,20 @@
 				}
 			case keyTag:
 				if key, ok := child.(*apexKey); ok {
-					keyFile = key.private_key_file
-					if !key.installable() && ctx.Config().Debuggable() {
-						// If the key is not installed, bundled it with the APEX.
-						// Note: this bundled key is valid only for non-production builds
-						// (eng/userdebug).
-						pubKeyFile = key.public_key_file
-					}
+					a.private_key_file = key.private_key_file
+					a.public_key_file = key.public_key_file
+					// If the key is not installed, bundled it with the APEX.
+					// Note: this bundled key is valid only for non-production builds
+					// (eng/userdebug).
+					a.bundle_public_key = !key.installable() && ctx.Config().Debuggable()
 					return false
 				} else {
 					ctx.PropertyErrorf("key", "%q is not an apex_key module", depName)
 				}
 			case certificateTag:
 				if dep, ok := child.(*java.AndroidAppCertificate); ok {
-					certificate = dep.Certificate
+					a.container_certificate_file = dep.Certificate.Pem
+					a.container_private_key_file = dep.Certificate.Key
 					return false
 				} else {
 					ctx.ModuleErrorf("certificate dependency %q must be an android_app_certificate module", depName)
@@ -741,7 +744,7 @@
 	})
 
 	a.flattened = ctx.Config().FlattenApex() && !ctx.Config().UnbundledBuild()
-	if keyFile == nil {
+	if a.private_key_file == nil {
 		ctx.PropertyErrorf("key", "private_key for %q could not be found", String(a.properties.Key))
 		return
 	}
@@ -775,30 +778,28 @@
 	a.filesInfo = filesInfo
 
 	if a.apexTypes.zip() {
-		a.buildUnflattenedApex(ctx, keyFile, pubKeyFile, certificate, zipApex)
+		a.buildUnflattenedApex(ctx, zipApex)
 	}
 	if a.apexTypes.image() {
 		// Build rule for unflattened APEX is created even when ctx.Config().FlattenApex()
 		// is true. This is to support referencing APEX via ":<module_name" syntax
 		// in other modules. It is in AndroidMk where the selection of flattened
 		// or unflattened APEX is made.
-		a.buildUnflattenedApex(ctx, keyFile, pubKeyFile, certificate, imageApex)
+		a.buildUnflattenedApex(ctx, imageApex)
 		a.buildFlattenedApex(ctx)
 	}
 }
 
-func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext, keyFile android.Path,
-	pubKeyFile android.Path, certificate java.Certificate, apexType apexPackaging) {
+func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext, apexType apexPackaging) {
 	cert := String(a.properties.Certificate)
 	if cert != "" && android.SrcIsModule(cert) == "" {
 		defaultDir := ctx.Config().DefaultAppCertificateDir(ctx)
-		certificate = java.Certificate{
-			defaultDir.Join(ctx, cert+".x509.pem"),
-			defaultDir.Join(ctx, cert+".pk8"),
-		}
+		a.container_certificate_file = defaultDir.Join(ctx, cert+".x509.pem")
+		a.container_private_key_file = defaultDir.Join(ctx, cert+".pk8")
 	} else if cert == "" {
 		pem, key := ctx.Config().DefaultAppCertificate(ctx)
-		certificate = java.Certificate{pem, key}
+		a.container_certificate_file = pem
+		a.container_private_key_file = key
 	}
 
 	manifest := ctx.ExpandSource(proptools.StringDefault(a.properties.Manifest, "apex_manifest.json"), "manifest")
@@ -886,10 +887,10 @@
 		optFlags := []string{}
 
 		// Additional implicit inputs.
-		implicitInputs = append(implicitInputs, cannedFsConfig, fileContexts, keyFile)
-		if pubKeyFile != nil {
-			implicitInputs = append(implicitInputs, pubKeyFile)
-			optFlags = append(optFlags, "--pubkey "+pubKeyFile.String())
+		implicitInputs = append(implicitInputs, cannedFsConfig, fileContexts, a.private_key_file)
+		if a.bundle_public_key {
+			implicitInputs = append(implicitInputs, a.public_key_file)
+			optFlags = append(optFlags, "--pubkey "+a.public_key_file.String())
 		}
 
 		manifestPackageName, overridden := ctx.DeviceConfig().OverrideManifestPackageNameFor(ctx.ModuleName())
@@ -915,7 +916,7 @@
 				"manifest":         manifest.String(),
 				"file_contexts":    fileContexts.String(),
 				"canned_fs_config": cannedFsConfig.String(),
-				"key":              keyFile.String(),
+				"key":              a.private_key_file.String(),
 				"opt_flags":        strings.Join(optFlags, " "),
 			},
 		})
@@ -962,14 +963,14 @@
 		Output:      a.outputFiles[apexType],
 		Input:       unsignedOutputFile,
 		Args: map[string]string{
-			"certificates": strings.Join([]string{certificate.Pem.String(), certificate.Key.String()}, " "),
+			"certificates": a.container_certificate_file.String() + " " + a.container_private_key_file.String(),
 			"flags":        "-a 4096", //alignment
 		},
 	})
 
 	// Install to $OUT/soong/{target,host}/.../apex
 	if a.installable() && (!ctx.Config().FlattenApex() || apexType.zip()) {
-		ctx.InstallFile(android.PathForModuleInstall(ctx, "apex"), ctx.ModuleName()+suffix, a.outputFiles[apexType])
+		ctx.InstallFile(a.installDir, ctx.ModuleName()+suffix, a.outputFiles[apexType])
 	}
 }
 
diff --git a/apex/key.go b/apex/key.go
index 5282416..4c83861 100644
--- a/apex/key.go
+++ b/apex/key.go
@@ -17,6 +17,7 @@
 import (
 	"fmt"
 	"io"
+	"strings"
 
 	"android/soong/android"
 
@@ -27,6 +28,8 @@
 
 func init() {
 	android.RegisterModuleType("apex_key", apexKeyFactory)
+	android.RegisterSingletonType("apex_keys_text", apexKeysTextFactory)
+	android.RegisterMakeVarsProvider(pctx, apexKeysFileProvider)
 }
 
 type apexKey struct {
@@ -102,3 +105,53 @@
 		},
 	}
 }
+
+////////////////////////////////////////////////////////////////////////
+// apex_keys_text
+type apexKeysText struct{}
+
+func (s *apexKeysText) GenerateBuildActions(ctx android.SingletonContext) {
+	output := android.PathForOutput(ctx, "apexkeys.txt")
+	*apexKeysFile(ctx.Config()) = output.String()
+	var filecontent strings.Builder
+	ctx.VisitAllModules(func(module android.Module) {
+		if m, ok := module.(android.Module); ok && !m.Enabled() {
+			return
+		}
+
+		if m, ok := module.(*apexBundle); ok {
+			fmt.Fprintf(&filecontent,
+				"name=%q public_key=%q private_key=%q container_certificate=%q container_private_key=%q\\n",
+				m.Name()+".apex",
+				m.public_key_file.String(),
+				m.private_key_file.String(),
+				m.container_certificate_file.String(),
+				m.container_private_key_file.String())
+		}
+	})
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        android.WriteFile,
+		Description: "apex_keys.txt",
+		Output:      output,
+		Args: map[string]string{
+			"content": filecontent.String(),
+		},
+	})
+}
+
+var apexKeysFileKey = android.NewOnceKey("apexKeysFile")
+
+func apexKeysFile(config android.Config) *string {
+	return config.Once(apexKeysFileKey, func() interface{} {
+		str := ""
+		return &str
+	}).(*string)
+}
+
+func apexKeysTextFactory() android.Singleton {
+	return &apexKeysText{}
+}
+
+func apexKeysFileProvider(ctx android.MakeVarsContext) {
+	ctx.Strict("SOONG_APEX_KEYS_FILE", *apexKeysFile(ctx.Config()))
+}
diff --git a/cc/builder.go b/cc/builder.go
index 6e24d56..97ae806 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -197,8 +197,8 @@
 	sAbiDiff = pctx.AndroidRuleFunc("sAbiDiff",
 		func(ctx android.PackageRuleContext) blueprint.RuleParams {
 			// TODO(b/78139997): Add -check-all-apis back
-			commandStr := "($sAbiDiffer $allowFlags -lib $libName -arch $arch -o ${out} -new $in -old $referenceDump)"
-			commandStr += "|| (echo ' ---- Please update abi references by running $$ANDROID_BUILD_TOP/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l ${libName} ----'"
+			commandStr := "($sAbiDiffer ${allowFlags} -lib ${libName} -arch ${arch} -o ${out} -new ${in} -old ${referenceDump})"
+			commandStr += "|| (echo 'error: Please update ABI references with: $$ANDROID_BUILD_TOP/development/vndk/tools/header-checker/utils/create_reference_dumps.py ${createReferenceDumpFlags} -l ${libName}'"
 			commandStr += " && (mkdir -p $$DIST_DIR/abidiffs && cp ${out} $$DIST_DIR/abidiffs/)"
 			commandStr += " && exit 1)"
 			return blueprint.RuleParams{
@@ -206,7 +206,7 @@
 				CommandDeps: []string{"$sAbiDiffer"},
 			}
 		},
-		"allowFlags", "referenceDump", "libName", "arch")
+		"allowFlags", "referenceDump", "libName", "arch", "createReferenceDumpFlags")
 
 	unzipRefSAbiDump = pctx.AndroidStaticRule("unzipRefSAbiDump",
 		blueprint.RuleParams{
@@ -711,16 +711,19 @@
 }
 
 func SourceAbiDiff(ctx android.ModuleContext, inputDump android.Path, referenceDump android.Path,
-	baseName, exportedHeaderFlags string, isVndkExt bool) android.OptionalPath {
+	baseName, exportedHeaderFlags string, isLlndk, isVndkExt bool) android.OptionalPath {
 
 	outputFile := android.PathForModuleOut(ctx, baseName+".abidiff")
 	libName := strings.TrimSuffix(baseName, filepath.Ext(baseName))
+	createReferenceDumpFlags := ""
+
 	localAbiCheckAllowFlags := append([]string(nil), abiCheckAllowFlags...)
 	if exportedHeaderFlags == "" {
 		localAbiCheckAllowFlags = append(localAbiCheckAllowFlags, "-advice-only")
 	}
-	if inList(libName, llndkLibraries) {
+	if isLlndk {
 		localAbiCheckAllowFlags = append(localAbiCheckAllowFlags, "-consider-opaque-types-different")
+		createReferenceDumpFlags = "--llndk"
 	}
 	if isVndkExt {
 		localAbiCheckAllowFlags = append(localAbiCheckAllowFlags, "-allow-extensions")
@@ -733,10 +736,11 @@
 		Input:       inputDump,
 		Implicit:    referenceDump,
 		Args: map[string]string{
-			"referenceDump": referenceDump.String(),
-			"libName":       libName,
-			"arch":          ctx.Arch().ArchType.Name,
-			"allowFlags":    strings.Join(localAbiCheckAllowFlags, " "),
+			"referenceDump":            referenceDump.String(),
+			"libName":                  libName,
+			"arch":                     ctx.Arch().ArchType.Name,
+			"allowFlags":               strings.Join(localAbiCheckAllowFlags, " "),
+			"createReferenceDumpFlags": createReferenceDumpFlags,
 		},
 	})
 	return android.OptionalPathForPath(outputFile)
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 22ac0d9..a0914c8 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -1039,7 +1039,7 @@
 
 func TestSplitListForSize(t *testing.T) {
 	for _, testCase := range splitListForSizeTestCases {
-		out, _ := splitListForSize(android.PathsForTesting(testCase.in), testCase.size)
+		out, _ := splitListForSize(android.PathsForTesting(testCase.in...), testCase.size)
 
 		var outStrings [][]string
 
diff --git a/cc/config/arm_device.go b/cc/config/arm_device.go
index aee16eb..cd7c410 100644
--- a/cc/config/arm_device.go
+++ b/cc/config/arm_device.go
@@ -51,6 +51,11 @@
 	}
 
 	armClangArchVariantCflags = map[string][]string{
+		"armv7-a": []string{
+			"-march=armv7-a",
+			"-mfloat-abi=softfp",
+			"-mfpu=vfpv3-d16",
+		},
 		"armv7-a-neon": []string{
 			"-march=armv7-a",
 			"-mfloat-abi=softfp",
@@ -184,6 +189,8 @@
 	pctx.StaticVariable("ArmClangThumbCflags", strings.Join(ClangFilterUnknownCflags(armThumbCflags), " "))
 
 	// Clang arch variant cflags
+	pctx.StaticVariable("ArmClangArmv7ACflags",
+		strings.Join(armClangArchVariantCflags["armv7-a"], " "))
 	pctx.StaticVariable("ArmClangArmv7ANeonCflags",
 		strings.Join(armClangArchVariantCflags["armv7-a-neon"], " "))
 	pctx.StaticVariable("ArmClangArmv8ACflags",
@@ -212,6 +219,7 @@
 
 var (
 	armClangArchVariantCflagsVar = map[string]string{
+		"armv7-a":      "${config.ArmClangArmv7ACflags}",
 		"armv7-a-neon": "${config.ArmClangArmv7ANeonCflags}",
 		"armv8-a":      "${config.ArmClangArmv8ACflags}",
 		"armv8-2a":     "${config.ArmClangArmv82ACflags}",
@@ -327,6 +335,8 @@
 		default:
 			fixCortexA8 = "-Wl,--no-fix-cortex-a8"
 		}
+	case "armv7-a":
+		fixCortexA8 = "-Wl,--fix-cortex-a8"
 	case "armv8-a", "armv8-2a":
 		// Nothing extra for armv8-a/armv8-2a
 	default:
diff --git a/cc/coverage.go b/cc/coverage.go
index cf67c9f..ad2f1e4 100644
--- a/cc/coverage.go
+++ b/cc/coverage.go
@@ -53,6 +53,10 @@
 		flags.Coverage = true
 		flags.GlobalFlags = append(flags.GlobalFlags, "--coverage", "-O0")
 		cov.linkCoverage = true
+
+		// Override -Wframe-larger-than and non-default optimization
+		// flags that the module may use.
+		flags.CFlags = append(flags.CFlags, "-Wno-frame-larger-than=", "-O0")
 	}
 
 	// Even if we don't have coverage enabled, if any of our object files were compiled
@@ -109,8 +113,6 @@
 		if mctx.Host() {
 			// TODO(dwillemsen): because of -nodefaultlibs, we must depend on libclang_rt.profile-*.a
 			// Just turn off for now.
-		} else if c.useVndk() || c.hasVendorVariant() {
-			// Do not enable coverage for VNDK libraries
 		} else if c.IsStubs() {
 			// Do not enable coverage for platform stub libraries
 		} else if c.isNDKStubLibrary() {
diff --git a/cc/library.go b/cc/library.go
index a48b45d..c23f26b 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -794,7 +794,7 @@
 		refAbiDumpFile := getRefAbiDumpFile(ctx, vndkVersion, fileName)
 		if refAbiDumpFile != nil {
 			library.sAbiDiff = SourceAbiDiff(ctx, library.sAbiOutputFile.Path(),
-				refAbiDumpFile, fileName, exportedHeaderFlags, ctx.isVndkExt())
+				refAbiDumpFile, fileName, exportedHeaderFlags, ctx.isLlndk(), ctx.isVndkExt())
 		}
 	}
 }
diff --git a/cc/test.go b/cc/test.go
index f7180b5..e9f0944 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -244,7 +244,7 @@
 func (test *testBinary) install(ctx ModuleContext, file android.Path) {
 	test.data = ctx.ExpandSources(test.Properties.Data, nil)
 	test.testConfig = tradefed.AutoGenNativeTestConfig(ctx, test.Properties.Test_config,
-		test.Properties.Test_config_template)
+		test.Properties.Test_config_template, test.Properties.Test_suites)
 
 	test.binaryDecorator.baseInstaller.dir = "nativetest"
 	test.binaryDecorator.baseInstaller.dir64 = "nativetest64"
@@ -368,7 +368,7 @@
 func (benchmark *benchmarkDecorator) install(ctx ModuleContext, file android.Path) {
 	benchmark.data = ctx.ExpandSources(benchmark.Properties.Data, nil)
 	benchmark.testConfig = tradefed.AutoGenNativeBenchmarkTestConfig(ctx, benchmark.Properties.Test_config,
-		benchmark.Properties.Test_config_template)
+		benchmark.Properties.Test_config_template, benchmark.Properties.Test_suites)
 
 	benchmark.binaryDecorator.baseInstaller.dir = filepath.Join("benchmarktest", ctx.ModuleName())
 	benchmark.binaryDecorator.baseInstaller.dir64 = filepath.Join("benchmarktest64", ctx.ModuleName())
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 1f6002e..41c7d46 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -75,8 +75,7 @@
 	bootstrap.Main(ctx.Context, configuration, configuration.ConfigFileName, configuration.ProductVariablesFileName)
 
 	if docFile != "" {
-		err := writeDocs(ctx, docFile)
-		if err != nil {
+		if err := writeDocs(ctx, docFile); err != nil {
 			fmt.Fprintf(os.Stderr, "%s", err)
 			os.Exit(1)
 		}
diff --git a/cmd/soong_build/writedocs.go b/cmd/soong_build/writedocs.go
index 8f86b33..74c854a 100644
--- a/cmd/soong_build/writedocs.go
+++ b/cmd/soong_build/writedocs.go
@@ -26,7 +26,25 @@
 	"github.com/google/blueprint/bootstrap/bpdoc"
 )
 
-func writeDocs(ctx *android.Context, filename string) error {
+type moduleTypeTemplateData struct {
+	Name       string
+	Synopsis   string
+	Properties []bpdoc.Property
+}
+
+// The properties in this map are displayed first, according to their rank.
+// TODO(jungjw): consider providing module type-dependent ranking
+var propertyRank = map[string]int{
+	"name":             0,
+	"src":              1,
+	"srcs":             2,
+	"defautls":         3,
+	"host_supported":   4,
+	"device_supported": 5,
+}
+
+// For each module type, extract its documentation and convert it to the template data.
+func moduleTypeDocsToTemplates(ctx *android.Context) ([]moduleTypeTemplateData, error) {
 	moduleTypeFactories := android.ModuleTypeFactories()
 	bpModuleTypeFactories := make(map[string]reflect.Value)
 	for moduleType, factory := range moduleTypeFactories {
@@ -35,39 +53,83 @@
 
 	packages, err := bootstrap.ModuleTypeDocs(ctx.Context, bpModuleTypeFactories)
 	if err != nil {
-		return err
+		return []moduleTypeTemplateData{}, err
 	}
-
-	buf := &bytes.Buffer{}
-
 	var moduleTypeList []*bpdoc.ModuleType
 	for _, pkg := range packages {
 		moduleTypeList = append(moduleTypeList, pkg.ModuleTypes...)
 	}
-	sort.Slice(moduleTypeList, func(i, j int) bool { return moduleTypeList[i].Name < moduleTypeList[j].Name })
 
-	unique := 0
+	result := make([]moduleTypeTemplateData, 0)
 
+	// Combine properties from all PropertyStruct's and reorder them -- first the ones
+	// with rank, then the rest of the properties in alphabetic order.
+	for _, m := range moduleTypeList {
+		item := moduleTypeTemplateData{
+			Name:       m.Name,
+			Synopsis:   m.Text,
+			Properties: make([]bpdoc.Property, 0),
+		}
+		props := make([]bpdoc.Property, 0)
+		for _, propStruct := range m.PropertyStructs {
+			props = append(props, propStruct.Properties...)
+		}
+		sort.Slice(props, func(i, j int) bool {
+			if rankI, ok := propertyRank[props[i].Name]; ok {
+				if rankJ, ok := propertyRank[props[j].Name]; ok {
+					return rankI < rankJ
+				} else {
+					return true
+				}
+			}
+			if _, ok := propertyRank[props[j].Name]; ok {
+				return false
+			}
+			return props[i].Name < props[j].Name
+		})
+		// Eliminate top-level duplicates. TODO(jungjw): improve bpdoc to handle this.
+		previousPropertyName := ""
+		for _, prop := range props {
+			if prop.Name == previousPropertyName {
+				oldProp := &item.Properties[len(item.Properties)-1].Properties
+				bpdoc.CollapseDuplicateProperties(oldProp, &prop.Properties)
+			} else {
+				item.Properties = append(item.Properties, prop)
+			}
+			previousPropertyName = prop.Name
+		}
+		result = append(result, item)
+	}
+	sort.Slice(result, func(i, j int) bool { return result[i].Name < result[j].Name })
+	return result, err
+}
+
+func writeDocs(ctx *android.Context, filename string) error {
+	buf := &bytes.Buffer{}
+
+	// We need a module name getter/setter function because I couldn't
+	// find a way to keep it in a variable defined within the template.
+	currentModuleName := ""
+	data, err := moduleTypeDocsToTemplates(ctx)
+	if err != nil {
+		return err
+	}
 	tmpl, err := template.New("file").Funcs(map[string]interface{}{
-		"unique": func() int {
-			unique++
-			return unique
-		}}).Parse(fileTemplate)
-	if err != nil {
-		return err
+		"setModule": func(moduleName string) string {
+			currentModuleName = moduleName
+			return ""
+		},
+		"getModule": func() string {
+			return currentModuleName
+		},
+	}).Parse(fileTemplate)
+	if err == nil {
+		err = tmpl.Execute(buf, data)
 	}
-
-	err = tmpl.Execute(buf, moduleTypeList)
-	if err != nil {
-		return err
+	if err == nil {
+		err = ioutil.WriteFile(filename, buf.Bytes(), 0666)
 	}
-
-	err = ioutil.WriteFile(filename, buf.Bytes(), 0666)
-	if err != nil {
-		return err
-	}
-
-	return nil
+	return err
 }
 
 const (
@@ -75,70 +137,112 @@
 <html>
 <head>
 <title>Build Docs</title>
-<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
-<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
-<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
+<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css">
+<style>
+.accordion,.simple{margin-left:1.5em;text-indent:-1.5em;margin-top:.25em}
+.collapsible{border-width:0 0 0 1;margin-left:.25em;padding-left:.25em;border-style:solid;
+  border-color:grey;display:none;}
+span.fixed{display: block; float: left; clear: left; width: 1em;}
+ul {
+	list-style-type: none;
+  margin: 0;
+  padding: 0;
+  width: 30ch;
+  background-color: #f1f1f1;
+  position: fixed;
+  height: 100%;
+  overflow: auto;
+}
+li a {
+  display: block;
+  color: #000;
+  padding: 8px 16px;
+  text-decoration: none;
+}
+
+li a.active {
+  background-color: #4CAF50;
+  color: white;
+}
+
+li a:hover:not(.active) {
+  background-color: #555;
+  color: white;
+}
+</style>
 </head>
 <body>
-<h1>Build Docs</h1>
-<div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true">
-  {{range .}}
-    {{ $collapseIndex := unique }}
-    <div class="panel panel-default">
-      <div class="panel-heading" role="tab" id="heading{{$collapseIndex}}">
-        <h2 class="panel-title">
-          <a class="collapsed" role="button" data-toggle="collapse" data-parent="#accordion" href="#collapse{{$collapseIndex}}" aria-expanded="false" aria-controls="collapse{{$collapseIndex}}">
-             {{.Name}}
-          </a>
-        </h2>
-      </div>
-    </div>
-    <div id="collapse{{$collapseIndex}}" class="panel-collapse collapse" role="tabpanel" aria-labelledby="heading{{$collapseIndex}}">
-      <div class="panel-body">
-        <p>{{.Text}}</p>
-        {{range .PropertyStructs}}
-          <p>{{.Text}}</p>
-          {{template "properties" .Properties}}
-        {{end}}
-      </div>
-    </div>
-  {{end}}
-</div>
-</body>
-</html>
+{{- /* Fixed sidebar with module types */ -}}
+<ul>
+<li><h3>Module Types:</h3></li>
+{{range $moduleType := .}}<li><a href="#{{$moduleType.Name}}">{{$moduleType.Name}}</a></li>
+{{end -}}
+</ul>
+{{/* Main panel with H1 section per module type */}}
+<div style="margin-left:30ch;padding:1px 16px;">
+<H1>Soong Modules Reference</H1>
+The latest versions of Android use the Soong build system, which greatly simplifies build
+configuration over the previous Make-based system. This site contains the generated reference
+files for the Soong build system.
+<p>
+See the <a href=https://source.android.com/setup/build/build-system>Android Build System</a>
+description for an overview of Soong and examples for its use.
 
-{{define "properties"}}
-  <div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true">
-    {{range .}}
-      {{$collapseIndex := unique}}
-      {{if .Properties}}
-        <div class="panel panel-default">
-          <div class="panel-heading" role="tab" id="heading{{$collapseIndex}}">
-            <h4 class="panel-title">
-              <a class="collapsed" role="button" data-toggle="collapse" data-parent="#accordion" href="#collapse{{$collapseIndex}}" aria-expanded="false" aria-controls="collapse{{$collapseIndex}}">
-                 {{.Name}}{{range .OtherNames}}, {{.}}{{end}}
-              </a>
-            </h4>
-          </div>
-        </div>
-        <div id="collapse{{$collapseIndex}}" class="panel-collapse collapse" role="tabpanel" aria-labelledby="heading{{$collapseIndex}}">
-          <div class="panel-body">
-            <p>{{.Text}}</p>
-            {{range .OtherTexts}}<p>{{.}}</p>{{end}}
-            {{template "properties" .Properties}}
-          </div>
-        </div>
-      {{else}}
-        <div>
-          <h4>{{.Name}}{{range .OtherNames}}, {{.}}{{end}}</h4>
-          <p>{{.Text}}</p>
-          {{range .OtherTexts}}<p>{{.}}</p>{{end}}
-          <p><i>Type: {{.Type}}</i></p>
-          {{if .Default}}<p><i>Default: {{.Default}}</i></p>{{end}}
-        </div>
-      {{end}}
-    {{end}}
+{{range $imodule, $moduleType := .}}
+	{{setModule $moduleType.Name}}
+	<p>
+  <h2 id="{{$moduleType.Name}}">{{$moduleType.Name}}</h2>
+  {{if $moduleType.Synopsis }}{{$moduleType.Synopsis}}{{else}}<i>Missing synopsis</i>{{end}}
+  {{- /* Comma-separated list of module attributes' links module attributes */ -}}
+	<div class="breadcrumb">
+    {{range $i,$prop := $moduleType.Properties }}
+				{{ if gt $i 0 }},&nbsp;{{end -}}
+				<a href=#{{getModule}}.{{$prop.Name}}>{{$prop.Name}}</a>
+		{{- end -}}
   </div>
-{{end}}
+	{{- /* Property description */ -}}
+	{{- template "properties" $moduleType.Properties -}}
+{{- end -}}
+
+{{define "properties" -}}
+  {{range .}}
+    {{if .Properties -}}
+      <div class="accordion"  id="{{getModule}}.{{.Name}}">
+        <span class="fixed">&#x2295</span><b>{{.Name}}</b>
+        {{- range .OtherNames -}}, {{.}}{{- end -}}
+      </div>
+      <div class="collapsible">
+        {{- .Text}} {{range .OtherTexts}}{{.}}{{end}}
+        {{template "properties" .Properties -}}
+      </div>
+    {{- else -}}
+      <div class="simple" id="{{getModule}}.{{.Name}}">
+        <span class="fixed">&nbsp;</span><b>{{.Name}} {{range .OtherNames}}, {{.}}{{end -}}</b>
+        {{- if .Text -}}{{.Text}}{{- end -}}
+        {{- with .OtherTexts -}}{{.}}{{- end -}}<i>{{.Type}}</i>
+	{{- if .Default -}}<i>Default: {{.Default}}</i>{{- end -}}
+      </div>
+    {{- end}}
+  {{- end -}}
+{{- end -}}
+
+</div>
+<script>
+  accordions = document.getElementsByClassName('accordion');
+  for (i=0; i < accordions.length; ++i) {
+    accordions[i].addEventListener("click", function() {
+      var panel = this.nextElementSibling;
+      var child = this.firstElementChild;
+      if (panel.style.display === "block") {
+          panel.style.display = "none";
+          child.textContent = '\u2295';
+      } else {
+          panel.style.display = "block";
+          child.textContent = '\u2296';
+      }
+    });
+  }
+</script>
+</body>
 `
 )
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
index c7f0638..0eb162d 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -34,11 +34,12 @@
 
 	DisableGenerateProfile bool // don't generate profiles
 
-	PreoptBootClassPathDexFiles     []string // file paths of boot class path files
-	PreoptBootClassPathDexLocations []string // virtual locations of boot class path files
+	BootJars []string // modules for jars that form the boot class path
 
-	BootJars         []string // modules for jars that form the boot class path
-	PreoptBootJars   []string // modules for jars that form the boot image
+	TargetCoreJars                []string // modules for jars that are in the runtime apex
+	ProductUpdatableBootModules   []string
+	ProductUpdatableBootLocations []string
+
 	SystemServerJars []string // jars that form the system server
 	SystemServerApps []string // apps that are loaded into system server
 	SpeedApps        []string // apps that should be speed optimized
@@ -64,15 +65,22 @@
 
 	DefaultAppImages bool // build app images (TODO: .art files?) by default
 
-	Dex2oatXmx string // max heap size
-	Dex2oatXms string // initial heap size
+	Dex2oatXmx string // max heap size for dex2oat
+	Dex2oatXms string // initial heap size for dex2oat
 
 	EmptyDirectory string // path to an empty directory
 
-	DefaultDexPreoptImage  map[android.ArchType]string // default boot image location for each architecture
 	CpuVariant             map[android.ArchType]string // cpu variant for each architecture
 	InstructionSetFeatures map[android.ArchType]string // instruction set for each architecture
 
+	// Only used for boot image
+	DirtyImageObjects string   // path to a dirty-image-objects file
+	PreloadedClasses  string   // path to a preloaded-classes file
+	BootImageProfiles []string // path to a boot-image-profile.txt file
+	BootFlags         string   // extra flags to pass to dex2oat for the boot image
+	Dex2oatImageXmx   string   // max heap size for dex2oat for the boot image
+	Dex2oatImageXms   string   // initial heap size for dex2oat for the boot image
+
 	Tools Tools // paths to tools possibly used by the generated commands
 }
 
@@ -109,6 +117,9 @@
 	Archs           []android.ArchType
 	DexPreoptImages []string
 
+	PreoptBootClassPathDexFiles     []string // file paths of boot class path files
+	PreoptBootClassPathDexLocations []string // virtual locations of boot class path files
+
 	PreoptExtractedApk bool // Overrides OnlyPreoptModules
 
 	NoCreateAppImage    bool
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index 68bd3ea..7fdfb49 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -199,9 +199,6 @@
 			pathtools.ReplaceExtension(filepath.Base(path), "odex"))
 	}
 
-	bcp := strings.Join(global.PreoptBootClassPathDexFiles, ":")
-	bcp_locations := strings.Join(global.PreoptBootClassPathDexLocations, ":")
-
 	odexPath := toOdexPath(filepath.Join(filepath.Dir(module.BuildPath), base))
 	odexInstallPath := toOdexPath(module.DexLocation)
 	if odexOnSystemOther(module, global) {
@@ -320,9 +317,8 @@
 		FlagWithOutput("--write-invocation-to=", invocationPath).ImplicitOutput(invocationPath).
 		Flag("--runtime-arg").FlagWithArg("-Xms", global.Dex2oatXms).
 		Flag("--runtime-arg").FlagWithArg("-Xmx", global.Dex2oatXmx).
-		Flag("--runtime-arg").FlagWithArg("-Xbootclasspath:", bcp).
-		Implicits(global.PreoptBootClassPathDexFiles).
-		Flag("--runtime-arg").FlagWithArg("-Xbootclasspath-locations:", bcp_locations).
+		Flag("--runtime-arg").FlagWithInputList("-Xbootclasspath:", module.PreoptBootClassPathDexFiles, ":").
+		Flag("--runtime-arg").FlagWithList("-Xbootclasspath-locations:", module.PreoptBootClassPathDexLocations, ":").
 		Flag("${class_loader_context_arg}").
 		Flag("${stored_class_loader_context_arg}").
 		FlagWithArg("--boot-image=", bootImageLocation).Implicit(bootImage).
@@ -582,9 +578,7 @@
 	}
 }
 
-func copyOf(l []string) []string {
-	return append([]string(nil), l...)
-}
+var copyOf = android.CopyOf
 
 func anyHavePrefix(l []string, prefix string) bool {
 	for _, x := range l {
diff --git a/dexpreopt/dexpreopt_test.go b/dexpreopt/dexpreopt_test.go
index 40c694f..a2c6f77 100644
--- a/dexpreopt/dexpreopt_test.go
+++ b/dexpreopt/dexpreopt_test.go
@@ -29,6 +29,9 @@
 	PatternsOnSystemOther:              nil,
 	DisableGenerateProfile:             false,
 	BootJars:                           nil,
+	TargetCoreJars:                     nil,
+	ProductUpdatableBootModules:        nil,
+	ProductUpdatableBootLocations:      nil,
 	SystemServerJars:                   nil,
 	SystemServerApps:                   nil,
 	SpeedApps:                          nil,
@@ -49,9 +52,14 @@
 	Dex2oatXmx:                         "",
 	Dex2oatXms:                         "",
 	EmptyDirectory:                     "",
-	DefaultDexPreoptImage:              nil,
 	CpuVariant:                         nil,
 	InstructionSetFeatures:             nil,
+	DirtyImageObjects:                  "",
+	PreloadedClasses:                   "",
+	BootImageProfiles:                  nil,
+	BootFlags:                          "",
+	Dex2oatImageXmx:                    "",
+	Dex2oatImageXms:                    "",
 	Tools: Tools{
 		Profman:             "profman",
 		Dex2oat:             "dex2oat",
@@ -64,28 +72,30 @@
 }
 
 var testModuleConfig = ModuleConfig{
-	Name:                  "",
-	DexLocation:           "",
-	BuildPath:             "",
-	DexPath:               "",
-	UncompressedDex:       false,
-	HasApkLibraries:       false,
-	PreoptFlags:           nil,
-	ProfileClassListing:   "",
-	ProfileIsTextListing:  false,
-	EnforceUsesLibraries:  false,
-	OptionalUsesLibraries: nil,
-	UsesLibraries:         nil,
-	LibraryPaths:          nil,
-	Archs:                 []android.ArchType{android.Arm},
-	DexPreoptImages:       []string{"system/framework/arm/boot.art"},
-	PreoptExtractedApk:    false,
-	NoCreateAppImage:      false,
-	ForceCreateAppImage:   false,
-	PresignedPrebuilt:     false,
-	NoStripping:           false,
-	StripInputPath:        "",
-	StripOutputPath:       "",
+	Name:                            "",
+	DexLocation:                     "",
+	BuildPath:                       "",
+	DexPath:                         "",
+	UncompressedDex:                 false,
+	HasApkLibraries:                 false,
+	PreoptFlags:                     nil,
+	ProfileClassListing:             "",
+	ProfileIsTextListing:            false,
+	EnforceUsesLibraries:            false,
+	OptionalUsesLibraries:           nil,
+	UsesLibraries:                   nil,
+	LibraryPaths:                    nil,
+	Archs:                           []android.ArchType{android.Arm},
+	DexPreoptImages:                 []string{"system/framework/arm/boot.art"},
+	PreoptBootClassPathDexFiles:     nil,
+	PreoptBootClassPathDexLocations: nil,
+	PreoptExtractedApk:              false,
+	NoCreateAppImage:                false,
+	ForceCreateAppImage:             false,
+	PresignedPrebuilt:               false,
+	NoStripping:                     false,
+	StripInputPath:                  "",
+	StripOutputPath:                 "",
 }
 
 func TestDexPreopt(t *testing.T) {
diff --git a/java/aar.go b/java/aar.go
index 583a6fc..e5ab036 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -77,6 +77,7 @@
 	isLibrary             bool
 	uncompressedJNI       bool
 	useEmbeddedDex        bool
+	usesNonSdkApis        bool
 
 	aaptProperties aaptProperties
 }
@@ -184,14 +185,11 @@
 	manifestSrcPath := android.PathForModuleSrc(ctx, manifestFile)
 
 	manifestPath := manifestMerger(ctx, manifestSrcPath, sdkContext, staticLibManifests, a.isLibrary,
-		a.uncompressedJNI, a.useEmbeddedDex)
+		a.uncompressedJNI, a.useEmbeddedDex, a.usesNonSdkApis)
 
 	linkFlags, linkDeps, resDirs, overlayDirs, rroDirs := a.aapt2Flags(ctx, sdkContext, manifestPath)
 
 	rroDirs = append(rroDirs, staticRRODirs...)
-	// TODO(b/124035856): stop de-duping when there are no more dupe resource dirs.
-	rroDirs = android.FirstUniquePaths(rroDirs)
-
 	linkFlags = append(linkFlags, libFlags...)
 	linkDeps = append(linkDeps, libDeps...)
 	linkFlags = append(linkFlags, extraLinkFlags...)
diff --git a/java/android_manifest.go b/java/android_manifest.go
index e63fb80..3cca4f7 100644
--- a/java/android_manifest.go
+++ b/java/android_manifest.go
@@ -44,7 +44,7 @@
 	"libs")
 
 func manifestMerger(ctx android.ModuleContext, manifest android.Path, sdkContext sdkContext,
-	staticLibManifests android.Paths, isLibrary bool, uncompressedJNI, useEmbeddedDex bool) android.Path {
+	staticLibManifests android.Paths, isLibrary, uncompressedJNI, useEmbeddedDex, usesNonSdkApis bool) android.Path {
 
 	var args []string
 	if isLibrary {
@@ -62,6 +62,10 @@
 		}
 	}
 
+	if usesNonSdkApis {
+		args = append(args, "--uses-non-sdk-api")
+	}
+
 	if useEmbeddedDex {
 		args = append(args, "--use-embedded-dex=true")
 	}
diff --git a/java/app.go b/java/app.go
index 3cb7e8e..c08aefd 100644
--- a/java/app.go
+++ b/java/app.go
@@ -186,6 +186,8 @@
 }
 
 func (a *AndroidApp) aaptBuildActions(ctx android.ModuleContext) {
+	a.aapt.usesNonSdkApis = Bool(a.Module.deviceProperties.Platform_apis)
+
 	aaptLinkFlags := []string{}
 
 	// Add TARGET_AAPT_CHARACTERISTICS values to AAPT link flags if they exist and --product flags were not provided.
@@ -441,7 +443,7 @@
 func (a *AndroidTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	a.generateAndroidBuildActions(ctx)
 
-	a.testConfig = tradefed.AutoGenInstrumentationTestConfig(ctx, a.testProperties.Test_config, a.testProperties.Test_config_template, a.manifestPath)
+	a.testConfig = tradefed.AutoGenInstrumentationTestConfig(ctx, a.testProperties.Test_config, a.testProperties.Test_config_template, a.manifestPath, a.testProperties.Test_suites)
 	a.data = ctx.ExpandSources(a.testProperties.Data, nil)
 }
 
diff --git a/java/dex.go b/java/dex.go
index 913eee6..54b7bfc 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -177,9 +177,9 @@
 	javalibJar := android.PathForModuleOut(ctx, "dex", jarName)
 	outDir := android.PathForModuleOut(ctx, "dex")
 
-	zipFlags := ""
+	zipFlags := "--ignore_missing_files"
 	if j.deviceProperties.UncompressDex {
-		zipFlags = "-L 0"
+		zipFlags += " -L 0"
 	}
 
 	if useR8 {
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index 127deab..0a56529 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -56,7 +56,11 @@
 }
 
 func (d *dexpreopter) dexpreoptDisabled(ctx android.ModuleContext) bool {
-	if ctx.Config().DisableDexPreopt(ctx.ModuleName()) {
+	if ctx.Config().DisableDexPreopt() {
+		return true
+	}
+
+	if ctx.Config().DisableDexPreoptForModule(ctx.ModuleName()) {
 		return true
 	}
 
@@ -83,8 +87,8 @@
 
 var dexpreoptGlobalConfigKey = android.NewOnceKey("DexpreoptGlobalConfig")
 
-func getGlobalConfig(ctx android.ModuleContext) dexpreopt.GlobalConfig {
-	globalConfig := ctx.Config().Once(dexpreoptGlobalConfigKey, func() interface{} {
+func dexpreoptGlobalConfig(ctx android.PathContext) dexpreopt.GlobalConfig {
+	return ctx.Config().Once(dexpreoptGlobalConfigKey, func() interface{} {
 		if f := ctx.Config().DexpreoptGlobalConfig(); f != "" {
 			ctx.AddNinjaFileDeps(f)
 			globalConfig, err := dexpreopt.LoadGlobalConfig(f)
@@ -95,11 +99,10 @@
 		}
 		return dexpreopt.GlobalConfig{}
 	}).(dexpreopt.GlobalConfig)
-	return globalConfig
 }
 
 func odexOnSystemOther(ctx android.ModuleContext, installPath android.OutputPath) bool {
-	return dexpreopt.OdexOnSystemOtherByName(ctx.ModuleName(), android.InstallPathToOnDevicePath(ctx, installPath), getGlobalConfig(ctx))
+	return dexpreopt.OdexOnSystemOtherByName(ctx.ModuleName(), android.InstallPathToOnDevicePath(ctx, installPath), dexpreoptGlobalConfig(ctx))
 }
 
 func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.ModuleOutPath) android.ModuleOutPath {
@@ -107,7 +110,7 @@
 		return dexJarFile
 	}
 
-	globalConfig := getGlobalConfig(ctx)
+	info := dexpreoptBootJarsInfo(ctx)
 
 	var archs []android.ArchType
 	for _, a := range ctx.MultiTargets() {
@@ -118,7 +121,7 @@
 		for _, target := range ctx.Config().Targets[android.Android] {
 			archs = append(archs, target.Arch.ArchType)
 		}
-		if inList(ctx.ModuleName(), globalConfig.SystemServerJars) && !d.isSDKLibrary {
+		if inList(ctx.ModuleName(), info.global.SystemServerJars) && !d.isSDKLibrary {
 			// If the module is not an SDK library and it's a system server jar, only preopt the primary arch.
 			archs = archs[:1]
 		}
@@ -130,7 +133,7 @@
 
 	var images []string
 	for _, arch := range archs {
-		images = append(images, globalConfig.DefaultDexPreoptImage[arch])
+		images = append(images, info.images[arch].String())
 	}
 
 	dexLocation := android.InstallPathToOnDevicePath(ctx, d.installPath)
@@ -178,6 +181,9 @@
 		Archs:           archs,
 		DexPreoptImages: images,
 
+		PreoptBootClassPathDexFiles:     info.preoptBootDex.Strings(),
+		PreoptBootClassPathDexLocations: info.preoptBootLocations,
+
 		PreoptExtractedApk: false,
 
 		NoCreateAppImage:    !BoolDefault(d.dexpreoptProperties.Dex_preopt.App_image, true),
@@ -188,7 +194,7 @@
 		StripOutputPath: strippedDexJarFile.String(),
 	}
 
-	dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(globalConfig, dexpreoptConfig)
+	dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(info.global, dexpreoptConfig)
 	if err != nil {
 		ctx.ModuleErrorf("error generating dexpreopt rule: %s", err.Error())
 		return dexJarFile
@@ -198,7 +204,7 @@
 
 	d.builtInstalled = dexpreoptRule.Installs().String()
 
-	stripRule, err := dexpreopt.GenerateStripRule(globalConfig, dexpreoptConfig)
+	stripRule, err := dexpreopt.GenerateStripRule(info.global, dexpreoptConfig)
 	if err != nil {
 		ctx.ModuleErrorf("error generating dexpreopt strip rule: %s", err.Error())
 		return dexJarFile
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
new file mode 100644
index 0000000..0656ff4
--- /dev/null
+++ b/java/dexpreopt_bootjars.go
@@ -0,0 +1,463 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+	"path/filepath"
+	"strings"
+
+	"android/soong/android"
+	"android/soong/dexpreopt"
+
+	"github.com/google/blueprint/pathtools"
+	"github.com/google/blueprint/proptools"
+)
+
+func init() {
+	android.RegisterSingletonType("dex_bootjars", dexpreoptBootJarsFactory)
+}
+
+// The image "location" is a symbolic path that with multiarchitecture
+// support doesn't really exist on the device. Typically it is
+// /system/framework/boot.art and should be the same for all supported
+// architectures on the device. The concrete architecture specific
+// content actually ends up in a "filename" that contains an
+// architecture specific directory name such as arm, arm64, mips,
+// mips64, x86, x86_64.
+//
+// Here are some example values for an x86_64 / x86 configuration:
+//
+// bootImages["x86_64"] = "out/soong/generic_x86_64/dex_bootjars/system/framework/x86_64/boot.art"
+// dexpreopt.PathToLocation(bootImages["x86_64"], "x86_64") = "out/soong/generic_x86_64/dex_bootjars/system/framework/boot.art"
+//
+// bootImages["x86"] = "out/soong/generic_x86_64/dex_bootjars/system/framework/x86/boot.art"
+// dexpreopt.PathToLocation(bootImages["x86"])= "out/soong/generic_x86_64/dex_bootjars/system/framework/boot.art"
+//
+// The location is passed as an argument to the ART tools like dex2oat instead of the real path. The ART tools
+// will then reconstruct the real path, so the rules must have a dependency on the real path.
+
+type bootJarsInfo struct {
+	dir        android.OutputPath
+	symbolsDir android.OutputPath
+	images     map[android.ArchType]android.OutputPath
+	installs   map[android.ArchType]android.RuleBuilderInstalls
+
+	vdexInstalls       map[android.ArchType]android.RuleBuilderInstalls
+	unstrippedInstalls map[android.ArchType]android.RuleBuilderInstalls
+	profileInstalls    android.RuleBuilderInstalls
+
+	global dexpreopt.GlobalConfig
+
+	preoptBootModules     []string
+	preoptBootLocations   []string
+	preoptBootDex         android.WritablePaths
+	allBootModules        []string
+	allBootLocations      []string
+	bootclasspath         string
+	systemServerClasspath string
+}
+
+var dexpreoptBootJarsInfoKey = android.NewOnceKey("dexpreoptBootJarsInfoKey")
+
+// dexpreoptBootJarsInfo creates all the paths for singleton files the first time it is called, which may be
+// from a ModuleContext that needs to reference a file that will be created by a singleton rule that hasn't
+// yet been created.
+func dexpreoptBootJarsInfo(ctx android.PathContext) *bootJarsInfo {
+	return ctx.Config().Once(dexpreoptBootJarsInfoKey, func() interface{} {
+
+		info := &bootJarsInfo{
+			dir:        android.PathForOutput(ctx, ctx.Config().DeviceName(), "dex_bootjars"),
+			symbolsDir: android.PathForOutput(ctx, ctx.Config().DeviceName(), "dex_bootjars_unstripped"),
+			images:     make(map[android.ArchType]android.OutputPath),
+			installs:   make(map[android.ArchType]android.RuleBuilderInstalls),
+
+			vdexInstalls:       make(map[android.ArchType]android.RuleBuilderInstalls),
+			unstrippedInstalls: make(map[android.ArchType]android.RuleBuilderInstalls),
+		}
+
+		for _, target := range ctx.Config().Targets[android.Android] {
+			info.images[target.Arch.ArchType] = info.dir.Join(ctx,
+				"system/framework", target.Arch.ArchType.String(), "boot.art")
+		}
+
+		info.global = dexpreoptGlobalConfig(ctx)
+		computeBootClasspath(ctx, info)
+		computeSystemServerClasspath(ctx, info)
+
+		return info
+	}).(*bootJarsInfo)
+}
+
+func concat(lists ...[]string) []string {
+	var size int
+	for _, l := range lists {
+		size += len(l)
+	}
+	ret := make([]string, 0, size)
+	for _, l := range lists {
+		ret = append(ret, l...)
+	}
+	return ret
+}
+
+func computeBootClasspath(ctx android.PathContext, info *bootJarsInfo) {
+	runtimeModules := android.RemoveListFromList(info.global.TargetCoreJars, info.global.ProductUpdatableBootModules)
+	nonFrameworkModules := concat(runtimeModules, info.global.ProductUpdatableBootModules)
+	frameworkModules := android.RemoveListFromList(info.global.BootJars, nonFrameworkModules)
+
+	var nonUpdatableBootModules []string
+	var nonUpdatableBootLocations []string
+
+	for _, m := range runtimeModules {
+		nonUpdatableBootModules = append(nonUpdatableBootModules, m)
+		nonUpdatableBootLocations = append(nonUpdatableBootLocations,
+			filepath.Join("/apex/com.android.runtime/javalib", m+".jar"))
+	}
+
+	for _, m := range frameworkModules {
+		nonUpdatableBootModules = append(nonUpdatableBootModules, m)
+		nonUpdatableBootLocations = append(nonUpdatableBootLocations,
+			filepath.Join("/system/framework", m+".jar"))
+	}
+
+	// The path to bootclasspath dex files needs to be known at module GenerateAndroidBuildAction time, before
+	// the bootclasspath modules have been compiled.  Set up known paths for them, the singleton rules will copy
+	// them there.
+	// TODO: use module dependencies instead
+	var nonUpdatableBootDex android.WritablePaths
+	for _, m := range nonUpdatableBootModules {
+		nonUpdatableBootDex = append(nonUpdatableBootDex,
+			android.PathForOutput(ctx, ctx.Config().DeviceName(), "dex_bootjars_input", m+".jar"))
+	}
+
+	allBootModules := concat(nonUpdatableBootModules, info.global.ProductUpdatableBootModules)
+	allBootLocations := concat(nonUpdatableBootLocations, info.global.ProductUpdatableBootLocations)
+
+	bootclasspath := strings.Join(allBootLocations, ":")
+
+	info.preoptBootModules = nonUpdatableBootModules
+	info.preoptBootLocations = nonUpdatableBootLocations
+	info.preoptBootDex = nonUpdatableBootDex
+	info.allBootModules = allBootModules
+	info.allBootLocations = allBootLocations
+	info.bootclasspath = bootclasspath
+}
+
+func computeSystemServerClasspath(ctx android.PathContext, info *bootJarsInfo) {
+	var systemServerClasspathLocations []string
+	for _, m := range info.global.SystemServerJars {
+		systemServerClasspathLocations = append(systemServerClasspathLocations,
+			filepath.Join("/system/framework", m+".jar"))
+	}
+
+	info.systemServerClasspath = strings.Join(systemServerClasspathLocations, ":")
+}
+func dexpreoptBootJarsFactory() android.Singleton {
+	return dexpreoptBootJars{}
+}
+
+func skipDexpreoptBootJars(ctx android.PathContext) bool {
+	if ctx.Config().UnbundledBuild() {
+		return true
+	}
+
+	if len(ctx.Config().Targets[android.Android]) == 0 {
+		// Host-only build
+		return true
+	}
+
+	return false
+}
+
+type dexpreoptBootJars struct{}
+
+// dexpreoptBoot singleton rules
+func (dexpreoptBootJars) GenerateBuildActions(ctx android.SingletonContext) {
+	if skipDexpreoptBootJars(ctx) {
+		return
+	}
+
+	info := dexpreoptBootJarsInfo(ctx)
+
+	// Skip recompiling the boot image for the second sanitization phase. We'll get separate paths
+	// and invalidate first-stage artifacts which are crucial to SANITIZE_LITE builds.
+	// Note: this is technically incorrect. Compiled code contains stack checks which may depend
+	//       on ASAN settings.
+	if len(ctx.Config().SanitizeDevice()) == 1 &&
+		ctx.Config().SanitizeDevice()[0] == "address" &&
+		info.global.SanitizeLite {
+		return
+	}
+
+	bootDexJars := make(android.Paths, len(info.preoptBootModules))
+
+	ctx.VisitAllModules(func(module android.Module) {
+		// Collect dex jar paths for the modules listed above.
+		if j, ok := module.(Dependency); ok {
+			name := ctx.ModuleName(module)
+			if i := android.IndexList(name, info.preoptBootModules); i != -1 {
+				bootDexJars[i] = j.DexJar()
+			}
+		}
+	})
+
+	var missingDeps []string
+	// Ensure all modules were converted to paths
+	for i := range bootDexJars {
+		if bootDexJars[i] == nil {
+			if ctx.Config().AllowMissingDependencies() {
+				missingDeps = append(missingDeps, info.preoptBootModules[i])
+				bootDexJars[i] = android.PathForOutput(ctx, "missing")
+			} else {
+				ctx.Errorf("failed to find dex jar path for module %q",
+					info.preoptBootModules[i])
+			}
+		}
+	}
+
+	// The path to bootclasspath dex files needs to be known at module GenerateAndroidBuildAction time, before
+	// the bootclasspath modules have been compiled.  Copy the dex jars there so the module rules that have
+	// already been set up can find them.
+	for i := range bootDexJars {
+		ctx.Build(pctx, android.BuildParams{
+			Rule:   android.Cp,
+			Input:  bootDexJars[i],
+			Output: info.preoptBootDex[i],
+		})
+	}
+
+	profile := bootImageProfileRule(ctx, info, missingDeps)
+
+	if !ctx.Config().DisableDexPreopt() {
+		targets := ctx.Config().Targets[android.Android]
+		if ctx.Config().SecondArchIsTranslated() {
+			targets = targets[:1]
+		}
+
+		for _, target := range targets {
+			dexPreoptBootImageRule(ctx, info, target.Arch.ArchType, profile, missingDeps)
+		}
+	}
+}
+
+func dexPreoptBootImageRule(ctx android.SingletonContext, info *bootJarsInfo,
+	arch android.ArchType, profile android.Path, missingDeps []string) {
+
+	symbolsDir := info.symbolsDir.Join(ctx, "system/framework", arch.String())
+	symbolsFile := symbolsDir.Join(ctx, "boot.oat")
+	outputDir := info.dir.Join(ctx, "system/framework", arch.String())
+	outputPath := info.images[arch]
+	oatLocation := pathtools.ReplaceExtension(dexpreopt.PathToLocation(outputPath.String(), arch), "oat")
+
+	rule := android.NewRuleBuilder()
+	rule.MissingDeps(missingDeps)
+
+	rule.Command().Text("mkdir").Flag("-p").Flag(symbolsDir.String())
+	rule.Command().Text("rm").Flag("-f").
+		Flag(symbolsDir.Join(ctx, "*.art").String()).
+		Flag(symbolsDir.Join(ctx, "*.oat").String()).
+		Flag(symbolsDir.Join(ctx, "*.invocation").String())
+	rule.Command().Text("rm").Flag("-f").
+		Flag(outputDir.Join(ctx, "*.art").String()).
+		Flag(outputDir.Join(ctx, "*.oat").String()).
+		Flag(outputDir.Join(ctx, "*.invocation").String())
+
+	cmd := rule.Command()
+
+	extraFlags := ctx.Config().Getenv("ART_BOOT_IMAGE_EXTRA_ARGS")
+	if extraFlags == "" {
+		// Use ANDROID_LOG_TAGS to suppress most logging by default...
+		cmd.Text(`ANDROID_LOG_TAGS="*:e"`)
+	} else {
+		// ...unless the boot image is generated specifically for testing, then allow all logging.
+		cmd.Text(`ANDROID_LOG_TAGS="*:v"`)
+	}
+
+	invocationPath := outputPath.ReplaceExtension(ctx, "invocation")
+
+	cmd.Tool(info.global.Tools.Dex2oat).
+		Flag("--avoid-storing-invocation").
+		FlagWithOutput("--write-invocation-to=", invocationPath.String()).ImplicitOutput(invocationPath.String()).
+		Flag("--runtime-arg").FlagWithArg("-Xms", info.global.Dex2oatImageXms).
+		Flag("--runtime-arg").FlagWithArg("-Xmx", info.global.Dex2oatImageXmx)
+
+	if profile == nil {
+		cmd.FlagWithArg("--image-classes=", info.global.PreloadedClasses)
+	} else {
+		cmd.FlagWithArg("--compiler-filter=", "speed-profile")
+		cmd.FlagWithInput("--profile-file=", profile.String())
+	}
+
+	if info.global.DirtyImageObjects != "" {
+		cmd.FlagWithArg("--dirty-image-objects=", info.global.DirtyImageObjects)
+	}
+
+	cmd.
+		FlagForEachInput("--dex-file=", info.preoptBootDex.Strings()).
+		FlagForEachArg("--dex-location=", info.preoptBootLocations).
+		Flag("--generate-debug-info").
+		Flag("--generate-build-id").
+		FlagWithArg("--oat-symbols=", symbolsFile.String()).
+		Flag("--strip").
+		FlagWithOutput("--oat-file=", outputPath.ReplaceExtension(ctx, "oat").String()).
+		FlagWithArg("--oat-location=", oatLocation).
+		FlagWithOutput("--image=", outputPath.String()).
+		FlagWithArg("--base=", ctx.Config().LibartImgDeviceBaseAddress()).
+		FlagWithArg("--instruction-set=", arch.String()).
+		FlagWithArg("--instruction-set-variant=", info.global.CpuVariant[arch]).
+		FlagWithArg("--instruction-set-features=", info.global.InstructionSetFeatures[arch]).
+		FlagWithArg("--android-root=", info.global.EmptyDirectory).
+		FlagWithArg("--no-inline-from=", "core-oj.jar").
+		Flag("--abort-on-hard-verifier-error")
+
+	if info.global.BootFlags != "" {
+		cmd.Flag(info.global.BootFlags)
+	}
+
+	if extraFlags != "" {
+		cmd.Flag(extraFlags)
+	}
+
+	cmd.Textf(`|| ( echo %s ; false )`, proptools.ShellEscape([]string{failureMessage})[0])
+
+	installDir := filepath.Join("/system/framework", arch.String())
+	vdexInstallDir := filepath.Join("/system/framework")
+
+	var extraFiles android.WritablePaths
+	var vdexInstalls android.RuleBuilderInstalls
+	var unstrippedInstalls android.RuleBuilderInstalls
+
+	// dex preopt on the bootclasspath produces multiple files.  The first dex file
+	// is converted into to boot.art (to match the legacy assumption that boot.art
+	// exists), and the rest are converted to boot-<name>.art.
+	// In addition, each .art file has an associated .oat and .vdex file, and an
+	// unstripped .oat file
+	for i, m := range info.preoptBootModules {
+		name := "boot"
+		if i != 0 {
+			name += "-" + m
+		}
+
+		art := outputDir.Join(ctx, name+".art")
+		oat := outputDir.Join(ctx, name+".oat")
+		vdex := outputDir.Join(ctx, name+".vdex")
+		unstrippedOat := symbolsDir.Join(ctx, name+".oat")
+
+		extraFiles = append(extraFiles, art, oat, vdex, unstrippedOat)
+
+		// Install the .oat and .art files.
+		rule.Install(art.String(), filepath.Join(installDir, art.Base()))
+		rule.Install(oat.String(), filepath.Join(installDir, oat.Base()))
+
+		// The vdex files are identical between architectures, install them to a shared location.  The Make rules will
+		// only use the install rules for one architecture, and will create symlinks into the architecture-specific
+		// directories.
+		vdexInstalls = append(vdexInstalls,
+			android.RuleBuilderInstall{vdex.String(), filepath.Join(vdexInstallDir, vdex.Base())})
+
+		// Install the unstripped oat files.  The Make rules will put these in $(TARGET_OUT_UNSTRIPPED)
+		unstrippedInstalls = append(unstrippedInstalls,
+			android.RuleBuilderInstall{unstrippedOat.String(), filepath.Join(installDir, unstrippedOat.Base())})
+	}
+
+	cmd.ImplicitOutputs(extraFiles.Strings())
+
+	rule.Build(pctx, ctx, "bootJarsDexpreopt_"+arch.String(), "dexpreopt boot jars "+arch.String())
+
+	// save output and installed files for makevars
+	info.installs[arch] = rule.Installs()
+	info.vdexInstalls[arch] = vdexInstalls
+	info.unstrippedInstalls[arch] = unstrippedInstalls
+}
+
+const failureMessage = `ERROR: Dex2oat failed to compile a boot image.
+It is likely that the boot classpath is inconsistent.
+Rebuild with ART_BOOT_IMAGE_EXTRA_ARGS="--runtime-arg -verbose:verifier" to see verification errors.`
+
+func bootImageProfileRule(ctx android.SingletonContext, info *bootJarsInfo, missingDeps []string) android.WritablePath {
+	if len(info.global.BootImageProfiles) == 0 {
+		return nil
+	}
+
+	tools := info.global.Tools
+
+	rule := android.NewRuleBuilder()
+	rule.MissingDeps(missingDeps)
+
+	var bootImageProfile string
+	if len(info.global.BootImageProfiles) > 1 {
+		combinedBootImageProfile := info.dir.Join(ctx, "boot-image-profile.txt")
+		rule.Command().Text("cat").Inputs(info.global.BootImageProfiles).Output(combinedBootImageProfile.String())
+		bootImageProfile = combinedBootImageProfile.String()
+	} else {
+		bootImageProfile = info.global.BootImageProfiles[0]
+	}
+
+	profile := info.dir.Join(ctx, "boot.prof")
+
+	rule.Command().
+		Text(`ANDROID_LOG_TAGS="*:e"`).
+		Tool(tools.Profman).
+		FlagWithArg("--create-profile-from=", bootImageProfile).
+		FlagForEachInput("--apk=", info.preoptBootDex.Strings()).
+		FlagForEachArg("--dex-location=", info.preoptBootLocations).
+		FlagWithOutput("--reference-profile-file=", profile.String())
+
+	rule.Install(profile.String(), "/system/etc/boot-image.prof")
+
+	rule.Build(pctx, ctx, "bootJarsProfile", "profile boot jars")
+
+	info.profileInstalls = rule.Installs()
+
+	return profile
+}
+
+func init() {
+	android.RegisterMakeVarsProvider(pctx, bootImageMakeVars)
+}
+
+// Export paths to Make.  INTERNAL_PLATFORM_HIDDENAPI_FLAGS is used by Make rules in art/ and cts/.
+// Both paths are used to call dist-for-goals.
+func bootImageMakeVars(ctx android.MakeVarsContext) {
+	if skipDexpreoptBootJars(ctx) {
+		return
+	}
+
+	info := dexpreoptBootJarsInfo(ctx)
+	for arch, _ := range info.images {
+		ctx.Strict("DEXPREOPT_IMAGE_"+arch.String(), info.images[arch].String())
+
+		var builtInstalled []string
+		for _, install := range info.installs[arch] {
+			builtInstalled = append(builtInstalled, install.From+":"+install.To)
+		}
+
+		var unstrippedBuiltInstalled []string
+		for _, install := range info.unstrippedInstalls[arch] {
+			unstrippedBuiltInstalled = append(unstrippedBuiltInstalled, install.From+":"+install.To)
+		}
+
+		ctx.Strict("DEXPREOPT_IMAGE_BUILT_INSTALLED_"+arch.String(), info.installs[arch].String())
+		ctx.Strict("DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_"+arch.String(), info.unstrippedInstalls[arch].String())
+		ctx.Strict("DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_"+arch.String(), info.vdexInstalls[arch].String())
+	}
+
+	ctx.Strict("DEXPREOPT_IMAGE_PROFILE_BUILT_INSTALLED", info.profileInstalls.String())
+
+	ctx.Strict("DEXPREOPT_BOOTCLASSPATH_DEX_FILES", strings.Join(info.preoptBootDex.Strings(), " "))
+	ctx.Strict("DEXPREOPT_BOOTCLASSPATH_DEX_LOCATIONS", strings.Join(info.preoptBootLocations, " "))
+	ctx.Strict("PRODUCT_BOOTCLASSPATH", info.bootclasspath)
+	ctx.Strict("PRODUCT_SYSTEM_SERVER_CLASSPATH", info.systemServerClasspath)
+}
diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go
index de1bcf5..adbd356 100644
--- a/java/hiddenapi_singleton.go
+++ b/java/hiddenapi_singleton.go
@@ -44,13 +44,15 @@
 }
 
 func hiddenAPISingletonFactory() android.Singleton {
-	return hiddenAPISingleton{}
+	return &hiddenAPISingleton{}
 }
 
-type hiddenAPISingleton struct{}
+type hiddenAPISingleton struct {
+	flags, metadata android.Path
+}
 
 // hiddenAPI singleton rules
-func (hiddenAPISingleton) GenerateBuildActions(ctx android.SingletonContext) {
+func (h *hiddenAPISingleton) GenerateBuildActions(ctx android.SingletonContext) {
 	// Don't run any hiddenapi rules if UNSAFE_DISABLE_HIDDENAPI_FLAGS=true
 	if ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") {
 		return
@@ -60,10 +62,24 @@
 
 	// These rules depend on files located in frameworks/base, skip them if running in a tree that doesn't have them.
 	if ctx.Config().FrameworksBaseDirExists(ctx) {
-		flagsRule(ctx)
-		metadataRule(ctx)
+		h.flags = flagsRule(ctx)
+		h.metadata = metadataRule(ctx)
 	} else {
-		emptyFlagsRule(ctx)
+		h.flags = emptyFlagsRule(ctx)
+	}
+}
+
+// Export paths to Make.  INTERNAL_PLATFORM_HIDDENAPI_FLAGS is used by Make rules in art/ and cts/.
+// Both paths are used to call dist-for-goals.
+func (h *hiddenAPISingleton) MakeVars(ctx android.MakeVarsContext) {
+	if ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") {
+		return
+	}
+
+	ctx.Strict("INTERNAL_PLATFORM_HIDDENAPI_FLAGS", h.flags.String())
+
+	if h.metadata != nil {
+		ctx.Strict("INTERNAL_PLATFORM_HIDDENAPI_GREYLIST_METADATA", h.metadata.String())
 	}
 }
 
@@ -170,7 +186,7 @@
 
 // flagsRule creates a rule to build hiddenapi-flags.csv out of flags.csv files generated for boot image modules and
 // the greylists.
-func flagsRule(ctx android.SingletonContext) {
+func flagsRule(ctx android.SingletonContext) android.Path {
 	var flagsCSV android.Paths
 
 	var greylistIgnoreConflicts android.Path
@@ -187,7 +203,7 @@
 
 	if greylistIgnoreConflicts == nil {
 		ctx.Errorf("failed to find removed_dex_api_filename from hiddenapi-lists-docs module")
-		return
+		return nil
 	}
 
 	rule := android.NewRuleBuilder()
@@ -216,11 +232,13 @@
 	commitChangeForRestat(rule, tempPath, outputPath)
 
 	rule.Build(pctx, ctx, "hiddenAPIFlagsFile", "hiddenapi flags")
+
+	return outputPath
 }
 
 // emptyFlagsRule creates a rule to build an empty hiddenapi-flags.csv, which is needed by master-art-host builds that
 // have a partial manifest without frameworks/base but still need to build a boot image.
-func emptyFlagsRule(ctx android.SingletonContext) {
+func emptyFlagsRule(ctx android.SingletonContext) android.Path {
 	rule := android.NewRuleBuilder()
 
 	outputPath := hiddenAPISingletonPaths(ctx).flags
@@ -229,11 +247,13 @@
 	rule.Command().Text("touch").Output(outputPath.String())
 
 	rule.Build(pctx, ctx, "emptyHiddenAPIFlagsFile", "empty hiddenapi flags")
+
+	return outputPath
 }
 
 // metadataRule creates a rule to build hiddenapi-greylist.csv out of the metadata.csv files generated for boot image
 // modules.
-func metadataRule(ctx android.SingletonContext) {
+func metadataRule(ctx android.SingletonContext) android.Path {
 	var metadataCSV android.Paths
 
 	ctx.VisitAllModules(func(module android.Module) {
@@ -255,6 +275,8 @@
 		Output(outputPath.String())
 
 	rule.Build(pctx, ctx, "hiddenAPIGreylistMetadataFile", "hiddenapi greylist metadata")
+
+	return outputPath
 }
 
 // commitChangeForRestat adds a command to a rule that updates outputPath from tempPath if they are different.  It
@@ -274,17 +296,3 @@
 		Text("fi").
 		Text(")")
 }
-
-func init() {
-	android.RegisterMakeVarsProvider(pctx, hiddenAPIMakeVars)
-}
-
-// Export paths to Make.  INTERNAL_PLATFORM_HIDDENAPI_FLAGS is used by Make rules in art/ and cts/.
-// Both paths are used to call dist-for-goals.
-func hiddenAPIMakeVars(ctx android.MakeVarsContext) {
-	if !ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") {
-		singletonPaths := hiddenAPISingletonPaths(ctx)
-		ctx.Strict("INTERNAL_PLATFORM_HIDDENAPI_FLAGS", singletonPaths.flags.String())
-		ctx.Strict("INTERNAL_PLATFORM_HIDDENAPI_GREYLIST_METADATA", singletonPaths.metadata.String())
-	}
-}
diff --git a/java/java.go b/java/java.go
index 880f920..3501174 100644
--- a/java/java.go
+++ b/java/java.go
@@ -1527,7 +1527,7 @@
 }
 
 func (j *Test) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	j.testConfig = tradefed.AutoGenJavaTestConfig(ctx, j.testProperties.Test_config, j.testProperties.Test_config_template)
+	j.testConfig = tradefed.AutoGenJavaTestConfig(ctx, j.testProperties.Test_config, j.testProperties.Test_config_template, j.testProperties.Test_suites)
 	j.data = ctx.ExpandSources(j.testProperties.Data, nil)
 
 	j.Library.GenerateAndroidBuildActions(ctx)
diff --git a/java/java_test.go b/java/java_test.go
index 034e905..8d3efcb 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -15,7 +15,6 @@
 package java
 
 import (
-	"fmt"
 	"io/ioutil"
 	"os"
 	"path/filepath"
@@ -54,16 +53,7 @@
 }
 
 func testConfig(env map[string]string) android.Config {
-	if env == nil {
-		env = make(map[string]string)
-	}
-	if env["ANDROID_JAVA8_HOME"] == "" {
-		env["ANDROID_JAVA8_HOME"] = "jdk8"
-	}
-	config := android.TestArchConfig(buildDir, env)
-	config.TestProductVariables.DeviceSystemSdkVersions = []string{"14", "15"}
-	return config
-
+	return TestConfig(buildDir, env)
 }
 
 func testContext(config android.Config, bp string,
@@ -113,53 +103,7 @@
 
 	ctx.Register()
 
-	extraModules := []string{
-		"core-lambda-stubs",
-		"framework",
-		"ext",
-		"android_stubs_current",
-		"android_system_stubs_current",
-		"android_test_stubs_current",
-		"core.current.stubs",
-		"core.platform.api.stubs",
-		"kotlin-stdlib",
-		"kotlin-annotations",
-	}
-
-	for _, extra := range extraModules {
-		bp += fmt.Sprintf(`
-			java_library {
-				name: "%s",
-				srcs: ["a.java"],
-				no_standard_libs: true,
-				sdk_version: "core_current",
-				system_modules: "core-platform-api-stubs-system-modules",
-			}
-		`, extra)
-	}
-
-	bp += `
-		android_app {
-			name: "framework-res",
-			no_framework_libs: true,
-		}
-	`
-
-	systemModules := []string{
-		"core-system-modules",
-		"core-platform-api-stubs-system-modules",
-		"android_stubs_current_system_modules",
-		"android_system_stubs_current_system_modules",
-		"android_test_stubs_current_system_modules",
-	}
-
-	for _, extra := range systemModules {
-		bp += fmt.Sprintf(`
-			java_system_modules {
-				name: "%s",
-			}
-		`, extra)
-	}
+	bp += GatherRequiredDepsForTest()
 
 	mockFS := map[string][]byte{
 		"Android.bp":             []byte(bp),
diff --git a/java/testing.go b/java/testing.go
new file mode 100644
index 0000000..6febfa1
--- /dev/null
+++ b/java/testing.go
@@ -0,0 +1,88 @@
+// Copyright 2019 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 (
+	"fmt"
+
+	"android/soong/android"
+)
+
+func TestConfig(buildDir string, env map[string]string) android.Config {
+	if env == nil {
+		env = make(map[string]string)
+	}
+	if env["ANDROID_JAVA8_HOME"] == "" {
+		env["ANDROID_JAVA8_HOME"] = "jdk8"
+	}
+	config := android.TestArchConfig(buildDir, env)
+	config.TestProductVariables.DeviceSystemSdkVersions = []string{"14", "15"}
+
+	return config
+}
+
+func GatherRequiredDepsForTest() string {
+	var bp string
+
+	extraModules := []string{
+		"core-lambda-stubs",
+		"framework",
+		"ext",
+		"android_stubs_current",
+		"android_system_stubs_current",
+		"android_test_stubs_current",
+		"core.current.stubs",
+		"core.platform.api.stubs",
+		"kotlin-stdlib",
+		"kotlin-annotations",
+	}
+
+	for _, extra := range extraModules {
+		bp += fmt.Sprintf(`
+			java_library {
+				name: "%s",
+				srcs: ["a.java"],
+				no_standard_libs: true,
+				sdk_version: "core_current",
+				system_modules: "core-platform-api-stubs-system-modules",
+			}
+		`, extra)
+	}
+
+	bp += `
+		android_app {
+			name: "framework-res",
+			no_framework_libs: true,
+		}
+	`
+
+	systemModules := []string{
+		"core-system-modules",
+		"core-platform-api-stubs-system-modules",
+		"android_stubs_current_system_modules",
+		"android_system_stubs_current_system_modules",
+		"android_test_stubs_current_system_modules",
+	}
+
+	for _, extra := range systemModules {
+		bp += fmt.Sprintf(`
+			java_system_modules {
+				name: "%s",
+			}
+		`, extra)
+	}
+
+	return bp
+}
diff --git a/python/test.go b/python/test.go
index 43ee54c..55b0ab5 100644
--- a/python/test.go
+++ b/python/test.go
@@ -50,7 +50,7 @@
 
 func (test *testDecorator) install(ctx android.ModuleContext, file android.Path) {
 	test.testConfig = tradefed.AutoGenPythonBinaryHostTestConfig(ctx, test.testProperties.Test_config,
-		test.testProperties.Test_config_template)
+		test.testProperties.Test_config_template, test.binaryDecorator.binaryProperties.Test_suites)
 
 	test.binaryDecorator.pythonInstaller.dir = "nativetest"
 	test.binaryDecorator.pythonInstaller.dir64 = "nativetest64"
diff --git a/sysprop/sysprop_test.go b/sysprop/sysprop_test.go
index 92e0af4..745e424 100644
--- a/sysprop/sysprop_test.go
+++ b/sysprop/sysprop_test.go
@@ -19,7 +19,6 @@
 	"android/soong/cc"
 	"android/soong/java"
 
-	"fmt"
 	"io/ioutil"
 	"os"
 	"strings"
@@ -90,54 +89,7 @@
 
 	ctx.Register()
 
-	extraModules := []string{
-		"core-lambda-stubs",
-		"framework",
-		"ext",
-		"updatable_media_stubs",
-
-		"android_stubs_current",
-		"android_system_stubs_current",
-		"android_test_stubs_current",
-		"core.current.stubs",
-		"core.platform.api.stubs",
-	}
-
-	for _, extra := range extraModules {
-		bp += fmt.Sprintf(`
-			java_library {
-				name: "%s",
-				srcs: ["a.java"],
-				no_standard_libs: true,
-				sdk_version: "core_current",
-				system_modules: "core-platform-api-stubs-system-modules",
-			}
-		`, extra)
-	}
-
-	bp += `
-		android_app {
-			name: "framework-res",
-			no_framework_libs: true,
-		}
-	`
-
-	systemModules := []string{
-		"core-system-modules",
-		"core-platform-api-stubs-system-modules",
-		"android_stubs_current_system_modules",
-		"android_system_stubs_current_system_modules",
-		"android_test_stubs_current_system_modules",
-	}
-
-	for _, extra := range systemModules {
-		bp += fmt.Sprintf(`
-			java_system_modules {
-				name: "%s",
-			}
-		`, extra)
-	}
-
+	bp += java.GatherRequiredDepsForTest()
 	bp += cc.GatherRequiredDepsForTest(android.Android)
 
 	mockFS := map[string][]byte{
@@ -224,16 +176,12 @@
 }
 
 func testConfig(env map[string]string) android.Config {
-	if env == nil {
-		env = make(map[string]string)
-	}
-	if env["ANDROID_JAVA8_HOME"] == "" {
-		env["ANDROID_JAVA8_HOME"] = "jdk8"
-	}
-	config := android.TestArchConfig(buildDir, env)
+	config := java.TestConfig(buildDir, env)
+
 	config.TestProductVariables.DeviceSystemSdkVersions = []string{"28"}
 	config.TestProductVariables.DeviceVndkVersion = proptools.StringPtr("current")
 	config.TestProductVariables.Platform_vndk_version = proptools.StringPtr("VER")
+
 	return config
 
 }
diff --git a/tradefed/autogen.go b/tradefed/autogen.go
index e6a1b48..cfa7164 100644
--- a/tradefed/autogen.go
+++ b/tradefed/autogen.go
@@ -15,8 +15,6 @@
 package tradefed
 
 import (
-	"strings"
-
 	"github.com/google/blueprint"
 
 	"android/soong/android"
@@ -40,10 +38,10 @@
 	CommandDeps: []string{"$template"},
 }, "name", "template")
 
-func testConfigPath(ctx android.ModuleContext, prop *string) (path android.Path, autogenPath android.WritablePath) {
+func testConfigPath(ctx android.ModuleContext, prop *string, testSuites []string) (path android.Path, autogenPath android.WritablePath) {
 	if p := getTestConfig(ctx, prop); p != nil {
 		return p, nil
-	} else if !strings.HasPrefix(ctx.ModuleDir(), "cts/") {
+	} else if !android.InList("cts", testSuites) {
 		outputFile := android.PathForModuleOut(ctx, ctx.ModuleName()+".config")
 		return nil, outputFile
 	} else {
@@ -67,8 +65,8 @@
 }
 
 func AutoGenNativeTestConfig(ctx android.ModuleContext, testConfigProp *string,
-	testConfigTemplateProp *string) android.Path {
-	path, autogenPath := testConfigPath(ctx, testConfigProp)
+	testConfigTemplateProp *string, testSuites []string) android.Path {
+	path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites)
 	if autogenPath != nil {
 		templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp)
 		if templatePath.Valid() {
@@ -86,8 +84,8 @@
 }
 
 func AutoGenNativeBenchmarkTestConfig(ctx android.ModuleContext, testConfigProp *string,
-	testConfigTemplateProp *string) android.Path {
-	path, autogenPath := testConfigPath(ctx, testConfigProp)
+	testConfigTemplateProp *string, testSuites []string) android.Path {
+	path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites)
 	if autogenPath != nil {
 		templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp)
 		if templatePath.Valid() {
@@ -100,8 +98,8 @@
 	return path
 }
 
-func AutoGenJavaTestConfig(ctx android.ModuleContext, testConfigProp *string, testConfigTemplateProp *string) android.Path {
-	path, autogenPath := testConfigPath(ctx, testConfigProp)
+func AutoGenJavaTestConfig(ctx android.ModuleContext, testConfigProp *string, testConfigTemplateProp *string, testSuites []string) android.Path {
+	path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites)
 	if autogenPath != nil {
 		templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp)
 		if templatePath.Valid() {
@@ -119,9 +117,9 @@
 }
 
 func AutoGenPythonBinaryHostTestConfig(ctx android.ModuleContext, testConfigProp *string,
-	testConfigTemplateProp *string) android.Path {
+	testConfigTemplateProp *string, testSuites []string) android.Path {
 
-	path, autogenPath := testConfigPath(ctx, testConfigProp)
+	path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites)
 	if autogenPath != nil {
 		templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp)
 		if templatePath.Valid() {
@@ -143,8 +141,8 @@
 	},
 }, "name", "template")
 
-func AutoGenInstrumentationTestConfig(ctx android.ModuleContext, testConfigProp *string, testConfigTemplateProp *string, manifest android.Path) android.Path {
-	path, autogenPath := testConfigPath(ctx, testConfigProp)
+func AutoGenInstrumentationTestConfig(ctx android.ModuleContext, testConfigProp *string, testConfigTemplateProp *string, manifest android.Path, testSuites []string) android.Path {
+	path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites)
 	if autogenPath != nil {
 		template := "${InstrumentationTestConfigTemplate}"
 		moduleTemplate := getTestConfigTemplate(ctx, testConfigTemplateProp)
diff --git a/ui/build/paths/config.go b/ui/build/paths/config.go
index d4922f3..fb30f85 100644
--- a/ui/build/paths/config.go
+++ b/ui/build/paths/config.go
@@ -74,41 +74,39 @@
 }
 
 var Configuration = map[string]PathConfig{
-	"bash":      Allowed,
-	"bc":        Allowed,
-	"bzip2":     Allowed,
-	"date":      Allowed,
-	"dd":        Allowed,
-	"diff":      Allowed,
-	"egrep":     Allowed,
-	"find":      Allowed,
-	"fuser":     Allowed,
-	"getopt":    Allowed,
-	"git":       Allowed,
-	"grep":      Allowed,
-	"gzip":      Allowed,
-	"hexdump":   Allowed,
-	"jar":       Allowed,
-	"java":      Allowed,
-	"javap":     Allowed,
-	"lsof":      Allowed,
-	"m4":        Allowed,
-	"openssl":   Allowed,
-	"patch":     Allowed,
-	"pstree":    Allowed,
-	"python":    Allowed,
-	"python2.7": Allowed,
-	"python3":   Allowed,
-	"realpath":  Allowed,
-	"rsync":     Allowed,
-	"sh":        Allowed,
-	"tar":       Allowed,
-	"timeout":   Allowed,
-	"tr":        Allowed,
-	"unzip":     Allowed,
-	"xz":        Allowed,
-	"zip":       Allowed,
-	"zipinfo":   Allowed,
+	"bash":     Allowed,
+	"bc":       Allowed,
+	"bzip2":    Allowed,
+	"date":     Allowed,
+	"dd":       Allowed,
+	"diff":     Allowed,
+	"egrep":    Allowed,
+	"find":     Allowed,
+	"fuser":    Allowed,
+	"getopt":   Allowed,
+	"git":      Allowed,
+	"grep":     Allowed,
+	"gzip":     Allowed,
+	"hexdump":  Allowed,
+	"jar":      Allowed,
+	"java":     Allowed,
+	"javap":    Allowed,
+	"lsof":     Allowed,
+	"m4":       Allowed,
+	"openssl":  Allowed,
+	"patch":    Allowed,
+	"pstree":   Allowed,
+	"python3":  Allowed,
+	"realpath": Allowed,
+	"rsync":    Allowed,
+	"sh":       Allowed,
+	"tar":      Allowed,
+	"timeout":  Allowed,
+	"tr":       Allowed,
+	"unzip":    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.