Merge "Include jacoco in the ART boot image if EMMA_INSTRUMENT_FRAMEWORK=true."
diff --git a/android/hooks.go b/android/hooks.go
index 604cb9c..04ba69e 100644
--- a/android/hooks.go
+++ b/android/hooks.go
@@ -15,7 +15,10 @@
 package android
 
 import (
+	"reflect"
+
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
 )
 
 // This file implements hooks that external module types can use to inject logic into existing
@@ -26,53 +29,78 @@
 // before the module has been split into architecture variants, and before defaults modules have
 // been applied.
 type LoadHookContext interface {
-	// TODO: a new context that includes Config() but not Target(), etc.?
-	BaseModuleContext
+	EarlyModuleContext
+
 	AppendProperties(...interface{})
 	PrependProperties(...interface{})
 	CreateModule(ModuleFactory, ...interface{}) Module
 }
 
-// Arch hooks are run after the module has been split into architecture variants, and can be used
-// to add architecture-specific properties.
-type ArchHookContext interface {
-	BaseModuleContext
-	AppendProperties(...interface{})
-	PrependProperties(...interface{})
-}
-
 func AddLoadHook(m blueprint.Module, hook func(LoadHookContext)) {
-	h := &m.(Module).base().hooks
-	h.load = append(h.load, hook)
+	blueprint.AddLoadHook(m, func(ctx blueprint.LoadHookContext) {
+		actx := &loadHookContext{
+			earlyModuleContext: m.(Module).base().earlyModuleContextFactory(ctx),
+			bp:                 ctx,
+		}
+		hook(actx)
+	})
 }
 
-func AddArchHook(m blueprint.Module, hook func(ArchHookContext)) {
-	h := &m.(Module).base().hooks
-	h.arch = append(h.arch, hook)
+type loadHookContext struct {
+	earlyModuleContext
+	bp     blueprint.LoadHookContext
+	module Module
 }
 
-func (x *hooks) runLoadHooks(ctx LoadHookContext, m *ModuleBase) {
-	if len(x.load) > 0 {
-		for _, x := range x.load {
-			x(ctx)
-			if ctx.Failed() {
-				return
+func (l *loadHookContext) AppendProperties(props ...interface{}) {
+	for _, p := range props {
+		err := proptools.AppendMatchingProperties(l.Module().base().customizableProperties,
+			p, nil)
+		if err != nil {
+			if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
+				l.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
+			} else {
+				panic(err)
 			}
 		}
 	}
 }
 
-func (x *hooks) runArchHooks(ctx ArchHookContext, m *ModuleBase) {
-	if len(x.arch) > 0 {
-		for _, x := range x.arch {
-			x(ctx)
-			if ctx.Failed() {
-				return
+func (l *loadHookContext) PrependProperties(props ...interface{}) {
+	for _, p := range props {
+		err := proptools.PrependMatchingProperties(l.Module().base().customizableProperties,
+			p, nil)
+		if err != nil {
+			if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
+				l.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
+			} else {
+				panic(err)
 			}
 		}
 	}
 }
 
+func (l *loadHookContext) CreateModule(factory ModuleFactory, props ...interface{}) Module {
+	inherited := []interface{}{&l.Module().base().commonProperties}
+	module := l.bp.CreateModule(ModuleFactoryAdaptor(factory), append(inherited, props...)...).(Module)
+
+	if l.Module().base().variableProperties != nil && module.base().variableProperties != nil {
+		src := l.Module().base().variableProperties
+		dst := []interface{}{
+			module.base().variableProperties,
+			// Put an empty copy of the src properties into dst so that properties in src that are not in dst
+			// don't cause a "failed to find property to extend" error.
+			proptools.CloneEmptyProperties(reflect.ValueOf(src).Elem()).Interface(),
+		}
+		err := proptools.AppendMatchingProperties(dst, src, nil)
+		if err != nil {
+			panic(err)
+		}
+	}
+
+	return module
+}
+
 type InstallHookContext interface {
 	ModuleContext
 	Path() InstallPath
@@ -118,31 +146,5 @@
 }
 
 type hooks struct {
-	load    []func(LoadHookContext)
-	arch    []func(ArchHookContext)
 	install []func(InstallHookContext)
 }
-
-func registerLoadHookMutator(ctx RegisterMutatorsContext) {
-	ctx.TopDown("load_hooks", LoadHookMutator).Parallel()
-}
-
-func LoadHookMutator(ctx TopDownMutatorContext) {
-	if m, ok := ctx.Module().(Module); ok {
-		m.base().commonProperties.DebugName = ctx.ModuleName()
-
-		// Cast through *topDownMutatorContext because AppendProperties is implemented
-		// on *topDownMutatorContext but not exposed through TopDownMutatorContext
-		var loadHookCtx LoadHookContext = ctx.(*topDownMutatorContext)
-		m.base().hooks.runLoadHooks(loadHookCtx, m.base())
-	}
-}
-
-func archHookMutator(ctx TopDownMutatorContext) {
-	if m, ok := ctx.Module().(Module); ok {
-		// Cast through *topDownMutatorContext because AppendProperties is implemented
-		// on *topDownMutatorContext but not exposed through TopDownMutatorContext
-		var archHookCtx ArchHookContext = ctx.(*topDownMutatorContext)
-		m.base().hooks.runArchHooks(archHookCtx, m.base())
-	}
-}
diff --git a/android/module.go b/android/module.go
index a14e575..c998007 100644
--- a/android/module.go
+++ b/android/module.go
@@ -55,16 +55,51 @@
 
 type ModuleBuildParams BuildParams
 
+// EarlyModuleContext provides methods that can be called early, as soon as the properties have
+// been parsed into the module and before any mutators have run.
+type EarlyModuleContext interface {
+	Module() Module
+	ModuleName() string
+	ModuleDir() string
+	ModuleType() string
+
+	ContainsProperty(name string) bool
+	Errorf(pos scanner.Position, fmt string, args ...interface{})
+	ModuleErrorf(fmt string, args ...interface{})
+	PropertyErrorf(property, fmt string, args ...interface{})
+	Failed() bool
+
+	AddNinjaFileDeps(deps ...string)
+
+	DeviceSpecific() bool
+	SocSpecific() bool
+	ProductSpecific() bool
+	SystemExtSpecific() bool
+	Platform() bool
+
+	Config() Config
+	DeviceConfig() DeviceConfig
+
+	// Deprecated: use Config()
+	AConfig() Config
+
+	// GlobWithDeps returns a list of files that match the specified pattern but do not match any
+	// of the patterns in excludes.  It also adds efficient dependencies to rerun the primary
+	// builder whenever a file matching the pattern as added or removed, without rerunning if a
+	// file that does not match the pattern is added to a searched directory.
+	GlobWithDeps(pattern string, excludes []string) ([]string, error)
+
+	Glob(globPattern string, excludes []string) Paths
+	GlobFiles(globPattern string, excludes []string) Paths
+	Fs() pathtools.FileSystem
+}
+
 // BaseModuleContext is the same as blueprint.BaseModuleContext except that Config() returns
 // a Config instead of an interface{}, and some methods have been wrapped to use an android.Module
 // instead of a blueprint.Module, plus some extra methods that return Android-specific information
 // about the current module.
 type BaseModuleContext interface {
-	Module() Module
-	ModuleName() string
-	ModuleDir() string
-	ModuleType() string
-	Config() Config
+	EarlyModuleContext
 
 	OtherModuleName(m blueprint.Module) string
 	OtherModuleDir(m blueprint.Module) string
@@ -91,24 +126,6 @@
 	// and returns a top-down dependency path from a start module to current child module.
 	GetWalkPath() []Module
 
-	ContainsProperty(name string) bool
-	Errorf(pos scanner.Position, fmt string, args ...interface{})
-	ModuleErrorf(fmt string, args ...interface{})
-	PropertyErrorf(property, fmt string, args ...interface{})
-	Failed() bool
-
-	// GlobWithDeps returns a list of files that match the specified pattern but do not match any
-	// of the patterns in excludes.  It also adds efficient dependencies to rerun the primary
-	// builder whenever a file matching the pattern as added or removed, without rerunning if a
-	// file that does not match the pattern is added to a searched directory.
-	GlobWithDeps(pattern string, excludes []string) ([]string, error)
-
-	Glob(globPattern string, excludes []string) Paths
-	GlobFiles(globPattern string, excludes []string) Paths
-
-	Fs() pathtools.FileSystem
-	AddNinjaFileDeps(deps ...string)
-
 	AddMissingDependencies(missingDeps []string)
 
 	Target() Target
@@ -123,18 +140,11 @@
 	Windows() bool
 	Debug() bool
 	PrimaryArch() bool
-	Platform() bool
-	DeviceSpecific() bool
-	SocSpecific() bool
-	ProductSpecific() bool
-	SystemExtSpecific() bool
-	AConfig() Config
-	DeviceConfig() DeviceConfig
 }
 
-// Deprecated: use BaseModuleContext instead
+// Deprecated: use EarlyModuleContext instead
 type BaseContext interface {
-	BaseModuleContext
+	EarlyModuleContext
 }
 
 type ModuleContext interface {
@@ -221,6 +231,10 @@
 
 	// Get the visibility rules that control the visibility of this module.
 	visibility() []string
+
+	RequiredModuleNames() []string
+	HostRequiredModuleNames() []string
+	TargetRequiredModuleNames() []string
 }
 
 // Qualified id for a module
@@ -887,6 +901,18 @@
 	return m.base().commonProperties.ImageVariation == RecoveryVariation
 }
 
+func (m *ModuleBase) RequiredModuleNames() []string {
+	return m.base().commonProperties.Required
+}
+
+func (m *ModuleBase) HostRequiredModuleNames() []string {
+	return m.base().commonProperties.Host_required
+}
+
+func (m *ModuleBase) TargetRequiredModuleNames() []string {
+	return m.base().commonProperties.Target_required
+}
+
 func (m *ModuleBase) generateModuleTarget(ctx ModuleContext) {
 	allInstalledFiles := Paths{}
 	allCheckbuildFiles := Paths{}
@@ -943,7 +969,7 @@
 	}
 }
 
-func determineModuleKind(m *ModuleBase, ctx blueprint.BaseModuleContext) moduleKind {
+func determineModuleKind(m *ModuleBase, ctx blueprint.EarlyModuleContext) moduleKind {
 	var socSpecific = Bool(m.commonProperties.Vendor) || Bool(m.commonProperties.Proprietary) || Bool(m.commonProperties.Soc_specific)
 	var deviceSpecific = Bool(m.commonProperties.Device_specific)
 	var productSpecific = Bool(m.commonProperties.Product_specific)
@@ -1002,15 +1028,22 @@
 	}
 }
 
+func (m *ModuleBase) earlyModuleContextFactory(ctx blueprint.EarlyModuleContext) earlyModuleContext {
+	return earlyModuleContext{
+		EarlyModuleContext: ctx,
+		kind:               determineModuleKind(m, ctx),
+		config:             ctx.Config().(Config),
+	}
+}
+
 func (m *ModuleBase) baseModuleContextFactory(ctx blueprint.BaseModuleContext) baseModuleContext {
 	return baseModuleContext{
-		BaseModuleContext: ctx,
-		os:                m.commonProperties.CompileOS,
-		target:            m.commonProperties.CompileTarget,
-		targetPrimary:     m.commonProperties.CompilePrimary,
-		multiTargets:      m.commonProperties.CompileMultiTargets,
-		kind:              determineModuleKind(m, ctx),
-		config:            ctx.Config().(Config),
+		bp:                 ctx,
+		earlyModuleContext: m.earlyModuleContextFactory(ctx),
+		os:                 m.commonProperties.CompileOS,
+		target:             m.commonProperties.CompileTarget,
+		targetPrimary:      m.commonProperties.CompilePrimary,
+		multiTargets:       m.commonProperties.CompileMultiTargets,
 	}
 }
 
@@ -1116,21 +1149,95 @@
 	m.variables = ctx.variables
 }
 
+type earlyModuleContext struct {
+	blueprint.EarlyModuleContext
+
+	kind   moduleKind
+	config Config
+}
+
+func (e *earlyModuleContext) Glob(globPattern string, excludes []string) Paths {
+	ret, err := e.GlobWithDeps(globPattern, excludes)
+	if err != nil {
+		e.ModuleErrorf("glob: %s", err.Error())
+	}
+	return pathsForModuleSrcFromFullPath(e, ret, true)
+}
+
+func (e *earlyModuleContext) GlobFiles(globPattern string, excludes []string) Paths {
+	ret, err := e.GlobWithDeps(globPattern, excludes)
+	if err != nil {
+		e.ModuleErrorf("glob: %s", err.Error())
+	}
+	return pathsForModuleSrcFromFullPath(e, ret, false)
+}
+
+func (e *earlyModuleContext) Module() Module {
+	module, _ := e.EarlyModuleContext.Module().(Module)
+	return module
+}
+
+func (e *earlyModuleContext) Config() Config {
+	return e.EarlyModuleContext.Config().(Config)
+}
+
+func (e *earlyModuleContext) AConfig() Config {
+	return e.config
+}
+
+func (e *earlyModuleContext) DeviceConfig() DeviceConfig {
+	return DeviceConfig{e.config.deviceConfig}
+}
+
+func (e *earlyModuleContext) Platform() bool {
+	return e.kind == platformModule
+}
+
+func (e *earlyModuleContext) DeviceSpecific() bool {
+	return e.kind == deviceSpecificModule
+}
+
+func (e *earlyModuleContext) SocSpecific() bool {
+	return e.kind == socSpecificModule
+}
+
+func (e *earlyModuleContext) ProductSpecific() bool {
+	return e.kind == productSpecificModule
+}
+
+func (e *earlyModuleContext) SystemExtSpecific() bool {
+	return e.kind == systemExtSpecificModule
+}
+
 type baseModuleContext struct {
-	blueprint.BaseModuleContext
+	bp blueprint.BaseModuleContext
+	earlyModuleContext
 	os            OsType
 	target        Target
 	multiTargets  []Target
 	targetPrimary bool
 	debug         bool
-	kind          moduleKind
-	config        Config
 
 	walkPath []Module
 
 	strictVisitDeps bool // If true, enforce that all dependencies are enabled
 }
 
+func (b *baseModuleContext) OtherModuleName(m blueprint.Module) string { return b.bp.OtherModuleName(m) }
+func (b *baseModuleContext) OtherModuleDir(m blueprint.Module) string  { return b.bp.OtherModuleDir(m) }
+func (b *baseModuleContext) OtherModuleErrorf(m blueprint.Module, fmt string, args ...interface{}) {
+	b.bp.OtherModuleErrorf(m, fmt, args)
+}
+func (b *baseModuleContext) OtherModuleDependencyTag(m blueprint.Module) blueprint.DependencyTag {
+	return b.bp.OtherModuleDependencyTag(m)
+}
+func (b *baseModuleContext) OtherModuleExists(name string) bool        { return b.bp.OtherModuleExists(name) }
+func (b *baseModuleContext) OtherModuleType(m blueprint.Module) string { return b.bp.OtherModuleType(m) }
+
+func (b *baseModuleContext) GetDirectDepWithTag(name string, tag blueprint.DependencyTag) blueprint.Module {
+	return b.bp.GetDirectDepWithTag(name, tag)
+}
+
 type moduleContext struct {
 	bp blueprint.ModuleContext
 	baseModuleContext
@@ -1245,16 +1352,6 @@
 
 	m.bp.Build(pctx.PackageContext, convertBuildParams(params))
 }
-
-func (b *baseModuleContext) Module() Module {
-	module, _ := b.BaseModuleContext.Module().(Module)
-	return module
-}
-
-func (b *baseModuleContext) Config() Config {
-	return b.BaseModuleContext.Config().(Config)
-}
-
 func (m *moduleContext) GetMissingDependencies() []string {
 	var missingDeps []string
 	missingDeps = append(missingDeps, m.Module().base().commonProperties.MissingDeps...)
@@ -1302,7 +1399,7 @@
 	var deps []dep
 	b.VisitDirectDepsBlueprint(func(module blueprint.Module) {
 		if aModule, _ := module.(Module); aModule != nil && aModule.base().BaseModuleName() == name {
-			returnedTag := b.BaseModuleContext.OtherModuleDependencyTag(aModule)
+			returnedTag := b.bp.OtherModuleDependencyTag(aModule)
 			if tag == nil || returnedTag == tag {
 				deps = append(deps, dep{aModule, returnedTag})
 			}
@@ -1322,7 +1419,7 @@
 	var deps []Module
 	b.VisitDirectDepsBlueprint(func(module blueprint.Module) {
 		if aModule, _ := module.(Module); aModule != nil {
-			if b.BaseModuleContext.OtherModuleDependencyTag(aModule) == tag {
+			if b.bp.OtherModuleDependencyTag(aModule) == tag {
 				deps = append(deps, aModule)
 			}
 		}
@@ -1340,11 +1437,11 @@
 }
 
 func (b *baseModuleContext) VisitDirectDepsBlueprint(visit func(blueprint.Module)) {
-	b.BaseModuleContext.VisitDirectDeps(visit)
+	b.bp.VisitDirectDeps(visit)
 }
 
 func (b *baseModuleContext) VisitDirectDeps(visit func(Module)) {
-	b.BaseModuleContext.VisitDirectDeps(func(module blueprint.Module) {
+	b.bp.VisitDirectDeps(func(module blueprint.Module) {
 		if aModule := b.validateAndroidModule(module, b.strictVisitDeps); aModule != nil {
 			visit(aModule)
 		}
@@ -1352,9 +1449,9 @@
 }
 
 func (b *baseModuleContext) VisitDirectDepsWithTag(tag blueprint.DependencyTag, visit func(Module)) {
-	b.BaseModuleContext.VisitDirectDeps(func(module blueprint.Module) {
+	b.bp.VisitDirectDeps(func(module blueprint.Module) {
 		if aModule := b.validateAndroidModule(module, b.strictVisitDeps); aModule != nil {
-			if b.BaseModuleContext.OtherModuleDependencyTag(aModule) == tag {
+			if b.bp.OtherModuleDependencyTag(aModule) == tag {
 				visit(aModule)
 			}
 		}
@@ -1362,7 +1459,7 @@
 }
 
 func (b *baseModuleContext) VisitDirectDepsIf(pred func(Module) bool, visit func(Module)) {
-	b.BaseModuleContext.VisitDirectDepsIf(
+	b.bp.VisitDirectDepsIf(
 		// pred
 		func(module blueprint.Module) bool {
 			if aModule := b.validateAndroidModule(module, b.strictVisitDeps); aModule != nil {
@@ -1378,7 +1475,7 @@
 }
 
 func (b *baseModuleContext) VisitDepsDepthFirst(visit func(Module)) {
-	b.BaseModuleContext.VisitDepsDepthFirst(func(module blueprint.Module) {
+	b.bp.VisitDepsDepthFirst(func(module blueprint.Module) {
 		if aModule := b.validateAndroidModule(module, b.strictVisitDeps); aModule != nil {
 			visit(aModule)
 		}
@@ -1386,7 +1483,7 @@
 }
 
 func (b *baseModuleContext) VisitDepsDepthFirstIf(pred func(Module) bool, visit func(Module)) {
-	b.BaseModuleContext.VisitDepsDepthFirstIf(
+	b.bp.VisitDepsDepthFirstIf(
 		// pred
 		func(module blueprint.Module) bool {
 			if aModule := b.validateAndroidModule(module, b.strictVisitDeps); aModule != nil {
@@ -1402,12 +1499,12 @@
 }
 
 func (b *baseModuleContext) WalkDepsBlueprint(visit func(blueprint.Module, blueprint.Module) bool) {
-	b.BaseModuleContext.WalkDeps(visit)
+	b.bp.WalkDeps(visit)
 }
 
 func (b *baseModuleContext) WalkDeps(visit func(Module, Module) bool) {
 	b.walkPath = []Module{b.Module()}
-	b.BaseModuleContext.WalkDeps(func(child, parent blueprint.Module) bool {
+	b.bp.WalkDeps(func(child, parent blueprint.Module) bool {
 		childAndroidModule, _ := child.(Module)
 		parentAndroidModule, _ := parent.(Module)
 		if childAndroidModule != nil && parentAndroidModule != nil {
@@ -1496,34 +1593,6 @@
 	return b.target.Arch.ArchType == b.config.Targets[b.target.Os][0].Arch.ArchType
 }
 
-func (b *baseModuleContext) AConfig() Config {
-	return b.config
-}
-
-func (b *baseModuleContext) DeviceConfig() DeviceConfig {
-	return DeviceConfig{b.config.deviceConfig}
-}
-
-func (b *baseModuleContext) Platform() bool {
-	return b.kind == platformModule
-}
-
-func (b *baseModuleContext) DeviceSpecific() bool {
-	return b.kind == deviceSpecificModule
-}
-
-func (b *baseModuleContext) SocSpecific() bool {
-	return b.kind == socSpecificModule
-}
-
-func (b *baseModuleContext) ProductSpecific() bool {
-	return b.kind == productSpecificModule
-}
-
-func (b *baseModuleContext) SystemExtSpecific() bool {
-	return b.kind == systemExtSpecificModule
-}
-
 // Makes this module a platform module, i.e. not specific to soc, device,
 // product, or system_ext.
 func (m *ModuleBase) MakeAsPlatform() {
@@ -1877,31 +1946,15 @@
 }
 
 func (m *moduleContext) RequiredModuleNames() []string {
-	return m.module.base().commonProperties.Required
+	return m.module.RequiredModuleNames()
 }
 
 func (m *moduleContext) HostRequiredModuleNames() []string {
-	return m.module.base().commonProperties.Host_required
+	return m.module.HostRequiredModuleNames()
 }
 
 func (m *moduleContext) TargetRequiredModuleNames() []string {
-	return m.module.base().commonProperties.Target_required
-}
-
-func (b *baseModuleContext) Glob(globPattern string, excludes []string) Paths {
-	ret, err := b.GlobWithDeps(globPattern, excludes)
-	if err != nil {
-		b.ModuleErrorf("glob: %s", err.Error())
-	}
-	return pathsForModuleSrcFromFullPath(b, ret, true)
-}
-
-func (b *baseModuleContext) GlobFiles(globPattern string, excludes []string) Paths {
-	ret, err := b.GlobWithDeps(globPattern, excludes)
-	if err != nil {
-		b.ModuleErrorf("glob: %s", err.Error())
-	}
-	return pathsForModuleSrcFromFullPath(b, ret, false)
+	return m.module.TargetRequiredModuleNames()
 }
 
 func init() {
diff --git a/android/mutator.go b/android/mutator.go
index c2bae44..f2f9663 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -75,7 +75,6 @@
 type RegisterMutatorFunc func(RegisterMutatorsContext)
 
 var preArch = []RegisterMutatorFunc{
-	registerLoadHookMutator,
 	RegisterNamespaceMutator,
 	// Rename package module types.
 	RegisterPackageRenamer,
@@ -89,7 +88,6 @@
 	ctx.BottomUp("os", osMutator).Parallel()
 	ctx.BottomUp("image", imageMutator).Parallel()
 	ctx.BottomUp("arch", archMutator).Parallel()
-	ctx.TopDown("arch_hooks", archHookMutator).Parallel()
 }
 
 var preDeps = []RegisterMutatorFunc{
diff --git a/android/namespace.go b/android/namespace.go
index 27ec163..64ad7e9 100644
--- a/android/namespace.go
+++ b/android/namespace.go
@@ -185,6 +185,7 @@
 	if ok {
 		// inform the module whether its namespace is one that we want to export to Make
 		amod.base().commonProperties.NamespaceExportedToMake = ns.exportToKati
+		amod.base().commonProperties.DebugName = module.Name()
 	}
 
 	return ns, nil
diff --git a/android/paths.go b/android/paths.go
index c841372..a03fe17 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -409,7 +409,7 @@
 // each string. If incDirs is false, strip paths with a trailing '/' from the list.
 // It intended for use in globs that only list files that exist, so it allows '$' in
 // filenames.
-func pathsForModuleSrcFromFullPath(ctx BaseModuleContext, paths []string, incDirs bool) Paths {
+func pathsForModuleSrcFromFullPath(ctx EarlyModuleContext, paths []string, incDirs bool) Paths {
 	prefix := filepath.Join(ctx.Config().srcDir, ctx.ModuleDir()) + "/"
 	if prefix == "./" {
 		prefix = ""
diff --git a/android/paths_test.go b/android/paths_test.go
index 5ff5f99..ec5e598 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -286,7 +286,9 @@
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
-					kind:   socSpecificModule,
+					earlyModuleContext: earlyModuleContext{
+						kind: socSpecificModule,
+					},
 				},
 			},
 			in:  []string{"bin", "my_test"},
@@ -298,7 +300,9 @@
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
-					kind:   deviceSpecificModule,
+					earlyModuleContext: earlyModuleContext{
+						kind: deviceSpecificModule,
+					},
 				},
 			},
 			in:  []string{"bin", "my_test"},
@@ -310,7 +314,9 @@
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
-					kind:   productSpecificModule,
+					earlyModuleContext: earlyModuleContext{
+						kind: productSpecificModule,
+					},
 				},
 			},
 			in:  []string{"bin", "my_test"},
@@ -322,7 +328,9 @@
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
-					kind:   systemExtSpecificModule,
+					earlyModuleContext: earlyModuleContext{
+						kind: systemExtSpecificModule,
+					},
 				},
 			},
 			in:  []string{"bin", "my_test"},
@@ -384,7 +392,9 @@
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
-					kind:   socSpecificModule,
+					earlyModuleContext: earlyModuleContext{
+						kind: socSpecificModule,
+					},
 				},
 				inData: true,
 			},
@@ -397,7 +407,9 @@
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
-					kind:   deviceSpecificModule,
+					earlyModuleContext: earlyModuleContext{
+						kind: deviceSpecificModule,
+					},
 				},
 				inData: true,
 			},
@@ -410,7 +422,9 @@
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
-					kind:   productSpecificModule,
+					earlyModuleContext: earlyModuleContext{
+						kind: productSpecificModule,
+					},
 				},
 				inData: true,
 			},
@@ -424,7 +438,9 @@
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
-					kind:   systemExtSpecificModule,
+					earlyModuleContext: earlyModuleContext{
+						kind: systemExtSpecificModule,
+					},
 				},
 				inData: true,
 			},
@@ -450,7 +466,9 @@
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
-					kind:   socSpecificModule,
+					earlyModuleContext: earlyModuleContext{
+						kind: socSpecificModule,
+					},
 				},
 				inSanitizerDir: true,
 			},
@@ -463,7 +481,9 @@
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
-					kind:   deviceSpecificModule,
+					earlyModuleContext: earlyModuleContext{
+						kind: deviceSpecificModule,
+					},
 				},
 				inSanitizerDir: true,
 			},
@@ -476,7 +496,9 @@
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
-					kind:   productSpecificModule,
+					earlyModuleContext: earlyModuleContext{
+						kind: productSpecificModule,
+					},
 				},
 				inSanitizerDir: true,
 			},
@@ -490,7 +512,9 @@
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
-					kind:   systemExtSpecificModule,
+					earlyModuleContext: earlyModuleContext{
+						kind: systemExtSpecificModule,
+					},
 				},
 				inSanitizerDir: true,
 			},
@@ -517,7 +541,9 @@
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
-					kind:   socSpecificModule,
+					earlyModuleContext: earlyModuleContext{
+						kind: socSpecificModule,
+					},
 				},
 				inData:         true,
 				inSanitizerDir: true,
@@ -531,7 +557,9 @@
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
-					kind:   deviceSpecificModule,
+					earlyModuleContext: earlyModuleContext{
+						kind: deviceSpecificModule,
+					},
 				},
 				inData:         true,
 				inSanitizerDir: true,
@@ -545,7 +573,9 @@
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
-					kind:   productSpecificModule,
+					earlyModuleContext: earlyModuleContext{
+						kind: productSpecificModule,
+					},
 				},
 				inData:         true,
 				inSanitizerDir: true,
@@ -559,7 +589,9 @@
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
-					kind:   systemExtSpecificModule,
+					earlyModuleContext: earlyModuleContext{
+						kind: systemExtSpecificModule,
+					},
 				},
 				inData:         true,
 				inSanitizerDir: true,
diff --git a/android/testing.go b/android/testing.go
index 4f0591b..6663728 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -37,8 +37,6 @@
 
 	ctx.SetNameInterface(nameResolver)
 
-	ctx.preArch = append(ctx.preArch, registerLoadHookMutator)
-
 	ctx.postDeps = append(ctx.postDeps, registerPathDepsMutator)
 
 	return ctx
@@ -54,6 +52,7 @@
 	*Context
 	preArch, preDeps, postDeps []RegisterMutatorFunc
 	NameResolver               *NameResolver
+	config                     Config
 }
 
 func (ctx *TestContext) PreArchMutators(f RegisterMutatorFunc) {
@@ -76,6 +75,20 @@
 	registerMutators(ctx.Context.Context, ctx.preArch, ctx.preDeps, ctx.postDeps)
 
 	ctx.RegisterSingletonType("env", EnvSingleton)
+
+	ctx.config = config
+}
+
+func (ctx *TestContext) ParseFileList(rootDir string, filePaths []string) (deps []string, errs []error) {
+	// This function adapts the old style ParseFileList calls that are spread throughout the tests
+	// to the new style that takes a config.
+	return ctx.Context.ParseFileList(rootDir, filePaths, ctx.config)
+}
+
+func (ctx *TestContext) ParseBlueprintsFiles(rootDir string) (deps []string, errs []error) {
+	// This function adapts the old style ParseBlueprintsFiles calls that are spread throughout the
+	// tests to the new style that takes a config.
+	return ctx.Context.ParseBlueprintsFiles(rootDir, ctx.config)
 }
 
 func (ctx *TestContext) RegisterModuleType(name string, factory ModuleFactory) {
diff --git a/apex/androidmk.go b/apex/androidmk.go
index c42b348..9aa0894 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -152,6 +152,27 @@
 	return moduleNames
 }
 
+func (a *apexBundle) writeRequiredModules(w io.Writer) {
+	var required []string
+	var targetRequired []string
+	var hostRequired []string
+	for _, fi := range a.filesInfo {
+		required = append(required, fi.requiredModuleNames...)
+		targetRequired = append(targetRequired, fi.targetRequiredModuleNames...)
+		hostRequired = append(hostRequired, fi.hostRequiredModuleNames...)
+	}
+
+	if len(required) > 0 {
+		fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES +=", strings.Join(required, " "))
+	}
+	if len(targetRequired) > 0 {
+		fmt.Fprintln(w, "LOCAL_TARGET_REQUIRED_MODULES +=", strings.Join(targetRequired, " "))
+	}
+	if len(hostRequired) > 0 {
+		fmt.Fprintln(w, "LOCAL_HOST_REQUIRED_MODULES +=", strings.Join(hostRequired, " "))
+	}
+}
+
 func (a *apexBundle) androidMkForType() android.AndroidMkData {
 	return android.AndroidMkData{
 		Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
@@ -170,6 +191,7 @@
 				if len(moduleNames) > 0 {
 					fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES :=", strings.Join(moduleNames, " "))
 				}
+				a.writeRequiredModules(w)
 				fmt.Fprintln(w, "include $(BUILD_PHONY_PACKAGE)")
 
 			} else {
@@ -188,6 +210,7 @@
 				if len(a.externalDeps) > 0 {
 					fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES +=", strings.Join(a.externalDeps, " "))
 				}
+				a.writeRequiredModules(w)
 				var postInstallCommands []string
 				if a.prebuiltFileToDelete != "" {
 					postInstallCommands = append(postInstallCommands, "rm -rf "+
diff --git a/apex/apex.go b/apex/apex.go
index 55f67e7..eb96142 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -461,6 +461,10 @@
 	symlinks      []string
 	transitiveDep bool
 	moduleDir     string
+
+	requiredModuleNames       []string
+	targetRequiredModuleNames []string
+	hostRequiredModuleNames   []string
 }
 
 func newApexFile(ctx android.BaseModuleContext, builtFile android.Path, moduleName string, installDir string, class apexFileClass, module android.Module) apexFile {
@@ -473,6 +477,9 @@
 	}
 	if module != nil {
 		ret.moduleDir = ctx.OtherModuleDir(module)
+		ret.requiredModuleNames = module.RequiredModuleNames()
+		ret.targetRequiredModuleNames = module.TargetRequiredModuleNames()
+		ret.hostRequiredModuleNames = module.HostRequiredModuleNames()
 	}
 	return ret
 }
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 0d929ed..cc346e9 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -3328,6 +3328,43 @@
 	`)
 }
 
+func TestCarryRequiredModuleNames(t *testing.T) {
+	ctx, config := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			native_shared_libs: ["mylib"],
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "mylib",
+			srcs: ["mylib.cpp"],
+			system_shared_libs: [],
+			stl: "none",
+			required: ["a", "b"],
+			host_required: ["c", "d"],
+			target_required: ["e", "f"],
+		}
+	`)
+
+	apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
+	data := android.AndroidMkDataForTest(t, config, "", apexBundle)
+	name := apexBundle.BaseModuleName()
+	prefix := "TARGET_"
+	var builder strings.Builder
+	data.Custom(&builder, name, prefix, "", data)
+	androidMk := builder.String()
+	ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES += a b\n")
+	ensureContains(t, androidMk, "LOCAL_HOST_REQUIRED_MODULES += c d\n")
+	ensureContains(t, androidMk, "LOCAL_TARGET_REQUIRED_MODULES += e f\n")
+}
+
 func TestMain(m *testing.M) {
 	run := func() int {
 		setUp()
diff --git a/apex/builder.go b/apex/builder.go
index fe465f5..4a760b9 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -105,6 +105,7 @@
 			`${apexer} --force --manifest ${manifest} ` +
 			`--file_contexts ${file_contexts} ` +
 			`--canned_fs_config ${canned_fs_config} ` +
+			`--include_build_info ` +
 			`--payload_type image ` +
 			`--key ${key} ${opt_flags} ${image_dir} ${out} `,
 		CommandDeps: []string{"${apexer}", "${avbtool}", "${e2fsdroid}", "${merge_zips}",
@@ -378,7 +379,7 @@
 			optFlags = append(optFlags, "--assets_dir "+filepath.Dir(noticeFile.String()))
 		}
 
-		if ctx.ModuleDir() != "system/apex/apexd/apexd_testdata" && a.testOnlyShouldSkipHashtreeGeneration() {
+		if ctx.ModuleDir() != "system/apex/apexd/apexd_testdata" && ctx.ModuleDir() != "system/apex/shim/build" && a.testOnlyShouldSkipHashtreeGeneration() {
 			ctx.PropertyErrorf("test_only_no_hashtree", "not available")
 			return
 		}
diff --git a/cc/androidmk.go b/cc/androidmk.go
index 988ebd4..ff88091 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -284,6 +284,9 @@
 			fmt.Fprintln(w, "LOCAL_FULL_TEST_CONFIG :=", benchmark.testConfig.String())
 		}
 		fmt.Fprintln(w, "LOCAL_NATIVE_BENCHMARK := true")
+		if !BoolDefault(benchmark.Properties.Auto_gen_config, true) {
+			fmt.Fprintln(w, "LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG := true")
+		}
 	})
 
 	androidMkWriteTestData(benchmark.data, ctx, ret)
@@ -304,6 +307,9 @@
 		if test.testConfig != nil {
 			fmt.Fprintln(w, "LOCAL_FULL_TEST_CONFIG :=", test.testConfig.String())
 		}
+		if !BoolDefault(test.Properties.Auto_gen_config, true) {
+			fmt.Fprintln(w, "LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG := true")
+		}
 	})
 
 	androidMkWriteTestData(test.data, ctx, ret)
diff --git a/cc/config/vndk.go b/cc/config/vndk.go
index c3cda49..9feb5a3 100644
--- a/cc/config/vndk.go
+++ b/cc/config/vndk.go
@@ -84,6 +84,7 @@
 	"android.hardware.thermal@1.0",
 	"android.hardware.tv.cec@1.0",
 	"android.hardware.tv.input@1.0",
+	"android.hardware.vibrator-ndk_platform",
 	"android.hardware.vibrator@1.0",
 	"android.hardware.vibrator@1.1",
 	"android.hardware.vibrator@1.2",
@@ -165,5 +166,4 @@
 	"libxml2",
 	"libyuv",
 	"libziparchive",
-	"vintf-vibrator-ndk_platform",
 }
diff --git a/cc/pgo.go b/cc/pgo.go
index 4618f4e..0072355 100644
--- a/cc/pgo.go
+++ b/cc/pgo.go
@@ -210,11 +210,6 @@
 		ctx.ModuleErrorf("PGO specification is missing properties: " + missingProps)
 	}
 
-	// Sampling not supported yet
-	if isSampling {
-		ctx.PropertyErrorf("pgo.sampling", "\"sampling\" is not supported yet)")
-	}
-
 	if isSampling && isInstrumentation {
 		ctx.PropertyErrorf("pgo", "Exactly one of \"instrumentation\" and \"sampling\" properties must be set")
 	}
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
index 83e3673..0c79ccc 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -22,7 +22,9 @@
 	"android/soong/android"
 )
 
-// GlobalConfig stores the configuration for dex preopting set by the product
+// GlobalConfig stores the configuration for dex preopting. The fields are set
+// from product variables via dex_preopt_config.mk, except for SoongConfig
+// which come from CreateGlobalSoongConfig.
 type GlobalConfig struct {
 	DisablePreopt        bool     // disable preopt for all modules
 	DisablePreoptModules []string // modules with preopt disabled by product-specific config
@@ -82,19 +84,19 @@
 	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
+	SoongConfig GlobalSoongConfig // settings read from dexpreopt_soong.config
 }
 
-// Tools contains paths to tools possibly used by the generated commands.  If you add a new tool here you MUST add it
-// to the order-only dependency list in DEXPREOPT_GEN_DEPS.
-type Tools struct {
-	Profman       android.Path
-	Dex2oat       android.Path
-	Aapt          android.Path
-	SoongZip      android.Path
-	Zip2zip       android.Path
-	ManifestCheck android.Path
-
+// GlobalSoongConfig contains the global config that is generated from Soong,
+// stored in dexpreopt_soong.config.
+type GlobalSoongConfig struct {
+	// Paths to tools possibly used by the generated commands.
+	Profman          android.Path
+	Dex2oat          android.Path
+	Aapt             android.Path
+	SoongZip         android.Path
+	Zip2zip          android.Path
+	ManifestCheck    android.Path
 	ConstructContext android.Path
 }
 
@@ -133,6 +135,17 @@
 	PresignedPrebuilt bool
 }
 
+type globalSoongConfigSingleton struct{}
+
+var pctx = android.NewPackageContext("android/soong/dexpreopt")
+
+func init() {
+	pctx.Import("android/soong/android")
+	android.RegisterSingletonType("dexpreopt-soong-config", func() android.Singleton {
+		return &globalSoongConfigSingleton{}
+	})
+}
+
 func constructPath(ctx android.PathContext, path string) android.Path {
 	buildDirPrefix := ctx.Config().BuildDir() + "/"
 	if path == "" {
@@ -167,9 +180,12 @@
 	return constructPath(ctx, path).(android.WritablePath)
 }
 
-// LoadGlobalConfig reads the global dexpreopt.config file into a GlobalConfig struct.  It is used directly in Soong
-// and in dexpreopt_gen called from Make to read the $OUT/dexpreopt.config written by Make.
-func LoadGlobalConfig(ctx android.PathContext, path string) (GlobalConfig, []byte, error) {
+// LoadGlobalConfig reads the global dexpreopt.config file into a GlobalConfig
+// struct, except the SoongConfig field which is set from the provided
+// soongConfig argument. LoadGlobalConfig is used directly in Soong and in
+// dexpreopt_gen called from Make to read the $OUT/dexpreopt.config written by
+// Make.
+func LoadGlobalConfig(ctx android.PathContext, path string, soongConfig GlobalSoongConfig) (GlobalConfig, []byte, error) {
 	type GlobalJSONConfig struct {
 		GlobalConfig
 
@@ -177,17 +193,6 @@
 		// used to construct the real value manually below.
 		DirtyImageObjects string
 		BootImageProfiles []string
-
-		Tools struct {
-			Profman       string
-			Dex2oat       string
-			Aapt          string
-			SoongZip      string
-			Zip2zip       string
-			ManifestCheck string
-
-			ConstructContext string
-		}
 	}
 
 	config := GlobalJSONConfig{}
@@ -200,13 +205,9 @@
 	config.GlobalConfig.DirtyImageObjects = android.OptionalPathForPath(constructPath(ctx, config.DirtyImageObjects))
 	config.GlobalConfig.BootImageProfiles = constructPaths(ctx, config.BootImageProfiles)
 
-	config.GlobalConfig.Tools.Profman = constructPath(ctx, config.Tools.Profman)
-	config.GlobalConfig.Tools.Dex2oat = constructPath(ctx, config.Tools.Dex2oat)
-	config.GlobalConfig.Tools.Aapt = constructPath(ctx, config.Tools.Aapt)
-	config.GlobalConfig.Tools.SoongZip = constructPath(ctx, config.Tools.SoongZip)
-	config.GlobalConfig.Tools.Zip2zip = constructPath(ctx, config.Tools.Zip2zip)
-	config.GlobalConfig.Tools.ManifestCheck = constructPath(ctx, config.Tools.ManifestCheck)
-	config.GlobalConfig.Tools.ConstructContext = constructPath(ctx, config.Tools.ConstructContext)
+	// Set this here to force the caller to provide a value for this struct (from
+	// either CreateGlobalSoongConfig or LoadGlobalSoongConfig).
+	config.GlobalConfig.SoongConfig = soongConfig
 
 	return config.GlobalConfig, data, nil
 }
@@ -253,6 +254,104 @@
 	return config.ModuleConfig, nil
 }
 
+// CreateGlobalSoongConfig creates a GlobalSoongConfig from the current context.
+// Should not be used in dexpreopt_gen.
+func CreateGlobalSoongConfig(ctx android.PathContext) GlobalSoongConfig {
+	// Default to debug version to help find bugs.
+	// Set USE_DEX2OAT_DEBUG to false for only building non-debug versions.
+	var dex2oatBinary string
+	if ctx.Config().Getenv("USE_DEX2OAT_DEBUG") == "false" {
+		dex2oatBinary = "dex2oat"
+	} else {
+		dex2oatBinary = "dex2oatd"
+	}
+
+	return GlobalSoongConfig{
+		Profman:          ctx.Config().HostToolPath(ctx, "profman"),
+		Dex2oat:          ctx.Config().HostToolPath(ctx, dex2oatBinary),
+		Aapt:             ctx.Config().HostToolPath(ctx, "aapt"),
+		SoongZip:         ctx.Config().HostToolPath(ctx, "soong_zip"),
+		Zip2zip:          ctx.Config().HostToolPath(ctx, "zip2zip"),
+		ManifestCheck:    ctx.Config().HostToolPath(ctx, "manifest_check"),
+		ConstructContext: android.PathForSource(ctx, "build/make/core/construct_context.sh"),
+	}
+}
+
+type globalJsonSoongConfig struct {
+	Profman          string
+	Dex2oat          string
+	Aapt             string
+	SoongZip         string
+	Zip2zip          string
+	ManifestCheck    string
+	ConstructContext string
+}
+
+// LoadGlobalSoongConfig reads the dexpreopt_soong.config file into a
+// GlobalSoongConfig struct. It is only used in dexpreopt_gen.
+func LoadGlobalSoongConfig(ctx android.PathContext, path string) (GlobalSoongConfig, error) {
+	var jc globalJsonSoongConfig
+
+	_, err := loadConfig(ctx, path, &jc)
+	if err != nil {
+		return GlobalSoongConfig{}, err
+	}
+
+	config := GlobalSoongConfig{
+		Profman:          constructPath(ctx, jc.Profman),
+		Dex2oat:          constructPath(ctx, jc.Dex2oat),
+		Aapt:             constructPath(ctx, jc.Aapt),
+		SoongZip:         constructPath(ctx, jc.SoongZip),
+		Zip2zip:          constructPath(ctx, jc.Zip2zip),
+		ManifestCheck:    constructPath(ctx, jc.ManifestCheck),
+		ConstructContext: constructPath(ctx, jc.ConstructContext),
+	}
+
+	return config, nil
+}
+
+func (s *globalSoongConfigSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+	config := CreateGlobalSoongConfig(ctx)
+	jc := globalJsonSoongConfig{
+		Profman:          config.Profman.String(),
+		Dex2oat:          config.Dex2oat.String(),
+		Aapt:             config.Aapt.String(),
+		SoongZip:         config.SoongZip.String(),
+		Zip2zip:          config.Zip2zip.String(),
+		ManifestCheck:    config.ManifestCheck.String(),
+		ConstructContext: config.ConstructContext.String(),
+	}
+
+	data, err := json.Marshal(jc)
+	if err != nil {
+		ctx.Errorf("failed to JSON marshal GlobalSoongConfig: %v", err)
+		return
+	}
+
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   android.WriteFile,
+		Output: android.PathForOutput(ctx, "dexpreopt_soong.config"),
+		Args: map[string]string{
+			"content": string(data),
+		},
+	})
+}
+
+func (s *globalSoongConfigSingleton) MakeVars(ctx android.MakeVarsContext) {
+	config := CreateGlobalSoongConfig(ctx)
+
+	ctx.Strict("DEX2OAT", config.Dex2oat.String())
+	ctx.Strict("DEXPREOPT_GEN_DEPS", strings.Join([]string{
+		config.Profman.String(),
+		config.Dex2oat.String(),
+		config.Aapt.String(),
+		config.SoongZip.String(),
+		config.Zip2zip.String(),
+		config.ManifestCheck.String(),
+		config.ConstructContext.String(),
+	}, " "))
+}
+
 func loadConfig(ctx android.PathContext, path string, config interface{}) ([]byte, error) {
 	r, err := ctx.Fs().Open(path)
 	if err != nil {
@@ -312,7 +411,7 @@
 		BootFlags:                          "",
 		Dex2oatImageXmx:                    "",
 		Dex2oatImageXms:                    "",
-		Tools: Tools{
+		SoongConfig: GlobalSoongConfig{
 			Profman:          android.PathForTesting("profman"),
 			Dex2oat:          android.PathForTesting("dex2oat"),
 			Aapt:             android.PathForTesting("aapt"),
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index fc1bae1..ac5b691 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -131,7 +131,7 @@
 
 	cmd := rule.Command().
 		Text(`ANDROID_LOG_TAGS="*:e"`).
-		Tool(global.Tools.Profman)
+		Tool(global.SoongConfig.Profman)
 
 	if module.ProfileIsTextListing {
 		// The profile is a test listing of classes (used for framework jars).
@@ -170,7 +170,7 @@
 
 	cmd := rule.Command().
 		Text(`ANDROID_LOG_TAGS="*:e"`).
-		Tool(global.Tools.Profman)
+		Tool(global.SoongConfig.Profman)
 
 	// The profile is a test listing of methods.
 	// We need to generate the actual binary profile.
@@ -222,14 +222,6 @@
 
 	invocationPath := odexPath.ReplaceExtension(ctx, "invocation")
 
-	// TODO(skvadrik): fix this to use boot image location in the module config (currently it is broken
-	// in JIT-zygote builds, because "default" boot image is hard-coded in parts of the module config).
-	bootImage := module.DexPreoptImages[archIdx]
-	var bootImageLocation string
-	if bootImage != nil {
-		bootImageLocation = PathToLocation(bootImage, arch)
-	}
-
 	// The class loader context using paths in the build
 	var classLoaderContextHost android.Paths
 
@@ -307,14 +299,14 @@
 	if module.EnforceUsesLibraries {
 		if module.ManifestPath != nil {
 			rule.Command().Text(`target_sdk_version="$(`).
-				Tool(global.Tools.ManifestCheck).
+				Tool(global.SoongConfig.ManifestCheck).
 				Flag("--extract-target-sdk-version").
 				Input(module.ManifestPath).
 				Text(`)"`)
 		} else {
 			// No manifest to extract targetSdkVersion from, hope that DexJar is an APK
 			rule.Command().Text(`target_sdk_version="$(`).
-				Tool(global.Tools.Aapt).
+				Tool(global.SoongConfig.Aapt).
 				Flag("dump badging").
 				Input(module.DexPath).
 				Text(`| grep "targetSdkVersion" | sed -n "s/targetSdkVersion:'\(.*\)'/\1/p"`).
@@ -335,7 +327,7 @@
 			Implicits(conditionalClassLoaderContextHost29)
 		rule.Command().Textf(`conditional_target_libs_29="%s"`,
 			strings.Join(conditionalClassLoaderContextTarget29, " "))
-		rule.Command().Text("source").Tool(global.Tools.ConstructContext).Input(module.DexPath)
+		rule.Command().Text("source").Tool(global.SoongConfig.ConstructContext).Input(module.DexPath)
 	}
 
 	// Devices that do not have a product partition use a symlink from /product to /system/product.
@@ -348,7 +340,7 @@
 
 	cmd := rule.Command().
 		Text(`ANDROID_LOG_TAGS="*:e"`).
-		Tool(global.Tools.Dex2oat).
+		Tool(global.SoongConfig.Dex2oat).
 		Flag("--avoid-storing-invocation").
 		FlagWithOutput("--write-invocation-to=", invocationPath).ImplicitOutput(invocationPath).
 		Flag("--runtime-arg").FlagWithArg("-Xms", global.Dex2oatXms).
@@ -357,7 +349,7 @@
 		Flag("--runtime-arg").FlagWithList("-Xbootclasspath-locations:", module.PreoptBootClassPathDexLocations, ":").
 		Flag("${class_loader_context_arg}").
 		Flag("${stored_class_loader_context_arg}").
-		FlagWithArg("--boot-image=", bootImageLocation).Implicits(module.DexPreoptImagesDeps[archIdx].Paths()).
+		FlagWithArg("--boot-image=", strings.Join(module.DexPreoptImageLocations, ":")).Implicits(module.DexPreoptImagesDeps[archIdx].Paths()).
 		FlagWithInput("--dex-file=", module.DexPath).
 		FlagWithArg("--dex-location=", dexLocationArg).
 		FlagWithOutput("--oat-file=", odexPath).ImplicitOutput(vdexPath).
@@ -417,7 +409,7 @@
 		dmInstalledPath := pathtools.ReplaceExtension(module.DexLocation, "dm")
 		tmpPath := module.BuildPath.InSameDir(ctx, "primary.vdex")
 		rule.Command().Text("cp -f").Input(vdexPath).Output(tmpPath)
-		rule.Command().Tool(global.Tools.SoongZip).
+		rule.Command().Tool(global.SoongConfig.SoongZip).
 			FlagWithArg("-L", "9").
 			FlagWithOutput("-o", dmPath).
 			Flag("-j").
diff --git a/dexpreopt/dexpreopt_gen/dexpreopt_gen.go b/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
index 6f51080..d2faa00 100644
--- a/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
+++ b/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
@@ -30,10 +30,11 @@
 )
 
 var (
-	dexpreoptScriptPath = flag.String("dexpreopt_script", "", "path to output dexpreopt script")
-	globalConfigPath    = flag.String("global", "", "path to global configuration file")
-	moduleConfigPath    = flag.String("module", "", "path to module configuration file")
-	outDir              = flag.String("out_dir", "", "path to output directory")
+	dexpreoptScriptPath   = flag.String("dexpreopt_script", "", "path to output dexpreopt script")
+	globalSoongConfigPath = flag.String("global_soong", "", "path to global configuration file for settings originating from Soong")
+	globalConfigPath      = flag.String("global", "", "path to global configuration file")
+	moduleConfigPath      = flag.String("module", "", "path to module configuration file")
+	outDir                = flag.String("out_dir", "", "path to output directory")
 )
 
 type pathContext struct {
@@ -63,17 +64,27 @@
 		usage("path to output dexpreopt script is required")
 	}
 
+	if *globalSoongConfigPath == "" {
+		usage("--global_soong configuration file is required")
+	}
+
 	if *globalConfigPath == "" {
-		usage("path to global configuration file is required")
+		usage("--global configuration file is required")
 	}
 
 	if *moduleConfigPath == "" {
-		usage("path to module configuration file is required")
+		usage("--module configuration file is required")
 	}
 
 	ctx := &pathContext{android.TestConfig(*outDir, nil, "", nil)}
 
-	globalConfig, _, err := dexpreopt.LoadGlobalConfig(ctx, *globalConfigPath)
+	globalSoongConfig, err := dexpreopt.LoadGlobalSoongConfig(ctx, *globalSoongConfigPath)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "error loading global config %q: %s\n", *globalSoongConfigPath, err)
+		os.Exit(2)
+	}
+
+	globalConfig, _, err := dexpreopt.LoadGlobalConfig(ctx, *globalConfigPath, globalSoongConfig)
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "error loading global config %q: %s\n", *globalConfigPath, err)
 		os.Exit(2)
@@ -121,7 +132,7 @@
 		dexpreoptRule.Command().Text("mkdir -p").Flag(filepath.Dir(installPath.String()))
 		dexpreoptRule.Command().Text("cp -f").Input(install.From).Output(installPath)
 	}
-	dexpreoptRule.Command().Tool(global.Tools.SoongZip).
+	dexpreoptRule.Command().Tool(global.SoongConfig.SoongZip).
 		FlagWithArg("-o ", "$2").
 		FlagWithArg("-C ", installDir.String()).
 		FlagWithArg("-D ", installDir.String())
diff --git a/java/androidmk.go b/java/androidmk.go
index 11fea82..04bf15c 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -141,6 +141,9 @@
 			entries.SetPath("LOCAL_FULL_TEST_CONFIG", j.testConfig)
 		}
 		androidMkWriteTestData(j.data, entries)
+		if !BoolDefault(j.testProperties.Auto_gen_config, true) {
+			entries.SetString("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", "true")
+		}
 	})
 
 	return entriesList
@@ -613,7 +616,7 @@
 					fmt.Fprintln(w, dstubs.Name()+"-check-last-released-api:",
 						dstubs.checkLastReleasedApiTimestamp.String())
 
-					if dstubs.Name() == "api-stubs-docs" || dstubs.Name() == "system-api-stubs-docs" {
+					if dstubs.Name() != "android.car-system-stubs-docs" {
 						fmt.Fprintln(w, ".PHONY: checkapi")
 						fmt.Fprintln(w, "checkapi:",
 							dstubs.checkLastReleasedApiTimestamp.String())
diff --git a/java/app.go b/java/app.go
index 94f6bb1..2933ccb 100755
--- a/java/app.go
+++ b/java/app.go
@@ -897,7 +897,7 @@
 	MergePropertiesFromVariant(ctx, &a.properties, archProps, archType.Name)
 }
 
-func MergePropertiesFromVariant(ctx android.BaseModuleContext,
+func MergePropertiesFromVariant(ctx android.EarlyModuleContext,
 	dst interface{}, variantGroup reflect.Value, variant string) {
 	src := variantGroup.FieldByName(proptools.FieldNameForProperty(variant))
 	if !src.IsValid() {
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index 88e3bc2..fe5bed5 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -327,7 +327,7 @@
 
 	invocationPath := outputPath.ReplaceExtension(ctx, "invocation")
 
-	cmd.Tool(global.Tools.Dex2oat).
+	cmd.Tool(global.SoongConfig.Dex2oat).
 		Flag("--avoid-storing-invocation").
 		FlagWithOutput("--write-invocation-to=", invocationPath).ImplicitOutput(invocationPath).
 		Flag("--runtime-arg").FlagWithArg("-Xms", global.Dex2oatImageXms).
@@ -436,7 +436,6 @@
 		return nil
 	}
 	profile := ctx.Config().Once(bootImageProfileRuleKey, func() interface{} {
-		tools := global.Tools
 		defaultProfile := "frameworks/base/config/boot-image-profile.txt"
 
 		rule := android.NewRuleBuilder()
@@ -462,7 +461,7 @@
 
 		rule.Command().
 			Text(`ANDROID_LOG_TAGS="*:e"`).
-			Tool(tools.Profman).
+			Tool(global.SoongConfig.Profman).
 			FlagWithInput("--create-profile-from=", bootImageProfile).
 			FlagForEachInput("--apk=", image.dexPathsDeps.Paths()).
 			FlagForEachArg("--dex-location=", image.dexLocationsDeps).
@@ -491,8 +490,6 @@
 		return nil
 	}
 	return ctx.Config().Once(bootFrameworkProfileRuleKey, func() interface{} {
-		tools := global.Tools
-
 		rule := android.NewRuleBuilder()
 		rule.MissingDeps(missingDeps)
 
@@ -513,7 +510,7 @@
 
 		rule.Command().
 			Text(`ANDROID_LOG_TAGS="*:e"`).
-			Tool(tools.Profman).
+			Tool(global.SoongConfig.Profman).
 			Flag("--generate-boot-profile").
 			FlagWithInput("--create-profile-from=", bootFrameworkProfile).
 			FlagForEachInput("--apk=", image.dexPathsDeps.Paths()).
@@ -590,6 +587,7 @@
 func (d *dexpreoptBootJars) MakeVars(ctx android.MakeVarsContext) {
 	if d.dexpreoptConfigForMake != nil {
 		ctx.Strict("DEX_PREOPT_CONFIG_FOR_MAKE", d.dexpreoptConfigForMake.String())
+		ctx.Strict("DEX_PREOPT_SOONG_CONFIG_FOR_MAKE", android.PathForOutput(ctx, "dexpreopt_soong.config").String())
 	}
 
 	image := d.defaultBootImage
@@ -597,7 +595,6 @@
 		ctx.Strict("DEXPREOPT_IMAGE_PROFILE_BUILT_INSTALLED", image.profileInstalls.String())
 		ctx.Strict("DEXPREOPT_BOOTCLASSPATH_DEX_FILES", strings.Join(image.dexPathsDeps.Strings(), " "))
 		ctx.Strict("DEXPREOPT_BOOTCLASSPATH_DEX_LOCATIONS", strings.Join(image.dexLocationsDeps, " "))
-		ctx.Strict("DEXPREOPT_IMAGE_LOCATIONS", strings.Join(image.imageLocations, ":"))
 
 		var imageNames []string
 		for _, current := range append(d.otherImages, image) {
@@ -610,15 +607,15 @@
 			sort.Slice(arches, func(i, j int) bool { return arches[i].String() < arches[j].String() })
 
 			for _, arch := range arches {
-				ctx.Strict("DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_"+current.name+"_"+arch.String(), current.vdexInstalls[arch].String())
-				ctx.Strict("DEXPREOPT_IMAGE_"+current.name+"_"+arch.String(), current.images[arch].String())
-				ctx.Strict("DEXPREOPT_IMAGE_DEPS_"+current.name+"_"+arch.String(), strings.Join(current.imagesDeps[arch].Strings(), " "))
-				ctx.Strict("DEXPREOPT_IMAGE_BUILT_INSTALLED_"+current.name+"_"+arch.String(), current.installs[arch].String())
-				ctx.Strict("DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_"+current.name+"_"+arch.String(), current.unstrippedInstalls[arch].String())
-				if current.zip != nil {
-				}
+				sfx := current.name + "_" + arch.String()
+				ctx.Strict("DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_"+sfx, current.vdexInstalls[arch].String())
+				ctx.Strict("DEXPREOPT_IMAGE_"+sfx, current.images[arch].String())
+				ctx.Strict("DEXPREOPT_IMAGE_DEPS_"+sfx, strings.Join(current.imagesDeps[arch].Strings(), " "))
+				ctx.Strict("DEXPREOPT_IMAGE_BUILT_INSTALLED_"+sfx, current.installs[arch].String())
+				ctx.Strict("DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_"+sfx, current.unstrippedInstalls[arch].String())
 			}
 
+			ctx.Strict("DEXPREOPT_IMAGE_LOCATIONS_"+current.name, strings.Join(current.imageLocations, ":"))
 			ctx.Strict("DEXPREOPT_IMAGE_ZIP_"+current.name, current.zip.String())
 		}
 		ctx.Strict("DEXPREOPT_IMAGE_NAMES", strings.Join(imageNames, " "))
diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go
index 76bda61..35748b8 100644
--- a/java/dexpreopt_config.go
+++ b/java/dexpreopt_config.go
@@ -37,8 +37,9 @@
 func dexpreoptGlobalConfigRaw(ctx android.PathContext) globalConfigAndRaw {
 	return ctx.Config().Once(dexpreoptGlobalConfigKey, func() interface{} {
 		if f := ctx.Config().DexpreoptGlobalConfig(); f != "" {
+			soongConfig := dexpreopt.CreateGlobalSoongConfig(ctx)
 			ctx.AddNinjaFileDeps(f)
-			globalConfig, data, err := dexpreopt.LoadGlobalConfig(ctx, f)
+			globalConfig, data, err := dexpreopt.LoadGlobalConfig(ctx, f, soongConfig)
 			if err != nil {
 				panic(err)
 			}
diff --git a/java/platform_compat_config.go b/java/platform_compat_config.go
index fefd0e6..715485f 100644
--- a/java/platform_compat_config.go
+++ b/java/platform_compat_config.go
@@ -19,9 +19,14 @@
 )
 
 func init() {
+	android.RegisterSingletonType("platform_compat_config_singleton", platformCompatConfigSingletonFactory)
 	android.RegisterModuleType("platform_compat_config", platformCompatConfigFactory)
 }
 
+type platformCompatConfigSingleton struct {
+	metadata android.Path
+}
+
 type platformCompatConfigProperties struct {
 	Src *string `android:"path"`
 }
@@ -35,6 +40,46 @@
 	metadataFile   android.OutputPath
 }
 
+func (p *platformCompatConfig) compatConfigMetadata() android.OutputPath {
+	return p.metadataFile
+}
+
+type platformCompatConfigIntf interface {
+	compatConfigMetadata() android.OutputPath
+}
+
+var _ platformCompatConfigIntf = (*platformCompatConfig)(nil)
+
+// compat singleton rules
+func (p *platformCompatConfigSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+
+	var compatConfigMetadata android.Paths
+
+	ctx.VisitAllModules(func(module android.Module) {
+		if c, ok := module.(platformCompatConfigIntf); ok {
+			metadata := c.compatConfigMetadata()
+			compatConfigMetadata = append(compatConfigMetadata, metadata)
+		}
+	})
+
+	if compatConfigMetadata == nil {
+		// nothing to do.
+		return
+	}
+
+	rule := android.NewRuleBuilder()
+	outputPath := android.PathForOutput(ctx, "compat_config", "merged_compat_config.xml")
+
+	rule.Command().
+		BuiltTool(ctx, "process-compat-config").
+		FlagForEachInput("--xml ", compatConfigMetadata).
+		FlagWithOutput("--merged-config ", outputPath)
+
+	rule.Build(pctx, ctx, "merged-compat-config", "Merge compat config")
+
+	p.metadata = outputPath
+}
+
 func (p *platformCompatConfig) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	rule := android.NewRuleBuilder()
 
@@ -69,6 +114,10 @@
 	}}
 }
 
+func platformCompatConfigSingletonFactory() android.Singleton {
+	return &platformCompatConfigSingleton{}
+}
+
 func platformCompatConfigFactory() android.Module {
 	module := &platformCompatConfig{}
 	module.AddProperties(&module.properties)
diff --git a/java/sdk.go b/java/sdk.go
index 6f0f432..66eb284 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -58,7 +58,7 @@
 
 // Returns a sdk version as a number.  For modules targeting an unreleased SDK (meaning it does not yet have a number)
 // it returns android.FutureApiLevel (10000).
-func sdkVersionToNumber(ctx android.BaseModuleContext, v string) (int, error) {
+func sdkVersionToNumber(ctx android.EarlyModuleContext, v string) (int, error) {
 	switch v {
 	case "", "none", "current", "test_current", "system_current", "core_current", "core_platform":
 		return ctx.Config().DefaultAppTargetSdkInt(), nil
@@ -72,7 +72,7 @@
 	}
 }
 
-func sdkVersionToNumberAsString(ctx android.BaseModuleContext, v string) (string, error) {
+func sdkVersionToNumberAsString(ctx android.EarlyModuleContext, v string) (string, error) {
 	n, err := sdkVersionToNumber(ctx, v)
 	if err != nil {
 		return "", err
@@ -80,7 +80,7 @@
 	return strconv.Itoa(n), nil
 }
 
-func decodeSdkDep(ctx android.BaseModuleContext, sdkContext sdkContext) sdkDep {
+func decodeSdkDep(ctx android.EarlyModuleContext, sdkContext sdkContext) sdkDep {
 	v := sdkContext.sdkVersion()
 
 	// For PDK builds, use the latest SDK version instead of "current"
@@ -127,12 +127,12 @@
 		}
 
 		if !jarPath.Valid() {
-			ctx.PropertyErrorf("sdk_version", "invalid sdk version %q, %q does not exist", v, jar)
+			ctx.PropertyErrorf("sdk_version", "invalid sdk version %q, %q does not exist", sdk, jar)
 			return sdkDep{}
 		}
 
 		if !aidlPath.Valid() {
-			ctx.PropertyErrorf("sdk_version", "invalid sdk version %q, %q does not exist", v, aidl)
+			ctx.PropertyErrorf("sdk_version", "invalid sdk version %q, %q does not exist", sdk, aidl)
 			return sdkDep{}
 		}
 
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 2cb8f72..9b30e2c 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -97,12 +97,22 @@
 	// List of Java libraries that will be in the classpath when building stubs
 	Stub_only_libs []string `android:"arch_variant"`
 
-	// list of package names that will be documented and publicized as API
+	// list of package names that will be documented and publicized as API.
+	// This allows the API to be restricted to a subset of the source files provided.
+	// If this is unspecified then all the source files will be treated as being part
+	// of the API.
 	Api_packages []string
 
 	// list of package names that must be hidden from the API
 	Hidden_api_packages []string
 
+	// the relative path to the directory containing the api specification files.
+	// Defaults to "api".
+	Api_dir *string
+
+	// If set to true there is no runtime library.
+	Api_only *bool
+
 	// local files that are used within user customized droiddoc options.
 	Droiddoc_option_files []string
 
@@ -176,7 +186,10 @@
 }
 
 func (module *SdkLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	module.Library.GenerateAndroidBuildActions(ctx)
+	// Don't build an implementation library if this is api only.
+	if !proptools.Bool(module.sdkLibraryProperties.Api_only) {
+		module.Library.GenerateAndroidBuildActions(ctx)
+	}
 
 	module.buildPermissionsFile(ctx)
 
@@ -240,6 +253,9 @@
 }
 
 func (module *SdkLibrary) AndroidMkEntries() []android.AndroidMkEntries {
+	if proptools.Bool(module.sdkLibraryProperties.Api_only) {
+		return nil
+	}
 	entriesList := module.Library.AndroidMkEntries()
 	entries := &entriesList[0]
 	entries.Required = append(entries.Required, module.xmlFileName())
@@ -358,7 +374,7 @@
 // SDK version that the stubs library is built against. Note that this is always
 // *current. Older stubs library built with a numberd SDK version is created from
 // the prebuilt jar.
-func (module *SdkLibrary) sdkVersion(apiScope apiScope) string {
+func (module *SdkLibrary) sdkVersionForScope(apiScope apiScope) string {
 	switch apiScope {
 	case apiScopePublic:
 		return "current"
@@ -371,6 +387,18 @@
 	}
 }
 
+// Get the sdk version for use when compiling the stubs library.
+func (module *SdkLibrary) sdkVersionForStubsLibrary(mctx android.LoadHookContext, apiScope apiScope) string {
+	sdkDep := decodeSdkDep(mctx, sdkContext(&module.Library))
+	if sdkDep.hasStandardLibs() {
+		// If building against a standard sdk then use the sdk version appropriate for the scope.
+		return module.sdkVersionForScope(apiScope)
+	} else {
+		// Otherwise, use no system module.
+		return "none"
+	}
+}
+
 // $(INTERNAL_PLATFORM_<apiTagName>_API_FILE) points to the generated
 // api file for the current source
 // TODO: remove this when apicheck is done in soong
@@ -418,14 +446,15 @@
 	props := struct {
 		Name                *string
 		Srcs                []string
+		Installable         *bool
 		Sdk_version         *string
+		System_modules      *string
 		Libs                []string
 		Soc_specific        *bool
 		Device_specific     *bool
 		Product_specific    *bool
 		System_ext_specific *bool
 		Compile_dex         *bool
-		System_modules      *string
 		Java_version        *string
 		Product_variables   struct {
 			Unbundled_build struct {
@@ -441,23 +470,19 @@
 		}
 	}{}
 
-	sdkVersion := module.sdkVersion(apiScope)
-	sdkDep := decodeSdkDep(mctx, sdkContext(&module.Library))
-	if !sdkDep.hasStandardLibs() {
-		sdkVersion = "none"
-	}
-
 	props.Name = proptools.StringPtr(module.stubsName(apiScope))
 	// sources are generated from the droiddoc
 	props.Srcs = []string{":" + module.docsName(apiScope)}
+	sdkVersion := module.sdkVersionForStubsLibrary(mctx, apiScope)
 	props.Sdk_version = proptools.StringPtr(sdkVersion)
+	props.System_modules = module.Library.Module.deviceProperties.System_modules
+	props.Installable = proptools.BoolPtr(false)
 	props.Libs = module.sdkLibraryProperties.Stub_only_libs
 	// Unbundled apps will use the prebult one from /prebuilts/sdk
 	if mctx.Config().UnbundledBuildUsePrebuiltSdks() {
 		props.Product_variables.Unbundled_build.Enabled = proptools.BoolPtr(false)
 	}
 	props.Product_variables.Pdk.Enabled = proptools.BoolPtr(false)
-	props.System_modules = module.Library.Module.deviceProperties.System_modules
 	props.Openjdk9.Srcs = module.Library.Module.properties.Openjdk9.Srcs
 	props.Openjdk9.Javacflags = module.Library.Module.properties.Openjdk9.Javacflags
 	props.Java_version = module.Library.Module.properties.Java_version
@@ -486,6 +511,7 @@
 		Srcs                             []string
 		Installable                      *bool
 		Sdk_version                      *string
+		System_modules                   *string
 		Libs                             []string
 		Arg_files                        []string
 		Args                             *string
@@ -507,6 +533,8 @@
 	}{}
 
 	sdkDep := decodeSdkDep(mctx, sdkContext(&module.Library))
+	// Use the platform API if standard libraries were requested, otherwise use
+	// no default libraries.
 	sdkVersion := ""
 	if !sdkDep.hasStandardLibs() {
 		sdkVersion = "none"
@@ -515,6 +543,7 @@
 	props.Name = proptools.StringPtr(module.docsName(apiScope))
 	props.Srcs = append(props.Srcs, module.Library.Module.properties.Srcs...)
 	props.Sdk_version = proptools.StringPtr(sdkVersion)
+	props.System_modules = module.Library.Module.deviceProperties.System_modules
 	props.Installable = proptools.BoolPtr(false)
 	// A droiddoc module has only one Libs property and doesn't distinguish between
 	// shared libs and static libs. So we need to add both of these libs to Libs property.
@@ -571,8 +600,9 @@
 		currentApiFileName = "test-" + currentApiFileName
 		removedApiFileName = "test-" + removedApiFileName
 	}
-	currentApiFileName = path.Join("api", currentApiFileName)
-	removedApiFileName = path.Join("api", removedApiFileName)
+	apiDir := module.getApiDir()
+	currentApiFileName = path.Join(apiDir, currentApiFileName)
+	removedApiFileName = path.Join(apiDir, removedApiFileName)
 	// TODO(jiyong): remove these three props
 	props.Api_tag_name = proptools.StringPtr(module.apiTagName(apiScope))
 	props.Api_filename = proptools.StringPtr(currentApiFileName)
@@ -637,7 +667,11 @@
 	jar := filepath.Join(dir, module.BaseModuleName()+".jar")
 	jarPath := android.ExistentPathForSource(ctx, jar)
 	if !jarPath.Valid() {
-		ctx.PropertyErrorf("sdk_library", "invalid sdk version %q, %q does not exist", v, jar)
+		if ctx.Config().AllowMissingDependencies() {
+			return android.Paths{android.PathForSource(ctx, jar)}
+		} else {
+			ctx.PropertyErrorf("sdk_library", "invalid sdk version %q, %q does not exist", sdkVersion, jar)
+		}
 		return nil
 	}
 	return android.Paths{jarPath.Path()}
@@ -687,6 +721,10 @@
 	}).(*[]string)
 }
 
+func (module *SdkLibrary) getApiDir() string {
+	return proptools.StringDefault(module.sdkLibraryProperties.Api_dir, "api")
+}
+
 // For a java_sdk_library module, create internal modules for stubs, docs,
 // runtime libs and xml file. If requested, the stubs and docs are created twice
 // once for public API level and once for system API level
@@ -696,11 +734,6 @@
 		return
 	}
 
-	if len(module.sdkLibraryProperties.Api_packages) == 0 {
-		mctx.PropertyErrorf("api_packages", "java_sdk_library must specify api_packages")
-		return
-	}
-
 	// If this builds against standard libraries (i.e. is not part of the core libraries)
 	// then assume it provides both system and test apis. Otherwise, assume it does not and
 	// also assume it does not contribute to the dist build.
@@ -716,9 +749,10 @@
 
 	missing_current_api := false
 
+	apiDir := module.getApiDir()
 	for _, scope := range scopes {
 		for _, api := range []string{"current.txt", "removed.txt"} {
-			path := path.Join(mctx.ModuleDir(), "api", scope+api)
+			path := path.Join(mctx.ModuleDir(), apiDir, scope+api)
 			p := android.ExistentPathForSource(mctx, path)
 			if !p.Valid() {
 				mctx.ModuleErrorf("Current api file %#v doesn't exist", path)
@@ -738,7 +772,7 @@
 		mctx.ModuleErrorf("One or more current api files are missing. "+
 			"You can update them by:\n"+
 			"%s %q %s && m update-api",
-			script, mctx.ModuleDir(), strings.Join(scopes, " "))
+			script, filepath.Join(mctx.ModuleDir(), apiDir), strings.Join(scopes, " "))
 		return
 	}
 
@@ -754,16 +788,18 @@
 		// for test API stubs
 		module.createStubsLibrary(mctx, apiScopeTest)
 		module.createStubsSources(mctx, apiScopeTest)
-
-		// for runtime
-		module.createXmlFile(mctx)
 	}
 
-	// record java_sdk_library modules so that they are exported to make
-	javaSdkLibraries := javaSdkLibraries(mctx.Config())
-	javaSdkLibrariesLock.Lock()
-	defer javaSdkLibrariesLock.Unlock()
-	*javaSdkLibraries = append(*javaSdkLibraries, module.BaseModuleName())
+	if !proptools.Bool(module.sdkLibraryProperties.Api_only) {
+		// for runtime
+		module.createXmlFile(mctx)
+
+		// record java_sdk_library modules so that they are exported to make
+		javaSdkLibraries := javaSdkLibraries(mctx.Config())
+		javaSdkLibrariesLock.Lock()
+		defer javaSdkLibrariesLock.Unlock()
+		*javaSdkLibraries = append(*javaSdkLibraries, module.BaseModuleName())
+	}
 }
 
 func (module *SdkLibrary) InitSdkLibraryProperties() {
diff --git a/java/testing.go b/java/testing.go
index ab3af65..08bae44 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -205,9 +205,6 @@
 	systemModules := []string{
 		"core-current-stubs-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 {
diff --git a/python/androidmk.go b/python/androidmk.go
index aae7ced..8e8e8ef 100644
--- a/python/androidmk.go
+++ b/python/androidmk.go
@@ -76,6 +76,10 @@
 					p.testConfig.String())
 			}
 		}
+
+		if !BoolDefault(p.binaryProperties.Auto_gen_config, true) {
+			fmt.Fprintln(w, "LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG := true")
+		}
 	})
 	base.subAndroidMk(ret, p.binaryDecorator.pythonInstaller)
 }
diff --git a/rust/androidmk.go b/rust/androidmk.go
index 2636d97..0fba739 100644
--- a/rust/androidmk.go
+++ b/rust/androidmk.go
@@ -99,6 +99,9 @@
 		if test.testConfig != nil {
 			fmt.Fprintln(w, "LOCAL_FULL_TEST_CONFIG :=", test.testConfig.String())
 		}
+		if !BoolDefault(test.Properties.Auto_gen_config, true) {
+			fmt.Fprintln(w, "LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG := true")
+		}
 	})
 	// TODO(chh): add test data with androidMkWriteTestData(test.data, ctx, ret)
 }
diff --git a/rust/config/global.go b/rust/config/global.go
index 4d87780..ad8eb3a 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -24,7 +24,7 @@
 var pctx = android.NewPackageContext("android/soong/rust/config")
 
 var (
-	RustDefaultVersion = "1.37.0"
+	RustDefaultVersion = "1.40.0"
 	RustDefaultBase    = "prebuilts/rust/"
 	DefaultEdition     = "2018"
 	Stdlibs            = []string{
diff --git a/scripts/gen-java-current-api-files.sh b/scripts/gen-java-current-api-files.sh
index 902fc2f..547387a 100755
--- a/scripts/gen-java-current-api-files.sh
+++ b/scripts/gen-java-current-api-files.sh
@@ -19,7 +19,7 @@
   exit 1
 fi
 
-api_dir=$1/api
+api_dir=$1
 shift
 
 mkdir -p "$api_dir"
diff --git a/ui/build/Android.bp b/ui/build/Android.bp
index f212fb6..2a5a51a 100644
--- a/ui/build/Android.bp
+++ b/ui/build/Android.bp
@@ -59,6 +59,7 @@
         "util.go",
     ],
     testSrcs: [
+        "cleanbuild_test.go",
         "config_test.go",
         "environment_test.go",
         "util_test.go",
diff --git a/ui/build/cleanbuild.go b/ui/build/cleanbuild.go
index 0b44b4d..1c4f574 100644
--- a/ui/build/cleanbuild.go
+++ b/ui/build/cleanbuild.go
@@ -15,10 +15,12 @@
 package build
 
 import (
+	"bytes"
 	"fmt"
 	"io/ioutil"
 	"os"
 	"path/filepath"
+	"sort"
 	"strings"
 
 	"android/soong/ui/metrics"
@@ -177,3 +179,78 @@
 
 	writeConfig()
 }
+
+// cleanOldFiles takes an input file (with all paths relative to basePath), and removes files from
+// the filesystem if they were removed from the input file since the last execution.
+func cleanOldFiles(ctx Context, basePath, file string) {
+	file = filepath.Join(basePath, file)
+	oldFile := file + ".previous"
+
+	if _, err := os.Stat(file); err != nil {
+		ctx.Fatalf("Expected %q to be readable", file)
+	}
+
+	if _, err := os.Stat(oldFile); os.IsNotExist(err) {
+		if err := os.Rename(file, oldFile); err != nil {
+			ctx.Fatalf("Failed to rename file list (%q->%q): %v", file, oldFile, err)
+		}
+		return
+	}
+
+	var newPaths, oldPaths []string
+	if newData, err := ioutil.ReadFile(file); err == nil {
+		if oldData, err := ioutil.ReadFile(oldFile); err == nil {
+			// Common case: nothing has changed
+			if bytes.Equal(newData, oldData) {
+				return
+			}
+			newPaths = strings.Fields(string(newData))
+			oldPaths = strings.Fields(string(oldData))
+		} else {
+			ctx.Fatalf("Failed to read list of installable files (%q): %v", oldFile, err)
+		}
+	} else {
+		ctx.Fatalf("Failed to read list of installable files (%q): %v", file, err)
+	}
+
+	// These should be mostly sorted by make already, but better make sure Go concurs
+	sort.Strings(newPaths)
+	sort.Strings(oldPaths)
+
+	for len(oldPaths) > 0 {
+		if len(newPaths) > 0 {
+			if oldPaths[0] == newPaths[0] {
+				// Same file; continue
+				newPaths = newPaths[1:]
+				oldPaths = oldPaths[1:]
+				continue
+			} else if oldPaths[0] > newPaths[0] {
+				// New file; ignore
+				newPaths = newPaths[1:]
+				continue
+			}
+		}
+		// File only exists in the old list; remove if it exists
+		old := filepath.Join(basePath, oldPaths[0])
+		oldPaths = oldPaths[1:]
+		if fi, err := os.Stat(old); err == nil {
+			if fi.IsDir() {
+				if err := os.Remove(old); err == nil {
+					ctx.Println("Removed directory that is no longer installed: ", old)
+				} else {
+					ctx.Println("Failed to remove directory that is no longer installed (%q): %v", old, err)
+					ctx.Println("It's recommended to run `m installclean`")
+				}
+			} else {
+				if err := os.Remove(old); err == nil {
+					ctx.Println("Removed file that is no longer installed: ", old)
+				} else if !os.IsNotExist(err) {
+					ctx.Fatalf("Failed to remove file that is no longer installed (%q): %v", old, err)
+				}
+			}
+		}
+	}
+
+	// Use the new list as the base for the next build
+	os.Rename(file, oldFile)
+}
diff --git a/ui/build/cleanbuild_test.go b/ui/build/cleanbuild_test.go
new file mode 100644
index 0000000..89f4ad9
--- /dev/null
+++ b/ui/build/cleanbuild_test.go
@@ -0,0 +1,100 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package build
+
+import (
+	"android/soong/ui/logger"
+	"bytes"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"reflect"
+	"sort"
+	"strings"
+	"testing"
+)
+
+func TestCleanOldFiles(t *testing.T) {
+	dir, err := ioutil.TempDir("", "testcleanoldfiles")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer os.RemoveAll(dir)
+
+	ctx := testContext()
+	logBuf := &bytes.Buffer{}
+	ctx.Logger = logger.New(logBuf)
+
+	touch := func(names ...string) {
+		for _, name := range names {
+			if f, err := os.Create(filepath.Join(dir, name)); err != nil {
+				t.Fatal(err)
+			} else {
+				f.Close()
+			}
+		}
+	}
+	runCleanOldFiles := func(names ...string) {
+		data := []byte(strings.Join(names, " "))
+		if err := ioutil.WriteFile(filepath.Join(dir, ".installed"), data, 0666); err != nil {
+			t.Fatal(err)
+		}
+
+		cleanOldFiles(ctx, dir, ".installed")
+	}
+
+	assertFileList := func(names ...string) {
+		t.Helper()
+
+		sort.Strings(names)
+
+		var foundNames []string
+		if foundFiles, err := ioutil.ReadDir(dir); err == nil {
+			for _, fi := range foundFiles {
+				foundNames = append(foundNames, fi.Name())
+			}
+		} else {
+			t.Fatal(err)
+		}
+
+		if !reflect.DeepEqual(names, foundNames) {
+			t.Errorf("Expected a different list of files:\nwant: %v\n got: %v", names, foundNames)
+			t.Error("Log: ", logBuf.String())
+			logBuf.Reset()
+		}
+	}
+
+	// Initial list of potential files
+	runCleanOldFiles("foo", "bar")
+	touch("foo", "bar", "baz")
+	assertFileList("foo", "bar", "baz", ".installed.previous")
+
+	// This should be a no-op, as the list hasn't changed
+	runCleanOldFiles("foo", "bar")
+	assertFileList("foo", "bar", "baz", ".installed", ".installed.previous")
+
+	// This should be a no-op, as only a file was added
+	runCleanOldFiles("foo", "bar", "foo2")
+	assertFileList("foo", "bar", "baz", ".installed.previous")
+
+	// "bar" should be removed, foo2 should be ignored as it was never there
+	runCleanOldFiles("foo")
+	assertFileList("foo", "baz", ".installed.previous")
+
+	// Recreate bar, and create foo2. Ensure that they aren't removed
+	touch("bar", "foo2")
+	runCleanOldFiles("foo", "baz")
+	assertFileList("foo", "bar", "baz", "foo2", ".installed.previous")
+}
diff --git a/ui/build/config.go b/ui/build/config.go
index fae569f..c084171 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -55,8 +55,9 @@
 
 	pdkBuild bool
 
-	brokenDupRules    bool
-	brokenUsesNetwork bool
+	brokenDupRules     bool
+	brokenUsesNetwork  bool
+	brokenNinjaEnvVars []string
 
 	pathReplaced bool
 }
@@ -907,6 +908,14 @@
 	return c.brokenUsesNetwork
 }
 
+func (c *configImpl) SetBuildBrokenNinjaUsesEnvVars(val []string) {
+	c.brokenNinjaEnvVars = val
+}
+
+func (c *configImpl) BuildBrokenNinjaUsesEnvVars() []string {
+	return c.brokenNinjaEnvVars
+}
+
 func (c *configImpl) SetTargetDeviceDir(dir string) {
 	c.targetDeviceDir = dir
 }
diff --git a/ui/build/dumpvars.go b/ui/build/dumpvars.go
index 4270bb1..c3da38b 100644
--- a/ui/build/dumpvars.go
+++ b/ui/build/dumpvars.go
@@ -216,6 +216,9 @@
 		// Whether to enable the network during the build
 		"BUILD_BROKEN_USES_NETWORK",
 
+		// Extra environment variables to be exported to ninja
+		"BUILD_BROKEN_NINJA_USES_ENV_VARS",
+
 		// Not used, but useful to be in the soong.log
 		"BOARD_VNDK_VERSION",
 
@@ -284,4 +287,5 @@
 	config.SetPdkBuild(make_vars["TARGET_BUILD_PDK"] == "true")
 	config.SetBuildBrokenDupRules(make_vars["BUILD_BROKEN_DUP_RULES"] == "true")
 	config.SetBuildBrokenUsesNetwork(make_vars["BUILD_BROKEN_USES_NETWORK"] == "true")
+	config.SetBuildBrokenNinjaUsesEnvVars(strings.Fields(make_vars["BUILD_BROKEN_NINJA_USES_ENV_VARS"]))
 }
diff --git a/ui/build/kati.go b/ui/build/kati.go
index ac09ce1..a845c5b 100644
--- a/ui/build/kati.go
+++ b/ui/build/kati.go
@@ -153,6 +153,7 @@
 	runKati(ctx, config, katiBuildSuffix, args, func(env *Environment) {})
 
 	cleanCopyHeaders(ctx, config)
+	cleanOldInstalledFiles(ctx, config)
 }
 
 func cleanCopyHeaders(ctx Context, config Config) {
@@ -192,6 +193,23 @@
 		})
 }
 
+func cleanOldInstalledFiles(ctx Context, config Config) {
+	ctx.BeginTrace("clean", "clean old installed files")
+	defer ctx.EndTrace()
+
+	// We shouldn't be removing files from one side of the two-step asan builds
+	var suffix string
+	if v, ok := config.Environment().Get("SANITIZE_TARGET"); ok {
+		if sanitize := strings.Fields(v); inList("address", sanitize) {
+			suffix = "_asan"
+		}
+	}
+
+	cleanOldFiles(ctx, config.ProductOut(), ".installable_files"+suffix)
+
+	cleanOldFiles(ctx, config.HostOut(), ".installable_test_files")
+}
+
 func runKatiPackage(ctx Context, config Config) {
 	ctx.BeginTrace(metrics.RunKati, "kati package")
 	defer ctx.EndTrace()
diff --git a/ui/build/ninja.go b/ui/build/ninja.go
index d5baafe..8c6ebb8 100644
--- a/ui/build/ninja.go
+++ b/ui/build/ninja.go
@@ -18,6 +18,7 @@
 	"fmt"
 	"os"
 	"path/filepath"
+	"sort"
 	"strconv"
 	"strings"
 	"time"
@@ -65,8 +66,6 @@
 		cmd.Environment.AppendFromKati(config.KatiEnvFile())
 	}
 
-	cmd.Environment.Set("DIST_DIR", config.DistDir())
-
 	// Allow both NINJA_ARGS and NINJA_EXTRA_ARGS, since both have been
 	// used in the past to specify extra ninja arguments.
 	if extra, ok := cmd.Environment.Get("NINJA_ARGS"); ok {
@@ -85,6 +84,74 @@
 			ninjaHeartbeatDuration = overrideDuration
 		}
 	}
+
+	// Filter the environment, as ninja does not rebuild files when environment variables change.
+	//
+	// Anything listed here must not change the output of rules/actions when the value changes,
+	// otherwise incremental builds may be unsafe. Vars explicitly set to stable values
+	// elsewhere in soong_ui are fine.
+	//
+	// For the majority of cases, either Soong or the makefiles should be replicating any
+	// necessary environment variables in the command line of each action that needs it.
+	if cmd.Environment.IsEnvTrue("ALLOW_NINJA_ENV") {
+		ctx.Println("Allowing all environment variables during ninja; incremental builds may be unsafe.")
+	} else {
+		cmd.Environment.Allow(append([]string{
+			"ASAN_SYMBOLIZER_PATH",
+			"HOME",
+			"JAVA_HOME",
+			"LANG",
+			"LC_MESSAGES",
+			"OUT_DIR",
+			"PATH",
+			"PWD",
+			"PYTHONDONTWRITEBYTECODE",
+			"TMPDIR",
+			"USER",
+
+			// TODO: remove these carefully
+			"ASAN_OPTIONS",
+			"TARGET_BUILD_APPS",
+			"TARGET_BUILD_VARIANT",
+			"TARGET_PRODUCT",
+			// b/147197813 - used by art-check-debug-apex-gen
+			"EMMA_INSTRUMENT_FRAMEWORK",
+
+			// Goma -- gomacc may not need all of these
+			"GOMA_DIR",
+			"GOMA_DISABLED",
+			"GOMA_FAIL_FAST",
+			"GOMA_FALLBACK",
+			"GOMA_GCE_SERVICE_ACCOUNT",
+			"GOMA_TMP_DIR",
+			"GOMA_USE_LOCAL",
+
+			// RBE client
+			"FLAG_exec_root",
+			"FLAG_exec_strategy",
+			"FLAG_invocation_id",
+			"FLAG_log_dir",
+			"FLAG_platform",
+			"FLAG_server_address",
+
+			// ccache settings
+			"CCACHE_COMPILERCHECK",
+			"CCACHE_SLOPPINESS",
+			"CCACHE_BASEDIR",
+			"CCACHE_CPP2",
+		}, config.BuildBrokenNinjaUsesEnvVars()...)...)
+	}
+
+	cmd.Environment.Set("DIST_DIR", config.DistDir())
+	cmd.Environment.Set("SHELL", "/bin/bash")
+
+	ctx.Verboseln("Ninja environment: ")
+	envVars := cmd.Environment.Environ()
+	sort.Strings(envVars)
+	for _, envVar := range envVars {
+		ctx.Verbosef("  %s", envVar)
+	}
+
 	// Poll the ninja log for updates; if it isn't updated enough, then we want to show some diagnostics
 	done := make(chan struct{})
 	defer close(done)
diff --git a/ui/status/critical_path.go b/ui/status/critical_path.go
index 444327b..8065c60 100644
--- a/ui/status/critical_path.go
+++ b/ui/status/critical_path.go
@@ -112,8 +112,10 @@
 		if !cp.start.IsZero() {
 			elapsedTime := cp.end.Sub(cp.start).Round(time.Second)
 			cp.log.Verbosef("elapsed time %s", elapsedTime.String())
-			cp.log.Verbosef("perfect parallelism ratio %d%%",
-				int(float64(criticalTime)/float64(elapsedTime)*100))
+			if elapsedTime > 0 {
+				cp.log.Verbosef("perfect parallelism ratio %d%%",
+					int(float64(criticalTime)/float64(elapsedTime)*100))
+			}
 		}
 		cp.log.Verbose("critical path:")
 		for i := len(criticalPath) - 1; i >= 0; i-- {