Merge "Set usesNonSdkApi in manifest when Platform_apis=true"
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/config.go b/android/config.go
index 63788b7..9217aab 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)
 }
 
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/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/coverage.go b/cc/coverage.go
index cf67c9f..0549705 100644
--- a/cc/coverage.go
+++ b/cc/coverage.go
@@ -109,8 +109,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/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..eacb86a 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).
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/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/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/binary.go b/python/binary.go
index bf9acb4..140f07a 100644
--- a/python/binary.go
+++ b/python/binary.go
@@ -42,6 +42,11 @@
 	// list of compatibility suites (for example "cts", "vts") that the module should be
 	// installed into.
 	Test_suites []string `android:"arch_variant"`
+
+	// whether to use `main` when starting the executable. The default is true, when set to
+	// false it will act much like the normal `python` executable, but with the sources and
+	// libraries automatically included in the PYTHONPATH.
+	Autorun *bool `android:"arch_variant"`
 }
 
 type binaryDecorator struct {
@@ -74,6 +79,10 @@
 	return module.Init()
 }
 
+func (binary *binaryDecorator) autorun() bool {
+	return BoolDefault(binary.binaryProperties.Autorun, true)
+}
+
 func (binary *binaryDecorator) bootstrapperProps() []interface{} {
 	return []interface{}{&binary.binaryProperties}
 }
@@ -82,7 +91,10 @@
 	embeddedLauncher bool, srcsPathMappings []pathMapping, srcsZip android.Path,
 	depsSrcsZips android.Paths) android.OptionalPath {
 
-	main := binary.getPyMainFile(ctx, srcsPathMappings)
+	main := ""
+	if binary.autorun() {
+		main = binary.getPyMainFile(ctx, srcsPathMappings)
+	}
 
 	var launcherPath android.OptionalPath
 	if embeddedLauncher {
diff --git a/python/builder.go b/python/builder.go
index e277bfd..e3b490c 100644
--- a/python/builder.go
+++ b/python/builder.go
@@ -54,7 +54,6 @@
 
 	embeddedPar = pctx.AndroidStaticRule("embeddedPar",
 		blueprint.RuleParams{
-			// `echo -n` to trim the newline, since the python code just wants the name
 			Command: `rm -f $out.main && ` +
 				`sed 's/ENTRY_POINT/$main/' build/soong/python/scripts/main.py >$out.main &&` +
 				`$mergeParCmd -p -pm $out.main --prefix $launcher $out $srcsZips && ` +
@@ -62,6 +61,14 @@
 			CommandDeps: []string{"$mergeParCmd", "$parCmd", "build/soong/python/scripts/main.py"},
 		},
 		"main", "srcsZips", "launcher")
+
+	embeddedParNoMain = pctx.AndroidStaticRule("embeddedParNoMain",
+		blueprint.RuleParams{
+			Command: `$mergeParCmd -p --prefix $launcher $out $srcsZips && ` +
+				`chmod +x $out`,
+			CommandDeps: []string{"$mergeParCmd"},
+		},
+		"srcsZips", "launcher")
 )
 
 func init() {
@@ -107,17 +114,30 @@
 		// added launcherPath to the implicits Ninja dependencies.
 		implicits = append(implicits, launcherPath.Path())
 
-		ctx.Build(pctx, android.BuildParams{
-			Rule:        embeddedPar,
-			Description: "embedded python archive",
-			Output:      binFile,
-			Implicits:   implicits,
-			Args: map[string]string{
-				"main":     strings.Replace(strings.TrimSuffix(main, pyExt), "/", ".", -1),
-				"srcsZips": strings.Join(srcsZips.Strings(), " "),
-				"launcher": launcherPath.String(),
-			},
-		})
+		if main == "" {
+			ctx.Build(pctx, android.BuildParams{
+				Rule:        embeddedParNoMain,
+				Description: "embedded python archive",
+				Output:      binFile,
+				Implicits:   implicits,
+				Args: map[string]string{
+					"srcsZips": strings.Join(srcsZips.Strings(), " "),
+					"launcher": launcherPath.String(),
+				},
+			})
+		} else {
+			ctx.Build(pctx, android.BuildParams{
+				Rule:        embeddedPar,
+				Description: "embedded python archive",
+				Output:      binFile,
+				Implicits:   implicits,
+				Args: map[string]string{
+					"main":     strings.Replace(strings.TrimSuffix(main, pyExt), "/", ".", -1),
+					"srcsZips": strings.Join(srcsZips.Strings(), " "),
+					"launcher": launcherPath.String(),
+				},
+			})
+		}
 	}
 
 	return binFile
diff --git a/python/python.go b/python/python.go
index ddc3f1f..4445f40 100644
--- a/python/python.go
+++ b/python/python.go
@@ -156,6 +156,8 @@
 	bootstrap(ctx android.ModuleContext, ActualVersion string, embeddedLauncher bool,
 		srcsPathMappings []pathMapping, srcsZip android.Path,
 		depsSrcsZips android.Paths) android.OptionalPath
+
+	autorun() bool
 }
 
 type installer interface {
@@ -307,9 +309,14 @@
 
 		if p.bootstrapper != nil && p.isEmbeddedLauncherEnabled(pyVersion2) {
 			ctx.AddVariationDependencies(nil, pythonLibTag, "py2-stdlib")
+
+			launcherModule := "py2-launcher"
+			if p.bootstrapper.autorun() {
+				launcherModule = "py2-launcher-autorun"
+			}
 			ctx.AddFarVariationDependencies([]blueprint.Variation{
 				{Mutator: "arch", Variation: ctx.Target().String()},
-			}, launcherTag, "py2-launcher")
+			}, launcherTag, launcherModule)
 
 			// Add py2-launcher shared lib dependencies. Ideally, these should be
 			// derived from the `shared_libs` property of "py2-launcher". However, we
@@ -422,7 +429,11 @@
 			p.properties.Actual_version, ctx.ModuleName()))
 	}
 	expandedSrcs := ctx.ExpandSources(srcs, exclude_srcs)
-	if len(expandedSrcs) == 0 {
+	requiresSrcs := true
+	if p.bootstrapper != nil && !p.bootstrapper.autorun() {
+		requiresSrcs = false
+	}
+	if len(expandedSrcs) == 0 && requiresSrcs {
 		ctx.ModuleErrorf("doesn't have any source files!")
 	}
 
@@ -656,4 +667,5 @@
 }
 
 var Bool = proptools.Bool
+var BoolDefault = proptools.BoolDefault
 var String = proptools.String
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
 
 }