Merge "[cc/sanitize] Deprecate blacklist sub-property of sanitize"
diff --git a/android/Android.bp b/android/Android.bp
index 50faa44..a1b5159 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -15,6 +15,7 @@
         "apex.go",
         "api_levels.go",
         "arch.go",
+        "bazel_overlay.go",
         "config.go",
         "csuite_config.go",
         "defaults.go",
@@ -40,6 +41,7 @@
         "paths.go",
         "phony.go",
         "prebuilt.go",
+        "prebuilt_build_tool.go",
         "proto.go",
         "register.go",
         "rule_builder.go",
diff --git a/android/androidmk.go b/android/androidmk.go
index 7e86140..fafbfd6 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -183,12 +183,15 @@
 	name := amod.BaseModuleName()
 
 	var ret []string
+	var availableTaggedDists TaggedDistFiles
 
-	availableTaggedDists := TaggedDistFiles{}
 	if a.DistFiles != nil {
 		availableTaggedDists = a.DistFiles
 	} else if a.OutputFile.Valid() {
 		availableTaggedDists = MakeDefaultDistFiles(a.OutputFile.Path())
+	} else {
+		// Nothing dist-able for this module.
+		return nil
 	}
 
 	// Iterate over this module's dist structs, merged from the dist and dists properties.
diff --git a/android/apex.go b/android/apex.go
index a7570dc..8c06b63 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -137,6 +137,7 @@
 	//
 	// "//apex_available:anyapex" is a pseudo APEX name that matches to any APEX.
 	// "//apex_available:platform" refers to non-APEX partitions like "system.img".
+	// "com.android.gki.*" matches any APEX module name with the prefix "com.android.gki.".
 	// Default is ["//apex_available:platform"].
 	Apex_available []string
 
@@ -213,6 +214,7 @@
 const (
 	AvailableToPlatform = "//apex_available:platform"
 	AvailableToAnyApex  = "//apex_available:anyapex"
+	AvailableToGkiApex  = "com.android.gki.*"
 )
 
 func CheckAvailableForApex(what string, apex_available []string) bool {
@@ -222,7 +224,8 @@
 		return what == AvailableToPlatform
 	}
 	return InList(what, apex_available) ||
-		(what != AvailableToPlatform && InList(AvailableToAnyApex, apex_available))
+		(what != AvailableToPlatform && InList(AvailableToAnyApex, apex_available)) ||
+		(strings.HasPrefix(what, "com.android.gki.") && InList(AvailableToGkiApex, apex_available))
 }
 
 func (m *ApexModuleBase) AvailableFor(what string) bool {
@@ -256,7 +259,7 @@
 
 func (m *ApexModuleBase) checkApexAvailableProperty(mctx BaseModuleContext) {
 	for _, n := range m.ApexProperties.Apex_available {
-		if n == AvailableToPlatform || n == AvailableToAnyApex {
+		if n == AvailableToPlatform || n == AvailableToAnyApex || n == AvailableToGkiApex {
 			continue
 		}
 		if !mctx.OtherModuleExists(n) && !mctx.Config().AllowMissingDependencies() {
@@ -293,7 +296,10 @@
 		for i, mod := range modules {
 			platformVariation := i == 0
 			if platformVariation && !mctx.Host() && !mod.(ApexModule).AvailableFor(AvailableToPlatform) {
-				mod.SkipInstall()
+				// Do not install the module for platform, but still allow it to output
+				// uninstallable AndroidMk entries in certain cases when they have
+				// side effects.
+				mod.MakeUninstallable()
 			}
 			if !platformVariation {
 				mod.(ApexModule).apexModuleBase().ApexProperties.Info = m.apexVariations[i-1]
diff --git a/android/bazel_overlay.go b/android/bazel_overlay.go
new file mode 100644
index 0000000..a034282
--- /dev/null
+++ b/android/bazel_overlay.go
@@ -0,0 +1,76 @@
+// 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 android
+
+import (
+	"fmt"
+	"os"
+	"strings"
+
+	"github.com/google/blueprint"
+)
+
+// The Bazel Overlay singleton is responsible for generating the Ninja actions
+// for calling the soong_build primary builder in the main build.ninja file.
+func init() {
+	RegisterSingletonType("bazel_overlay", BazelOverlaySingleton)
+}
+
+func BazelOverlaySingleton() Singleton {
+	return &bazelOverlaySingleton{}
+}
+
+type bazelOverlaySingleton struct{}
+
+func (c *bazelOverlaySingleton) GenerateBuildActions(ctx SingletonContext) {
+	// Create a build and rule statement, using the Bazel overlay's WORKSPACE
+	// file as the output file marker.
+	var deps Paths
+	moduleListFilePath := pathForBuildToolDep(ctx, ctx.Config().moduleListFile)
+	deps = append(deps, moduleListFilePath)
+	deps = append(deps, pathForBuildToolDep(ctx, ctx.Config().ProductVariablesFileName))
+
+	bazelOverlayDirectory := PathForOutput(ctx, "bazel_overlay")
+	bazelOverlayWorkspaceFile := bazelOverlayDirectory.Join(ctx, "WORKSPACE")
+	primaryBuilder := primaryBuilderPath(ctx)
+	bazelOverlay := ctx.Rule(pctx, "bazelOverlay",
+		blueprint.RuleParams{
+			Command: fmt.Sprintf(
+				"rm -rf ${outDir}/* && %s --bazel_overlay_dir ${outDir} %s && echo WORKSPACE: `cat %s` > ${outDir}/.overlay-depfile.d",
+				primaryBuilder.String(),
+				strings.Join(os.Args[1:], " "),
+				moduleListFilePath.String(), // Use the contents of Android.bp.list as the depfile.
+			),
+			CommandDeps: []string{primaryBuilder.String()},
+			Description: fmt.Sprintf(
+				"Creating the Bazel overlay workspace with %s at $outDir",
+				primaryBuilder.Base()),
+			Deps:    blueprint.DepsGCC,
+			Depfile: "${outDir}/.overlay-depfile.d",
+		},
+		"outDir")
+
+	ctx.Build(pctx, BuildParams{
+		Rule:   bazelOverlay,
+		Output: bazelOverlayWorkspaceFile,
+		Inputs: deps,
+		Args: map[string]string{
+			"outDir": bazelOverlayDirectory.String(),
+		},
+	})
+
+	// Add a phony target for building the bazel overlay
+	ctx.Phony("bazel_overlay", bazelOverlayWorkspaceFile)
+}
diff --git a/android/config.go b/android/config.go
index aeed81d..a1e97c9 100644
--- a/android/config.go
+++ b/android/config.go
@@ -722,7 +722,7 @@
 	return Bool(c.productVariables.Allow_missing_dependencies)
 }
 
-// Returns true if building without full platform sources.
+// Returns true if a full platform source tree cannot be assumed.
 func (c *config) UnbundledBuild() bool {
 	return Bool(c.productVariables.Unbundled_build)
 }
@@ -733,18 +733,15 @@
 	return Bool(c.productVariables.Unbundled_build_apps)
 }
 
-func (c *config) UnbundledBuildUsePrebuiltSdks() bool {
-	return Bool(c.productVariables.Unbundled_build) && !Bool(c.productVariables.Unbundled_build_sdks_from_source)
+// Returns true if building modules against prebuilt SDKs.
+func (c *config) AlwaysUsePrebuiltSdks() bool {
+	return Bool(c.productVariables.Always_use_prebuilt_sdks)
 }
 
 func (c *config) Fuchsia() bool {
 	return Bool(c.productVariables.Fuchsia)
 }
 
-func (c *config) IsPdkBuild() bool {
-	return Bool(c.productVariables.Pdk)
-}
-
 func (c *config) MinimizeJavaDebugInfo() bool {
 	return Bool(c.productVariables.MinimizeJavaDebugInfo) && !Bool(c.productVariables.Eng)
 }
@@ -913,34 +910,6 @@
 	return c.productVariables.ModulesLoadedByPrivilegedModules
 }
 
-// Expected format for apexJarValue = <apex name>:<jar name>
-func SplitApexJarPair(ctx PathContext, str string) (string, string) {
-	pair := strings.SplitN(str, ":", 2)
-	if len(pair) == 2 {
-		return pair[0], pair[1]
-	} else {
-		reportPathErrorf(ctx, "malformed (apex, jar) pair: '%s', expected format: <apex>:<jar>", str)
-		return "error-apex", "error-jar"
-	}
-}
-
-func GetJarsFromApexJarPairs(ctx PathContext, apexJarPairs []string) []string {
-	modules := make([]string, len(apexJarPairs))
-	for i, p := range apexJarPairs {
-		_, jar := SplitApexJarPair(ctx, p)
-		modules[i] = jar
-	}
-	return modules
-}
-
-func (c *config) BootJars() []string {
-	ctx := NullPathContext{Config{
-		config: c,
-	}}
-	return append(GetJarsFromApexJarPairs(ctx, c.productVariables.BootJars),
-		GetJarsFromApexJarPairs(ctx, c.productVariables.UpdatableBootJars)...)
-}
-
 func (c *config) DexpreoptGlobalConfig(ctx PathContext) ([]byte, error) {
 	if c.productVariables.DexpreoptGlobalConfig == nil {
 		return nil, nil
@@ -1271,3 +1240,189 @@
 func (c *deviceConfig) BoardUsesRecoveryAsBoot() bool {
 	return Bool(c.config.productVariables.BoardUsesRecoveryAsBoot)
 }
+
+func (c *deviceConfig) BoardKernelBinaries() []string {
+	return c.config.productVariables.BoardKernelBinaries
+}
+
+func (c *deviceConfig) BoardKernelModuleInterfaceVersions() []string {
+	return c.config.productVariables.BoardKernelModuleInterfaceVersions
+}
+
+// The ConfiguredJarList struct provides methods for handling a list of (apex, jar) pairs.
+// Such lists are used in the build system for things like bootclasspath jars or system server jars.
+// The apex part is either an apex name, or a special names "platform" or "system_ext". Jar is a
+// module name. The pairs come from Make product variables as a list of colon-separated strings.
+//
+// Examples:
+//   - "com.android.art:core-oj"
+//   - "platform:framework"
+//   - "system_ext:foo"
+//
+type ConfiguredJarList struct {
+	apexes []string // A list of apex components.
+	jars   []string // A list of jar components.
+}
+
+// The length of the list.
+func (l *ConfiguredJarList) Len() int {
+	return len(l.jars)
+}
+
+// Apex component of idx-th pair on the list.
+func (l *ConfiguredJarList) apex(idx int) string {
+	return l.apexes[idx]
+}
+
+// Jar component of idx-th pair on the list.
+func (l *ConfiguredJarList) Jar(idx int) string {
+	return l.jars[idx]
+}
+
+// If the list contains a pair with the given jar.
+func (l *ConfiguredJarList) ContainsJar(jar string) bool {
+	return InList(jar, l.jars)
+}
+
+// If the list contains the given (apex, jar) pair.
+func (l *ConfiguredJarList) containsApexJarPair(apex, jar string) bool {
+	for i := 0; i < l.Len(); i++ {
+		if apex == l.apex(i) && jar == l.Jar(i) {
+			return true
+		}
+	}
+	return false
+}
+
+// Index of the first pair with the given jar on the list, or -1 if none.
+func (l *ConfiguredJarList) IndexOfJar(jar string) int {
+	return IndexList(jar, l.jars)
+}
+
+// Append an (apex, jar) pair to the list.
+func (l *ConfiguredJarList) Append(apex string, jar string) {
+	l.apexes = append(l.apexes, apex)
+	l.jars = append(l.jars, jar)
+}
+
+// Filter out sublist.
+func (l *ConfiguredJarList) RemoveList(list ConfiguredJarList) {
+	apexes := make([]string, 0, l.Len())
+	jars := make([]string, 0, l.Len())
+
+	for i, jar := range l.jars {
+		apex := l.apex(i)
+		if !list.containsApexJarPair(apex, jar) {
+			apexes = append(apexes, apex)
+			jars = append(jars, jar)
+		}
+	}
+
+	l.apexes = apexes
+	l.jars = jars
+}
+
+// A copy of itself.
+func (l *ConfiguredJarList) CopyOf() ConfiguredJarList {
+	return ConfiguredJarList{CopyOf(l.apexes), CopyOf(l.jars)}
+}
+
+// A copy of the list of strings containing jar components.
+func (l *ConfiguredJarList) CopyOfJars() []string {
+	return CopyOf(l.jars)
+}
+
+// A copy of the list of strings with colon-separated (apex, jar) pairs.
+func (l *ConfiguredJarList) CopyOfApexJarPairs() []string {
+	pairs := make([]string, 0, l.Len())
+
+	for i, jar := range l.jars {
+		apex := l.apex(i)
+		pairs = append(pairs, apex+":"+jar)
+	}
+
+	return pairs
+}
+
+// A list of build paths based on the given directory prefix.
+func (l *ConfiguredJarList) BuildPaths(ctx PathContext, dir OutputPath) WritablePaths {
+	paths := make(WritablePaths, l.Len())
+	for i, jar := range l.jars {
+		paths[i] = dir.Join(ctx, ModuleStem(jar)+".jar")
+	}
+	return paths
+}
+
+func ModuleStem(module string) string {
+	// b/139391334: the stem of framework-minus-apex is framework. This is hard coded here until we
+	// find a good way to query the stem of a module before any other mutators are run.
+	if module == "framework-minus-apex" {
+		return "framework"
+	}
+	return module
+}
+
+// A list of on-device paths.
+func (l *ConfiguredJarList) DevicePaths(cfg Config, ostype OsType) []string {
+	paths := make([]string, l.Len())
+	for i, jar := range l.jars {
+		apex := l.apexes[i]
+		name := ModuleStem(jar) + ".jar"
+
+		var subdir string
+		if apex == "platform" {
+			subdir = "system/framework"
+		} else if apex == "system_ext" {
+			subdir = "system_ext/framework"
+		} else {
+			subdir = filepath.Join("apex", apex, "javalib")
+		}
+
+		if ostype.Class == Host {
+			paths[i] = filepath.Join(cfg.Getenv("OUT_DIR"), "host", cfg.PrebuiltOS(), subdir, name)
+		} else {
+			paths[i] = filepath.Join("/", subdir, name)
+		}
+	}
+	return paths
+}
+
+// Expected format for apexJarValue = <apex name>:<jar name>
+func splitConfiguredJarPair(ctx PathContext, str string) (string, string) {
+	pair := strings.SplitN(str, ":", 2)
+	if len(pair) == 2 {
+		return pair[0], pair[1]
+	} else {
+		reportPathErrorf(ctx, "malformed (apex, jar) pair: '%s', expected format: <apex>:<jar>", str)
+		return "error-apex", "error-jar"
+	}
+}
+
+func CreateConfiguredJarList(ctx PathContext, list []string) ConfiguredJarList {
+	apexes := make([]string, 0, len(list))
+	jars := make([]string, 0, len(list))
+
+	l := ConfiguredJarList{apexes, jars}
+
+	for _, apexjar := range list {
+		apex, jar := splitConfiguredJarPair(ctx, apexjar)
+		l.Append(apex, jar)
+	}
+
+	return l
+}
+
+func EmptyConfiguredJarList() ConfiguredJarList {
+	return ConfiguredJarList{}
+}
+
+var earlyBootJarsKey = NewOnceKey("earlyBootJars")
+
+func (c *config) BootJars() []string {
+	return c.Once(earlyBootJarsKey, func() interface{} {
+		ctx := NullPathContext{Config{c}}
+		list := CreateConfiguredJarList(ctx,
+			append(CopyOf(c.productVariables.BootJars), c.productVariables.UpdatableBootJars...))
+		return list.CopyOfJars()
+	}).([]string)
+}
diff --git a/android/defs.go b/android/defs.go
index 4552224..83daa03 100644
--- a/android/defs.go
+++ b/android/defs.go
@@ -69,7 +69,7 @@
 	// A symlink rule.
 	Symlink = pctx.AndroidStaticRule("Symlink",
 		blueprint.RuleParams{
-			Command:     "ln -f -s $fromPath $out",
+			Command:     "rm -f $out && ln -f -s $fromPath $out",
 			Description: "symlink $out",
 		},
 		"fromPath")
diff --git a/android/filegroup.go b/android/filegroup.go
index ec522fc..68311e3 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -15,9 +15,7 @@
 package android
 
 import (
-	"io"
 	"strings"
-	"text/template"
 )
 
 func init() {
@@ -71,23 +69,8 @@
 	return append(Paths{}, fg.srcs...)
 }
 
-var androidMkTemplate = template.Must(template.New("filegroup").Parse(`
-ifdef {{.makeVar}}
-  $(error variable {{.makeVar}} set by soong module is already set in make)
-endif
-{{.makeVar}} := {{.value}}
-.KATI_READONLY := {{.makeVar}}
-`))
-
-func (fg *fileGroup) AndroidMk() AndroidMkData {
-	return AndroidMkData{
-		Custom: func(w io.Writer, name, prefix, moduleDir string, data AndroidMkData) {
-			if makeVar := String(fg.properties.Export_to_make_var); makeVar != "" {
-				androidMkTemplate.Execute(w, map[string]string{
-					"makeVar": makeVar,
-					"value":   strings.Join(fg.srcs.Strings(), " "),
-				})
-			}
-		},
+func (fg *fileGroup) MakeVars(ctx MakeVarsModuleContext) {
+	if makeVar := String(fg.properties.Export_to_make_var); makeVar != "" {
+		ctx.StrictRaw(makeVar, strings.Join(fg.srcs.Strings(), " "))
 	}
 }
diff --git a/android/makevars.go b/android/makevars.go
index ff7c8e4..86f4b42 100644
--- a/android/makevars.go
+++ b/android/makevars.go
@@ -17,6 +17,7 @@
 import (
 	"bytes"
 	"fmt"
+	"sort"
 	"strconv"
 	"strings"
 
@@ -34,43 +35,16 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
-// Interface for other packages to use to declare make variables
-type MakeVarsContext interface {
+
+// BaseMakeVarsContext contains the common functions for other packages to use
+// to declare make variables
+type BaseMakeVarsContext interface {
 	Config() Config
 	DeviceConfig() DeviceConfig
 	AddNinjaFileDeps(deps ...string)
 
-	ModuleName(module blueprint.Module) string
-	ModuleDir(module blueprint.Module) string
-	ModuleSubDir(module blueprint.Module) string
-	ModuleType(module blueprint.Module) string
-	BlueprintFile(module blueprint.Module) string
-
-	ModuleErrorf(module blueprint.Module, format string, args ...interface{})
-	Errorf(format string, args ...interface{})
 	Failed() bool
 
-	VisitAllModules(visit func(Module))
-	VisitAllModulesIf(pred func(Module) bool, visit func(Module))
-
-	// Verify the make variable matches the Soong version, fail the build
-	// if it does not. If the make variable is empty, just set it.
-	Strict(name, ninjaStr string)
-	// Check to see if the make variable matches the Soong version, warn if
-	// it does not. If the make variable is empty, just set it.
-	Check(name, ninjaStr string)
-
-	// These are equivalent to the above, but sort the make and soong
-	// variables before comparing them. They also show the unique entries
-	// in each list when displaying the difference, instead of the entire
-	// string.
-	StrictSorted(name, ninjaStr string)
-	CheckSorted(name, ninjaStr string)
-
-	// Evaluates a ninja string and returns the result. Used if more
-	// complicated modification needs to happen before giving it to Make.
-	Eval(ninjaStr string) (string, error)
-
 	// These are equivalent to Strict and Check, but do not attempt to
 	// evaluate the values before writing them to the Makefile. They can
 	// be used when all ninja variables have already been evaluated through
@@ -108,6 +82,48 @@
 	DistForGoalsWithFilename(goals []string, path Path, filename string)
 }
 
+// MakeVarsContext contains the set of functions available for MakeVarsProvider
+// and SingletonMakeVarsProvider implementations.
+type MakeVarsContext interface {
+	BaseMakeVarsContext
+
+	ModuleName(module blueprint.Module) string
+	ModuleDir(module blueprint.Module) string
+	ModuleSubDir(module blueprint.Module) string
+	ModuleType(module blueprint.Module) string
+	BlueprintFile(module blueprint.Module) string
+
+	ModuleErrorf(module blueprint.Module, format string, args ...interface{})
+	Errorf(format string, args ...interface{})
+
+	VisitAllModules(visit func(Module))
+	VisitAllModulesIf(pred func(Module) bool, visit func(Module))
+
+	// Verify the make variable matches the Soong version, fail the build
+	// if it does not. If the make variable is empty, just set it.
+	Strict(name, ninjaStr string)
+	// Check to see if the make variable matches the Soong version, warn if
+	// it does not. If the make variable is empty, just set it.
+	Check(name, ninjaStr string)
+
+	// These are equivalent to the above, but sort the make and soong
+	// variables before comparing them. They also show the unique entries
+	// in each list when displaying the difference, instead of the entire
+	// string.
+	StrictSorted(name, ninjaStr string)
+	CheckSorted(name, ninjaStr string)
+
+	// Evaluates a ninja string and returns the result. Used if more
+	// complicated modification needs to happen before giving it to Make.
+	Eval(ninjaStr string) (string, error)
+}
+
+// MakeVarsModuleContext contains the set of functions available for modules
+// implementing the ModuleMakeVarsProvider interface.
+type MakeVarsModuleContext interface {
+	BaseMakeVarsContext
+}
+
 var _ PathContext = MakeVarsContext(nil)
 
 type MakeVarsProvider func(ctx MakeVarsContext)
@@ -135,6 +151,14 @@
 	return func(ctx MakeVarsContext) { singleton.MakeVars(ctx) }
 }
 
+// ModuleMakeVarsProvider is a Module with an extra method to provide extra values to be exported to Make.
+type ModuleMakeVarsProvider interface {
+	Module
+
+	// MakeVars uses a MakeVarsModuleContext to provide extra values to be exported to Make.
+	MakeVars(ctx MakeVarsModuleContext)
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 func makeVarsSingletonFunc() Singleton {
@@ -209,10 +233,45 @@
 		dists = append(dists, mctx.dists...)
 	}
 
+	ctx.VisitAllModules(func(m Module) {
+		if provider, ok := m.(ModuleMakeVarsProvider); ok {
+			mctx := &makeVarsContext{
+				SingletonContext: ctx,
+			}
+
+			provider.MakeVars(mctx)
+
+			vars = append(vars, mctx.vars...)
+			phonies = append(phonies, mctx.phonies...)
+			dists = append(dists, mctx.dists...)
+		}
+	})
+
 	if ctx.Failed() {
 		return
 	}
 
+	sort.Slice(vars, func(i, j int) bool {
+		return vars[i].name < vars[j].name
+	})
+	sort.Slice(phonies, func(i, j int) bool {
+		return phonies[i].name < phonies[j].name
+	})
+	lessArr := func(a, b []string) bool {
+		if len(a) == len(b) {
+			for i := range a {
+				if a[i] < b[i] {
+					return true
+				}
+			}
+			return false
+		}
+		return len(a) < len(b)
+	}
+	sort.Slice(dists, func(i, j int) bool {
+		return lessArr(dists[i].goals, dists[j].goals) || lessArr(dists[i].paths, dists[j].paths)
+	})
+
 	outBytes := s.writeVars(vars)
 
 	if err := pathtools.WriteFileIfChanged(outFile, outBytes, 0666); err != nil {
diff --git a/android/module.go b/android/module.go
index 2062a4d..6956167 100644
--- a/android/module.go
+++ b/android/module.go
@@ -256,6 +256,7 @@
 	InstallForceOS() *OsType
 	SkipInstall()
 	IsSkipInstall() bool
+	MakeUninstallable()
 	ExportedToMake() bool
 	InitRc() Paths
 	VintfFragments() Paths
@@ -548,6 +549,9 @@
 
 	SkipInstall bool `blueprint:"mutated"`
 
+	// Disabled by mutators. If set to true, it overrides Enabled property.
+	ForcedDisabled bool `blueprint:"mutated"`
+
 	NamespaceExportedToMake bool `blueprint:"mutated"`
 
 	MissingDeps []string `blueprint:"mutated"`
@@ -565,6 +569,12 @@
 type TaggedDistFiles map[string]Paths
 
 func MakeDefaultDistFiles(paths ...Path) TaggedDistFiles {
+	for _, path := range paths {
+		if path == nil {
+			panic("The path to a dist file cannot be nil.")
+		}
+	}
+
 	// The default OutputFile tag is the empty "" string.
 	return TaggedDistFiles{"": paths}
 }
@@ -1022,6 +1032,9 @@
 }
 
 func (m *ModuleBase) Enabled() bool {
+	if m.commonProperties.ForcedDisabled {
+		return false
+	}
 	if m.commonProperties.Enabled == nil {
 		return !m.Os().DefaultDisabled
 	}
@@ -1029,7 +1042,7 @@
 }
 
 func (m *ModuleBase) Disable() {
-	m.commonProperties.Enabled = proptools.BoolPtr(false)
+	m.commonProperties.ForcedDisabled = true
 }
 
 func (m *ModuleBase) SkipInstall() {
@@ -1040,6 +1053,15 @@
 	return m.commonProperties.SkipInstall == true
 }
 
+// Similar to SkipInstall, but if the AndroidMk entry would set
+// LOCAL_UNINSTALLABLE_MODULE then this variant may still output that entry
+// rather than leaving it out altogether. That happens in cases where it would
+// have other side effects, in particular when it adds a NOTICE file target,
+// which other install targets might depend on.
+func (m *ModuleBase) MakeUninstallable() {
+	m.SkipInstall()
+}
+
 func (m *ModuleBase) ExportedToMake() bool {
 	return m.commonProperties.NamespaceExportedToMake
 }
@@ -1833,7 +1855,7 @@
 
 // A regexp for removing boilerplate from BaseDependencyTag from the string representation of
 // a dependency tag.
-var tagCleaner = regexp.MustCompile(`\QBaseDependencyTag:blueprint.BaseDependencyTag{}\E(, )?`)
+var tagCleaner = regexp.MustCompile(`\QBaseDependencyTag:{}\E(, )?`)
 
 // PrettyPrintTag returns string representation of the tag, but prefers
 // custom String() method if available.
@@ -1844,7 +1866,7 @@
 	}
 
 	// Otherwise, get a default string representation of the tag's struct.
-	tagString := fmt.Sprintf("%#v", tag)
+	tagString := fmt.Sprintf("%T: %+v", tag, tag)
 
 	// Remove the boilerplate from BaseDependencyTag as it adds no value.
 	tagString = tagCleaner.ReplaceAllString(tagString, "")
diff --git a/android/neverallow.go b/android/neverallow.go
index 73829f1..aaea920 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -194,7 +194,7 @@
 		// This sometimes works because the APEX modules that contain derive_sdk and
 		// derive_sdk_prefer32 suppress the platform installation rules, but fails when
 		// the APEX modules contain the SDK variant and the platform variant still exists.
-		"frameworks/base/apex/sdkextensions/derive_sdk",
+		"packages/modules/SdkExtensions/derive_sdk",
 		// These are for apps and shouldn't be used by non-SDK variant modules.
 		"prebuilts/ndk",
 		"tools/test/graphicsbenchmark/apps/sample_app",
diff --git a/android/prebuilt_build_tool.go b/android/prebuilt_build_tool.go
new file mode 100644
index 0000000..1dcf199
--- /dev/null
+++ b/android/prebuilt_build_tool.go
@@ -0,0 +1,94 @@
+// 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 android
+
+func init() {
+	RegisterModuleType("prebuilt_build_tool", prebuiltBuildToolFactory)
+}
+
+type prebuiltBuildToolProperties struct {
+	// Source file to be executed for this build tool
+	Src *string `android:"path,arch_variant"`
+
+	// Extra files that should trigger rules using this tool to rebuild
+	Deps []string `android:"path,arch_variant"`
+
+	// Create a make variable with the specified name that contains the path to
+	// this prebuilt built tool, relative to the root of the source tree.
+	Export_to_make_var *string
+}
+
+type prebuiltBuildTool struct {
+	ModuleBase
+	prebuilt Prebuilt
+
+	properties prebuiltBuildToolProperties
+
+	toolPath OptionalPath
+}
+
+func (t *prebuiltBuildTool) Name() string {
+	return t.prebuilt.Name(t.ModuleBase.Name())
+}
+
+func (t *prebuiltBuildTool) Prebuilt() *Prebuilt {
+	return &t.prebuilt
+}
+
+func (t *prebuiltBuildTool) DepsMutator(ctx BottomUpMutatorContext) {
+	if t.properties.Src == nil {
+		ctx.PropertyErrorf("src", "missing prebuilt source file")
+	}
+}
+
+func (t *prebuiltBuildTool) GenerateAndroidBuildActions(ctx ModuleContext) {
+	sourcePath := t.prebuilt.SingleSourcePath(ctx)
+	installedPath := PathForModuleOut(ctx, t.ModuleBase.Name())
+	deps := PathsForModuleSrc(ctx, t.properties.Deps)
+
+	ctx.Build(pctx, BuildParams{
+		Rule:      Symlink,
+		Output:    installedPath,
+		Input:     sourcePath,
+		Implicits: deps,
+		Args: map[string]string{
+			"fromPath": "$$PWD/" + sourcePath.String(),
+		},
+	})
+
+	t.toolPath = OptionalPathForPath(installedPath)
+}
+
+func (t *prebuiltBuildTool) MakeVars(ctx MakeVarsModuleContext) {
+	if makeVar := String(t.properties.Export_to_make_var); makeVar != "" {
+		ctx.StrictRaw(makeVar, t.toolPath.String())
+	}
+}
+
+func (t *prebuiltBuildTool) HostToolPath() OptionalPath {
+	return t.toolPath
+}
+
+var _ HostToolProvider = &prebuiltBuildTool{}
+
+// prebuilt_build_tool is to declare prebuilts to be used during the build, particularly for use
+// in genrules with the "tools" property.
+func prebuiltBuildToolFactory() Module {
+	module := &prebuiltBuildTool{}
+	module.AddProperties(&module.properties)
+	InitSingleSourcePrebuiltModule(module, &module.properties, "Src")
+	InitAndroidArchModule(module, HostSupportedNoCross, MultilibFirst)
+	return module
+}
diff --git a/android/sdk.go b/android/sdk.go
index 28f5cd5..9ea7ff4 100644
--- a/android/sdk.go
+++ b/android/sdk.go
@@ -327,6 +327,12 @@
 	// SdkAware and be added with an SdkMemberTypeDependencyTag tag.
 	HasTransitiveSdkMembers() bool
 
+	// Return true if prebuilt host artifacts may be specific to the host OS. Only
+	// applicable to modules where HostSupported() is true. If this is true,
+	// snapshots will list each host OS variant explicitly and disable all other
+	// host OS'es.
+	IsHostOsDependent() bool
+
 	// Add dependencies from the SDK module to all the module variants the member
 	// type contributes to the SDK. `names` is the list of module names given in
 	// the member type property (as returned by SdkPropertyName()) in the SDK
@@ -389,6 +395,7 @@
 	PropertyName         string
 	SupportsSdk          bool
 	TransitiveSdkMembers bool
+	HostOsDependent      bool
 }
 
 func (b *SdkMemberTypeBase) SdkPropertyName() string {
@@ -403,6 +410,10 @@
 	return b.TransitiveSdkMembers
 }
 
+func (b *SdkMemberTypeBase) IsHostOsDependent() bool {
+	return b.HostOsDependent
+}
+
 // Encapsulates the information about registered SdkMemberTypes.
 type SdkMemberTypesRegistry struct {
 	// The list of types sorted by property name.
diff --git a/android/variable.go b/android/variable.go
index 5826138..1f21f34 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -214,30 +214,29 @@
 
 	AppsDefaultVersionName *string `json:",omitempty"`
 
-	Allow_missing_dependencies       *bool `json:",omitempty"`
-	Unbundled_build                  *bool `json:",omitempty"`
-	Unbundled_build_apps             *bool `json:",omitempty"`
-	Unbundled_build_sdks_from_source *bool `json:",omitempty"`
-	Malloc_not_svelte                *bool `json:",omitempty"`
-	Malloc_zero_contents             *bool `json:",omitempty"`
-	Malloc_pattern_fill_contents     *bool `json:",omitempty"`
-	Safestack                        *bool `json:",omitempty"`
-	HostStaticBinaries               *bool `json:",omitempty"`
-	Binder32bit                      *bool `json:",omitempty"`
-	UseGoma                          *bool `json:",omitempty"`
-	UseRBE                           *bool `json:",omitempty"`
-	UseRBEJAVAC                      *bool `json:",omitempty"`
-	UseRBER8                         *bool `json:",omitempty"`
-	UseRBED8                         *bool `json:",omitempty"`
-	Debuggable                       *bool `json:",omitempty"`
-	Eng                              *bool `json:",omitempty"`
-	Treble_linker_namespaces         *bool `json:",omitempty"`
-	Enforce_vintf_manifest           *bool `json:",omitempty"`
-	Pdk                              *bool `json:",omitempty"`
-	Uml                              *bool `json:",omitempty"`
-	Use_lmkd_stats_log               *bool `json:",omitempty"`
-	Arc                              *bool `json:",omitempty"`
-	MinimizeJavaDebugInfo            *bool `json:",omitempty"`
+	Allow_missing_dependencies   *bool `json:",omitempty"`
+	Unbundled_build              *bool `json:",omitempty"`
+	Unbundled_build_apps         *bool `json:",omitempty"`
+	Always_use_prebuilt_sdks     *bool `json:",omitempty"`
+	Malloc_not_svelte            *bool `json:",omitempty"`
+	Malloc_zero_contents         *bool `json:",omitempty"`
+	Malloc_pattern_fill_contents *bool `json:",omitempty"`
+	Safestack                    *bool `json:",omitempty"`
+	HostStaticBinaries           *bool `json:",omitempty"`
+	Binder32bit                  *bool `json:",omitempty"`
+	UseGoma                      *bool `json:",omitempty"`
+	UseRBE                       *bool `json:",omitempty"`
+	UseRBEJAVAC                  *bool `json:",omitempty"`
+	UseRBER8                     *bool `json:",omitempty"`
+	UseRBED8                     *bool `json:",omitempty"`
+	Debuggable                   *bool `json:",omitempty"`
+	Eng                          *bool `json:",omitempty"`
+	Treble_linker_namespaces     *bool `json:",omitempty"`
+	Enforce_vintf_manifest       *bool `json:",omitempty"`
+	Uml                          *bool `json:",omitempty"`
+	Use_lmkd_stats_log           *bool `json:",omitempty"`
+	Arc                          *bool `json:",omitempty"`
+	MinimizeJavaDebugInfo        *bool `json:",omitempty"`
 
 	Check_elf_files *bool `json:",omitempty"`
 
@@ -344,6 +343,9 @@
 	InstallExtraFlattenedApexes *bool `json:",omitempty"`
 
 	BoardUsesRecoveryAsBoot *bool `json:",omitempty"`
+
+	BoardKernelBinaries                []string `json:",omitempty"`
+	BoardKernelModuleInterfaceVersions []string `json:",omitempty"`
 }
 
 func boolPtr(v bool) *bool {
diff --git a/apex/androidmk.go b/apex/androidmk.go
index 082aa60..5c6d6cc 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -214,7 +214,7 @@
 			if !ok {
 				panic(fmt.Sprintf("Expected %s to be AndroidAppSet", fi.module))
 			}
-			fmt.Fprintln(w, "LOCAL_APK_SET_MASTER_FILE :=", as.MasterFile())
+			fmt.Fprintln(w, "LOCAL_APK_SET_INSTALL_FILE :=", as.InstallFile())
 			fmt.Fprintln(w, "LOCAL_APKCERTS_FILE :=", as.APKCertsFile().String())
 			fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_android_app_set.mk")
 		case nativeSharedLib, nativeExecutable, nativeTest:
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 0959a49..d13ec5f 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -236,13 +236,15 @@
 	ctx.PreArchMutators(android.RegisterComponentsMutator)
 	ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators)
 
-	cc.RegisterRequiredBuildComponentsForTest(ctx)
+	android.RegisterPrebuiltMutators(ctx)
 
-	// Register this after the prebuilt mutators have been registered (in
-	// cc.RegisterRequiredBuildComponentsForTest) to match what happens at runtime.
+	// Register these after the prebuilt mutators have been registered to match what
+	// happens at runtime.
 	ctx.PreArchMutators(android.RegisterVisibilityRuleGatherer)
 	ctx.PostDepsMutators(android.RegisterVisibilityRuleEnforcer)
 
+	cc.RegisterRequiredBuildComponentsForTest(ctx)
+
 	ctx.RegisterModuleType("cc_test", cc.TestFactory)
 	ctx.RegisterModuleType("vndk_prebuilt_shared", cc.VndkPrebuiltSharedFactory)
 	ctx.RegisterModuleType("vndk_libraries_txt", cc.VndkLibrariesTxtFactory)
@@ -412,7 +414,14 @@
 			system_shared_libs: [],
 			static_executable: true,
 			stl: "none",
-			apex_available: [ "myapex" ],
+			apex_available: [ "myapex", "com.android.gki.*" ],
+		}
+
+		apex {
+			name: "com.android.gki.fake",
+			binaries: ["foo"],
+			key: "myapex.key",
+			file_contexts: ":myapex-file_contexts",
 		}
 
 		cc_library_shared {
@@ -4441,11 +4450,11 @@
 func TestApexAvailable_IndirectDep(t *testing.T) {
 	// libbbaz is an indirect dep
 	testApexError(t, `requires "libbaz" that is not available for the APEX. Dependency path:
-.*via tag apex\.dependencyTag.*"sharedLib".*
+.*via tag apex\.dependencyTag.*name:sharedLib.*
 .*-> libfoo.*link:shared.*
-.*via tag cc\.DependencyTag.*"shared".*
+.*via tag cc\.libraryDependencyTag.*Kind:sharedLibraryDependency.*
 .*-> libbar.*link:shared.*
-.*via tag cc\.DependencyTag.*"shared".*
+.*via tag cc\.libraryDependencyTag.*Kind:sharedLibraryDependency.*
 .*-> libbaz.*link:shared.*`, `
 	apex {
 		name: "myapex",
@@ -5542,6 +5551,7 @@
 	ctx.RegisterModuleType("apex_key", ApexKeyFactory)
 	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
 	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
+	android.RegisterPrebuiltMutators(ctx)
 	cc.RegisterRequiredBuildComponentsForTest(ctx)
 	java.RegisterJavaBuildComponents(ctx)
 	java.RegisterSystemModulesBuildComponents(ctx)
@@ -5592,13 +5602,15 @@
 }
 
 func TestNoUpdatableJarsInBootImage(t *testing.T) {
-
 	var err string
 	var transform func(*dexpreopt.GlobalConfig)
 
+	config := android.TestArchConfig(buildDir, nil, "", nil)
+	ctx := android.PathContextForTesting(config)
+
 	t.Run("updatable jar from ART apex in the ART boot image => ok", func(t *testing.T) {
 		transform = func(config *dexpreopt.GlobalConfig) {
-			config.ArtApexJars = []string{"com.android.art.something:some-art-lib"}
+			config.ArtApexJars = android.CreateConfiguredJarList(ctx, []string{"com.android.art.something:some-art-lib"})
 		}
 		testNoUpdatableJarsInBootImage(t, "", transform)
 	})
@@ -5606,7 +5618,7 @@
 	t.Run("updatable jar from ART apex in the framework boot image => error", func(t *testing.T) {
 		err = "module 'some-art-lib' from updatable apex 'com.android.art.something' is not allowed in the framework boot image"
 		transform = func(config *dexpreopt.GlobalConfig) {
-			config.BootJars = []string{"com.android.art.something:some-art-lib"}
+			config.BootJars = android.CreateConfiguredJarList(ctx, []string{"com.android.art.something:some-art-lib"})
 		}
 		testNoUpdatableJarsInBootImage(t, err, transform)
 	})
@@ -5614,7 +5626,7 @@
 	t.Run("updatable jar from some other apex in the ART boot image => error", func(t *testing.T) {
 		err = "module 'some-updatable-apex-lib' from updatable apex 'some-updatable-apex' is not allowed in the ART boot image"
 		transform = func(config *dexpreopt.GlobalConfig) {
-			config.ArtApexJars = []string{"some-updatable-apex:some-updatable-apex-lib"}
+			config.ArtApexJars = android.CreateConfiguredJarList(ctx, []string{"some-updatable-apex:some-updatable-apex-lib"})
 		}
 		testNoUpdatableJarsInBootImage(t, err, transform)
 	})
@@ -5622,7 +5634,7 @@
 	t.Run("non-updatable jar from some other apex in the ART boot image => error", func(t *testing.T) {
 		err = "module 'some-non-updatable-apex-lib' is not allowed in the ART boot image"
 		transform = func(config *dexpreopt.GlobalConfig) {
-			config.ArtApexJars = []string{"some-non-updatable-apex:some-non-updatable-apex-lib"}
+			config.ArtApexJars = android.CreateConfiguredJarList(ctx, []string{"some-non-updatable-apex:some-non-updatable-apex-lib"})
 		}
 		testNoUpdatableJarsInBootImage(t, err, transform)
 	})
@@ -5630,14 +5642,14 @@
 	t.Run("updatable jar from some other apex in the framework boot image => error", func(t *testing.T) {
 		err = "module 'some-updatable-apex-lib' from updatable apex 'some-updatable-apex' is not allowed in the framework boot image"
 		transform = func(config *dexpreopt.GlobalConfig) {
-			config.BootJars = []string{"some-updatable-apex:some-updatable-apex-lib"}
+			config.BootJars = android.CreateConfiguredJarList(ctx, []string{"some-updatable-apex:some-updatable-apex-lib"})
 		}
 		testNoUpdatableJarsInBootImage(t, err, transform)
 	})
 
 	t.Run("non-updatable jar from some other apex in the framework boot image => ok", func(t *testing.T) {
 		transform = func(config *dexpreopt.GlobalConfig) {
-			config.BootJars = []string{"some-non-updatable-apex:some-non-updatable-apex-lib"}
+			config.BootJars = android.CreateConfiguredJarList(ctx, []string{"some-non-updatable-apex:some-non-updatable-apex-lib"})
 		}
 		testNoUpdatableJarsInBootImage(t, "", transform)
 	})
@@ -5645,7 +5657,7 @@
 	t.Run("nonexistent jar in the ART boot image => error", func(t *testing.T) {
 		err = "failed to find a dex jar path for module 'nonexistent'"
 		transform = func(config *dexpreopt.GlobalConfig) {
-			config.ArtApexJars = []string{"platform:nonexistent"}
+			config.ArtApexJars = android.CreateConfiguredJarList(ctx, []string{"platform:nonexistent"})
 		}
 		testNoUpdatableJarsInBootImage(t, err, transform)
 	})
@@ -5653,7 +5665,7 @@
 	t.Run("nonexistent jar in the framework boot image => error", func(t *testing.T) {
 		err = "failed to find a dex jar path for module 'nonexistent'"
 		transform = func(config *dexpreopt.GlobalConfig) {
-			config.BootJars = []string{"platform:nonexistent"}
+			config.BootJars = android.CreateConfiguredJarList(ctx, []string{"platform:nonexistent"})
 		}
 		testNoUpdatableJarsInBootImage(t, err, transform)
 	})
@@ -5661,14 +5673,14 @@
 	t.Run("platform jar in the ART boot image => error", func(t *testing.T) {
 		err = "module 'some-platform-lib' is not allowed in the ART boot image"
 		transform = func(config *dexpreopt.GlobalConfig) {
-			config.ArtApexJars = []string{"platform:some-platform-lib"}
+			config.ArtApexJars = android.CreateConfiguredJarList(ctx, []string{"platform:some-platform-lib"})
 		}
 		testNoUpdatableJarsInBootImage(t, err, transform)
 	})
 
 	t.Run("platform jar in the framework boot image => ok", func(t *testing.T) {
 		transform = func(config *dexpreopt.GlobalConfig) {
-			config.BootJars = []string{"platform:some-platform-lib"}
+			config.BootJars = android.CreateConfiguredJarList(ctx, []string{"platform:some-platform-lib"})
 		}
 		testNoUpdatableJarsInBootImage(t, "", transform)
 	})
diff --git a/bpfix/bpfix/bpfix.go b/bpfix/bpfix/bpfix.go
index 689cbd1..faec473 100644
--- a/bpfix/bpfix/bpfix.go
+++ b/bpfix/bpfix/bpfix.go
@@ -128,6 +128,10 @@
 		Name: "removeSoongConfigBoolVariable",
 		Fix:  removeSoongConfigBoolVariable,
 	},
+	{
+		Name: "removePdkProperty",
+		Fix:  runPatchListMod(removePdkProperty),
+	},
 }
 
 func NewFixRequest() FixRequest {
@@ -993,6 +997,25 @@
 	return patchlist.Add(prop.Pos().Offset, prop.End().Offset+2, replaceStr)
 }
 
+func removePdkProperty(mod *parser.Module, buf []byte, patchlist *parser.PatchList) error {
+	prop, ok := mod.GetProperty("product_variables")
+	if !ok {
+		return nil
+	}
+	propMap, ok := prop.Value.(*parser.Map)
+	if !ok {
+		return nil
+	}
+	pdkProp, ok := propMap.GetProperty("pdk")
+	if !ok {
+		return nil
+	}
+	if len(propMap.Properties) > 1 {
+		return patchlist.Add(pdkProp.Pos().Offset, pdkProp.End().Offset+2, "")
+	}
+	return patchlist.Add(prop.Pos().Offset, prop.End().Offset+2, "")
+}
+
 func mergeMatchingModuleProperties(mod *parser.Module, buf []byte, patchlist *parser.PatchList) error {
 	return mergeMatchingProperties(&mod.Properties, buf, patchlist)
 }
diff --git a/bpfix/bpfix/bpfix_test.go b/bpfix/bpfix/bpfix_test.go
index 8988177..ef9814f 100644
--- a/bpfix/bpfix/bpfix_test.go
+++ b/bpfix/bpfix/bpfix_test.go
@@ -998,3 +998,61 @@
 		})
 	}
 }
+
+func TestRemovePdkProperty(t *testing.T) {
+	tests := []struct {
+		name string
+		in   string
+		out  string
+	}{
+		{
+			name: "remove property",
+			in: `
+				cc_library_shared {
+					name: "foo",
+					product_variables: {
+						other: {
+							bar: true,
+						},
+						pdk: {
+							enabled: false,
+						},
+					},
+				}
+			`,
+			out: `
+				cc_library_shared {
+					name: "foo",
+					product_variables: {
+						other: {
+							bar: true,
+						},
+					},
+				}
+			`,
+		},
+		{
+			name: "remove property and empty product_variables",
+			in: `
+				cc_library_shared {
+					name: "foo",
+					product_variables: {
+						pdk: {
+							enabled: false,
+						},
+					},
+				}
+			`,
+			out: `
+				cc_library_shared {
+					name: "foo",
+				}
+			`,
+		},
+	}
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			runPass(t, test.in, test.out, runPatchListMod(removePdkProperty))
+		})
+	}
+}
diff --git a/cc/Android.bp b/cc/Android.bp
index 9ece05f..831911e 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -19,6 +19,7 @@
         "check.go",
         "coverage.go",
         "gen.go",
+        "image.go",
         "linkable.go",
         "lto.go",
         "makevars.go",
diff --git a/cc/androidmk.go b/cc/androidmk.go
index e91b40a..7c747c8 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -286,10 +286,8 @@
 			entries.SubName = "." + library.stubsVersion()
 		}
 		entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
-			// Note library.skipInstall() has a special case to get here for static
-			// libraries that otherwise would have skipped installation and hence not
-			// have executed AndroidMkEntries at all. The reason is to ensure they get
-			// a NOTICE file make target which other libraries might depend on.
+			// library.makeUninstallable() depends on this to bypass SkipInstall() for
+			// static libraries.
 			entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
 			if library.buildStubs() {
 				entries.SetBool("LOCAL_NO_NOTICE_FILE", true)
@@ -518,10 +516,14 @@
 		entries.Class = "HEADER_LIBRARIES"
 	}
 
+	entries.SubName = ""
+
+	if c.sanitizerProperties.CfiEnabled {
+		entries.SubName += ".cfi"
+	}
+
 	if c.androidMkVendorSuffix {
-		entries.SubName = vendorSuffix
-	} else {
-		entries.SubName = ""
+		entries.SubName += vendorSuffix
 	}
 
 	entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
diff --git a/cc/binary_sdk_member.go b/cc/binary_sdk_member.go
index 51d8b4e..337de55 100644
--- a/cc/binary_sdk_member.go
+++ b/cc/binary_sdk_member.go
@@ -29,7 +29,8 @@
 
 var ccBinarySdkMemberType = &binarySdkMemberType{
 	SdkMemberTypeBase: android.SdkMemberTypeBase{
-		PropertyName: "native_binaries",
+		PropertyName:    "native_binaries",
+		HostOsDependent: true,
 	},
 }
 
diff --git a/cc/cc.go b/cc/cc.go
index 962da4c..6f1a06d 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -418,51 +418,137 @@
 	inSanitizerDir() bool
 	hostToolPath() android.OptionalPath
 	relativeInstallPath() string
-	skipInstall(mod *Module)
+	makeUninstallable(mod *Module)
 }
 
 type xref interface {
 	XrefCcFiles() android.Paths
 }
 
+type libraryDependencyKind int
+
+const (
+	headerLibraryDependency = iota
+	sharedLibraryDependency
+	staticLibraryDependency
+)
+
+func (k libraryDependencyKind) String() string {
+	switch k {
+	case headerLibraryDependency:
+		return "headerLibraryDependency"
+	case sharedLibraryDependency:
+		return "sharedLibraryDependency"
+	case staticLibraryDependency:
+		return "staticLibraryDependency"
+	default:
+		panic(fmt.Errorf("unknown libraryDependencyKind %d", k))
+	}
+}
+
+type libraryDependencyOrder int
+
+const (
+	earlyLibraryDependency  = -1
+	normalLibraryDependency = 0
+	lateLibraryDependency   = 1
+)
+
+func (o libraryDependencyOrder) String() string {
+	switch o {
+	case earlyLibraryDependency:
+		return "earlyLibraryDependency"
+	case normalLibraryDependency:
+		return "normalLibraryDependency"
+	case lateLibraryDependency:
+		return "lateLibraryDependency"
+	default:
+		panic(fmt.Errorf("unknown libraryDependencyOrder %d", o))
+	}
+}
+
+// libraryDependencyTag is used to tag dependencies on libraries.  Unlike many dependency
+// tags that have a set of predefined tag objects that are reused for each dependency, a
+// libraryDependencyTag is designed to contain extra metadata and is constructed as needed.
+// That means that comparing a libraryDependencyTag for equality will only be equal if all
+// of the metadata is equal.  Most usages will want to type assert to libraryDependencyTag and
+// then check individual metadata fields instead.
+type libraryDependencyTag struct {
+	blueprint.BaseDependencyTag
+
+	// These are exported so that fmt.Printf("%#v") can call their String methods.
+	Kind  libraryDependencyKind
+	Order libraryDependencyOrder
+
+	wholeStatic bool
+
+	reexportFlags       bool
+	explicitlyVersioned bool
+	dataLib             bool
+	ndk                 bool
+
+	staticUnwinder bool
+
+	makeSuffix string
+}
+
+// header returns true if the libraryDependencyTag is tagging a header lib dependency.
+func (d libraryDependencyTag) header() bool {
+	return d.Kind == headerLibraryDependency
+}
+
+// shared returns true if the libraryDependencyTag is tagging a shared lib dependency.
+func (d libraryDependencyTag) shared() bool {
+	return d.Kind == sharedLibraryDependency
+}
+
+// shared returns true if the libraryDependencyTag is tagging a static lib dependency.
+func (d libraryDependencyTag) static() bool {
+	return d.Kind == staticLibraryDependency
+}
+
+// dependencyTag is used for tagging miscellanous dependency types that don't fit into
+// libraryDependencyTag.  Each tag object is created globally and reused for multiple
+// dependencies (although since the object contains no references, assigning a tag to a
+// variable and modifying it will not modify the original).  Users can compare the tag
+// returned by ctx.OtherModuleDependencyTag against the global original
+type dependencyTag struct {
+	blueprint.BaseDependencyTag
+	name string
+}
+
 var (
-	dataLibDepTag         = DependencyTag{Name: "data_lib", Library: true, Shared: true}
-	sharedExportDepTag    = DependencyTag{Name: "shared", Library: true, Shared: true, ReexportFlags: true}
-	earlySharedDepTag     = DependencyTag{Name: "early_shared", Library: true, Shared: true}
-	lateSharedDepTag      = DependencyTag{Name: "late shared", Library: true, Shared: true}
-	staticExportDepTag    = DependencyTag{Name: "static", Library: true, ReexportFlags: true}
-	lateStaticDepTag      = DependencyTag{Name: "late static", Library: true}
-	staticUnwinderDepTag  = DependencyTag{Name: "static unwinder", Library: true}
-	wholeStaticDepTag     = DependencyTag{Name: "whole static", Library: true, ReexportFlags: true}
-	headerDepTag          = DependencyTag{Name: "header", Library: true}
-	headerExportDepTag    = DependencyTag{Name: "header", Library: true, ReexportFlags: true}
-	genSourceDepTag       = DependencyTag{Name: "gen source"}
-	genHeaderDepTag       = DependencyTag{Name: "gen header"}
-	genHeaderExportDepTag = DependencyTag{Name: "gen header", ReexportFlags: true}
-	objDepTag             = DependencyTag{Name: "obj"}
-	linkerFlagsDepTag     = DependencyTag{Name: "linker flags file"}
-	dynamicLinkerDepTag   = DependencyTag{Name: "dynamic linker"}
-	reuseObjTag           = DependencyTag{Name: "reuse objects"}
-	staticVariantTag      = DependencyTag{Name: "static variant"}
-	ndkStubDepTag         = DependencyTag{Name: "ndk stub", Library: true}
-	ndkLateStubDepTag     = DependencyTag{Name: "ndk late stub", Library: true}
-	vndkExtDepTag         = DependencyTag{Name: "vndk extends", Library: true}
-	runtimeDepTag         = DependencyTag{Name: "runtime lib"}
-	testPerSrcDepTag      = DependencyTag{Name: "test_per_src"}
+	genSourceDepTag       = dependencyTag{name: "gen source"}
+	genHeaderDepTag       = dependencyTag{name: "gen header"}
+	genHeaderExportDepTag = dependencyTag{name: "gen header export"}
+	objDepTag             = dependencyTag{name: "obj"}
+	linkerFlagsDepTag     = dependencyTag{name: "linker flags file"}
+	dynamicLinkerDepTag   = dependencyTag{name: "dynamic linker"}
+	reuseObjTag           = dependencyTag{name: "reuse objects"}
+	staticVariantTag      = dependencyTag{name: "static variant"}
+	vndkExtDepTag         = dependencyTag{name: "vndk extends"}
+	dataLibDepTag         = dependencyTag{name: "data lib"}
+	runtimeDepTag         = dependencyTag{name: "runtime lib"}
+	testPerSrcDepTag      = dependencyTag{name: "test_per_src"}
 )
 
 func IsSharedDepTag(depTag blueprint.DependencyTag) bool {
-	ccDepTag, ok := depTag.(DependencyTag)
-	return ok && ccDepTag.Shared
+	ccLibDepTag, ok := depTag.(libraryDependencyTag)
+	return ok && ccLibDepTag.shared()
+}
+
+func IsStaticDepTag(depTag blueprint.DependencyTag) bool {
+	ccLibDepTag, ok := depTag.(libraryDependencyTag)
+	return ok && ccLibDepTag.static()
 }
 
 func IsRuntimeDepTag(depTag blueprint.DependencyTag) bool {
-	ccDepTag, ok := depTag.(DependencyTag)
+	ccDepTag, ok := depTag.(dependencyTag)
 	return ok && ccDepTag == runtimeDepTag
 }
 
 func IsTestPerSrcDepTag(depTag blueprint.DependencyTag) bool {
-	ccDepTag, ok := depTag.(DependencyTag)
+	ccDepTag, ok := depTag.(dependencyTag)
 	return ok && ccDepTag == testPerSrcDepTag
 }
 
@@ -941,48 +1027,6 @@
 	return ""
 }
 
-// Returns true only when this module is configured to have core, product and vendor
-// variants.
-func (c *Module) HasVendorVariant() bool {
-	return c.IsVndk() || Bool(c.VendorProperties.Vendor_available)
-}
-
-const (
-	// VendorVariationPrefix is the variant prefix used for /vendor code that compiles
-	// against the VNDK.
-	VendorVariationPrefix = "vendor."
-
-	// ProductVariationPrefix is the variant prefix used for /product code that compiles
-	// against the VNDK.
-	ProductVariationPrefix = "product."
-)
-
-// Returns true if the module is "product" variant. Usually these modules are installed in /product
-func (c *Module) inProduct() bool {
-	return c.Properties.ImageVariationPrefix == ProductVariationPrefix
-}
-
-// Returns true if the module is "vendor" variant. Usually these modules are installed in /vendor
-func (c *Module) inVendor() bool {
-	return c.Properties.ImageVariationPrefix == VendorVariationPrefix
-}
-
-func (c *Module) InRamdisk() bool {
-	return c.ModuleBase.InRamdisk() || c.ModuleBase.InstallInRamdisk()
-}
-
-func (c *Module) InRecovery() bool {
-	return c.ModuleBase.InRecovery() || c.ModuleBase.InstallInRecovery()
-}
-
-func (c *Module) OnlyInRamdisk() bool {
-	return c.ModuleBase.InstallInRamdisk()
-}
-
-func (c *Module) OnlyInRecovery() bool {
-	return c.ModuleBase.InstallInRecovery()
-}
-
 func (c *Module) IsStubs() bool {
 	if library, ok := c.linker.(*libraryDecorator); ok {
 		return library.buildStubs()
@@ -1090,16 +1134,6 @@
 	moduleContextImpl
 }
 
-func (ctx *moduleContext) ProductSpecific() bool {
-	return ctx.ModuleContext.ProductSpecific() ||
-		(ctx.mod.HasVendorVariant() && ctx.mod.inProduct() && !ctx.mod.IsVndk())
-}
-
-func (ctx *moduleContext) SocSpecific() bool {
-	return ctx.ModuleContext.SocSpecific() ||
-		(ctx.mod.HasVendorVariant() && ctx.mod.inVendor() && !ctx.mod.IsVndk())
-}
-
 type moduleContextImpl struct {
 	mod *Module
 	ctx BaseModuleContext
@@ -1195,22 +1229,6 @@
 	return ctx.mod.MustUseVendorVariant()
 }
 
-func (ctx *moduleContextImpl) inProduct() bool {
-	return ctx.mod.inProduct()
-}
-
-func (ctx *moduleContextImpl) inVendor() bool {
-	return ctx.mod.inVendor()
-}
-
-func (ctx *moduleContextImpl) inRamdisk() bool {
-	return ctx.mod.InRamdisk()
-}
-
-func (ctx *moduleContextImpl) inRecovery() bool {
-	return ctx.mod.InRecovery()
-}
-
 // Check whether ABI dumps should be created for this module.
 func (ctx *moduleContextImpl) shouldCreateSourceAbiDump() bool {
 	if ctx.ctx.Config().IsEnvTrue("SKIP_ABI_CHECKS") {
@@ -1859,9 +1877,9 @@
 
 	vendorSnapshotHeaderLibs := vendorSnapshotHeaderLibs(actx.Config())
 	for _, lib := range deps.HeaderLibs {
-		depTag := headerDepTag
+		depTag := libraryDependencyTag{Kind: headerLibraryDependency}
 		if inList(lib, deps.ReexportHeaderLibHeaders) {
-			depTag = headerExportDepTag
+			depTag.reexportFlags = true
 		}
 
 		lib = rewriteSnapshotLibs(lib, vendorSnapshotHeaderLibs)
@@ -1884,7 +1902,7 @@
 	vendorSnapshotStaticLibs := vendorSnapshotStaticLibs(actx.Config())
 
 	for _, lib := range deps.WholeStaticLibs {
-		depTag := wholeStaticDepTag
+		depTag := libraryDependencyTag{Kind: staticLibraryDependency, wholeStatic: true, reexportFlags: true}
 		if impl, ok := syspropImplLibraries[lib]; ok {
 			lib = impl
 		}
@@ -1897,9 +1915,9 @@
 	}
 
 	for _, lib := range deps.StaticLibs {
-		depTag := StaticDepTag
+		depTag := libraryDependencyTag{Kind: staticLibraryDependency}
 		if inList(lib, deps.ReexportStaticLibHeaders) {
-			depTag = staticExportDepTag
+			depTag.reexportFlags = true
 		}
 
 		if impl, ok := syspropImplLibraries[lib]; ok {
@@ -1917,24 +1935,26 @@
 	// so that native libraries/binaries are linked with static unwinder
 	// because Q libc doesn't have unwinder APIs
 	if deps.StaticUnwinderIfLegacy {
+		depTag := libraryDependencyTag{Kind: staticLibraryDependency, staticUnwinder: true}
 		actx.AddVariationDependencies([]blueprint.Variation{
 			{Mutator: "link", Variation: "static"},
-		}, staticUnwinderDepTag, rewriteSnapshotLibs(staticUnwinder(actx), vendorSnapshotStaticLibs))
+		}, depTag, rewriteSnapshotLibs(staticUnwinder(actx), vendorSnapshotStaticLibs))
 	}
 
 	for _, lib := range deps.LateStaticLibs {
+		depTag := libraryDependencyTag{Kind: staticLibraryDependency, Order: lateLibraryDependency}
 		actx.AddVariationDependencies([]blueprint.Variation{
 			{Mutator: "link", Variation: "static"},
-		}, lateStaticDepTag, rewriteSnapshotLibs(lib, vendorSnapshotStaticLibs))
+		}, depTag, rewriteSnapshotLibs(lib, vendorSnapshotStaticLibs))
 	}
 
-	addSharedLibDependencies := func(depTag DependencyTag, name string, version string) {
+	addSharedLibDependencies := func(depTag libraryDependencyTag, name string, version string) {
 		var variations []blueprint.Variation
 		variations = append(variations, blueprint.Variation{Mutator: "link", Variation: "shared"})
 		if version != "" && VersionVariantAvailable(c) {
 			// Version is explicitly specified. i.e. libFoo#30
 			variations = append(variations, blueprint.Variation{Mutator: "version", Variation: version})
-			depTag.ExplicitlyVersioned = true
+			depTag.explicitlyVersioned = true
 		}
 		actx.AddVariationDependencies(variations, depTag, name)
 
@@ -1956,12 +1976,9 @@
 	var sharedLibNames []string
 
 	for _, lib := range deps.SharedLibs {
-		depTag := SharedDepTag
-		if c.static() {
-			depTag = SharedFromStaticDepTag
-		}
+		depTag := libraryDependencyTag{Kind: sharedLibraryDependency}
 		if inList(lib, deps.ReexportSharedLibHeaders) {
-			depTag = sharedExportDepTag
+			depTag.reexportFlags = true
 		}
 
 		if impl, ok := syspropImplLibraries[lib]; ok {
@@ -1981,7 +1998,8 @@
 			// linking against both the stubs lib and the non-stubs lib at the same time.
 			continue
 		}
-		addSharedLibDependencies(lateSharedDepTag, lib, "")
+		depTag := libraryDependencyTag{Kind: sharedLibraryDependency, Order: lateLibraryDependency}
+		addSharedLibDependencies(depTag, lib, "")
 	}
 
 	actx.AddVariationDependencies([]blueprint.Variation{
@@ -2020,10 +2038,14 @@
 	}
 
 	version := ctx.sdkVersion()
+
+	ndkStubDepTag := libraryDependencyTag{Kind: sharedLibraryDependency, ndk: true, makeSuffix: "." + version}
 	actx.AddVariationDependencies([]blueprint.Variation{
 		{Mutator: "ndk_api", Variation: version},
 		{Mutator: "link", Variation: "shared"},
 	}, ndkStubDepTag, variantNdkLibs...)
+
+	ndkLateStubDepTag := libraryDependencyTag{Kind: sharedLibraryDependency, Order: lateLibraryDependency, ndk: true, makeSuffix: "." + version}
 	actx.AddVariationDependencies([]blueprint.Variation{
 		{Mutator: "ndk_api", Variation: version},
 		{Mutator: "link", Variation: "shared"},
@@ -2047,7 +2069,19 @@
 
 // Whether a module can link to another module, taking into
 // account NDK linking.
-func checkLinkType(ctx android.ModuleContext, from LinkableInterface, to LinkableInterface, tag DependencyTag) {
+func checkLinkType(ctx android.ModuleContext, from LinkableInterface, to LinkableInterface,
+	tag blueprint.DependencyTag) {
+
+	switch t := tag.(type) {
+	case dependencyTag:
+		if t != vndkExtDepTag {
+			return
+		}
+	case libraryDependencyTag:
+	default:
+		return
+	}
+
 	if from.Module().Target().Os != android.Android {
 		// Host code is not restricted
 		return
@@ -2205,8 +2239,6 @@
 	directStaticDeps := []LinkableInterface{}
 	directSharedDeps := []LinkableInterface{}
 
-	vendorPublicLibraries := vendorPublicLibraries(ctx.Config())
-
 	reexportExporter := func(exporter exportedFlagsProducer) {
 		depPaths.ReexportedDirs = append(depPaths.ReexportedDirs, exporter.exportedDirs()...)
 		depPaths.ReexportedSystemDirs = append(depPaths.ReexportedSystemDirs, exporter.exportedSystemDirs()...)
@@ -2316,39 +2348,24 @@
 			}
 		}
 
-		if depTag == staticUnwinderDepTag {
-			// Use static unwinder for legacy (min_sdk_version = 29) apexes (b/144430859)
-			if c.apexSdkVersion <= android.SdkVersion_Android10 {
-				depTag = StaticDepTag
-			} else {
+		checkLinkType(ctx, c, ccDep, depTag)
+
+		linkFile := ccDep.OutputFile()
+
+		if libDepTag, ok := depTag.(libraryDependencyTag); ok {
+			// Only use static unwinder for legacy (min_sdk_version = 29) apexes (b/144430859)
+			if libDepTag.staticUnwinder && c.apexSdkVersion > android.SdkVersion_Android10 {
 				return
 			}
-		}
 
-		// Extract ExplicitlyVersioned field from the depTag and reset it inside the struct.
-		// Otherwise, SharedDepTag and lateSharedDepTag with ExplicitlyVersioned set to true
-		// won't be matched to SharedDepTag and lateSharedDepTag.
-		explicitlyVersioned := false
-		if t, ok := depTag.(DependencyTag); ok {
-			explicitlyVersioned = t.ExplicitlyVersioned
-			t.ExplicitlyVersioned = false
-			depTag = t
-		}
-
-		if t, ok := depTag.(DependencyTag); ok && t.Library {
-			depIsStatic := false
-			switch depTag {
-			case StaticDepTag, staticExportDepTag, lateStaticDepTag, wholeStaticDepTag:
-				depIsStatic = true
-			}
-			if ccDep.CcLibrary() && !depIsStatic {
+			if ccDep.CcLibrary() && !libDepTag.static() {
 				depIsStubs := ccDep.BuildStubs()
 				depHasStubs := VersionVariantAvailable(c) && ccDep.HasStubsVariants()
 				depInSameApex := android.DirectlyInApex(c.ApexName(), depName)
 				depInPlatform := !android.DirectlyInAnyApex(ctx, depName)
 
 				var useThisDep bool
-				if depIsStubs && explicitlyVersioned {
+				if depIsStubs && libDepTag.explicitlyVersioned {
 					// Always respect dependency to the versioned stubs (i.e. libX#10)
 					useThisDep = true
 				} else if !depHasStubs {
@@ -2380,7 +2397,7 @@
 				}
 
 				// when to use (unspecified) stubs, check min_sdk_version and choose the right one
-				if useThisDep && depIsStubs && !explicitlyVersioned {
+				if useThisDep && depIsStubs && !libDepTag.explicitlyVersioned {
 					versionToUse, err := c.ChooseSdkVersion(ccDep.StubsVersions(), c.apexSdkVersion)
 					if err != nil {
 						ctx.OtherModuleErrorf(dep, err.Error())
@@ -2425,7 +2442,7 @@
 					depPaths.GeneratedDeps = append(depPaths.GeneratedDeps, i.exportedDeps()...)
 					depPaths.Flags = append(depPaths.Flags, i.exportedFlags()...)
 
-					if t.ReexportFlags {
+					if libDepTag.reexportFlags {
 						reexportExporter(i)
 						// Add these re-exported flags to help header-abi-dumper to infer the abi exported by a library.
 						// Re-exported shared library headers must be included as well since they can help us with type information
@@ -2437,210 +2454,178 @@
 					}
 				}
 			}
-			checkLinkType(ctx, c, ccDep, t)
-		}
 
-		var ptr *android.Paths
-		var depPtr *android.Paths
+			var ptr *android.Paths
+			var depPtr *android.Paths
 
-		linkFile := ccDep.OutputFile()
-		depFile := android.OptionalPath{}
+			depFile := android.OptionalPath{}
 
-		switch depTag {
-		case ndkStubDepTag, SharedDepTag, SharedFromStaticDepTag, sharedExportDepTag:
-			ptr = &depPaths.SharedLibs
-			depPtr = &depPaths.SharedLibsDeps
-			depFile = ccDep.Toc()
-			directSharedDeps = append(directSharedDeps, ccDep)
-
-		case earlySharedDepTag:
-			ptr = &depPaths.EarlySharedLibs
-			depPtr = &depPaths.EarlySharedLibsDeps
-			depFile = ccDep.Toc()
-			directSharedDeps = append(directSharedDeps, ccDep)
-		case lateSharedDepTag, ndkLateStubDepTag:
-			ptr = &depPaths.LateSharedLibs
-			depPtr = &depPaths.LateSharedLibsDeps
-			depFile = ccDep.Toc()
-		case StaticDepTag, staticExportDepTag:
-			ptr = nil
-			directStaticDeps = append(directStaticDeps, ccDep)
-		case lateStaticDepTag:
-			ptr = &depPaths.LateStaticLibs
-		case wholeStaticDepTag:
-			ptr = &depPaths.WholeStaticLibs
-			if !ccDep.CcLibraryInterface() || !ccDep.Static() {
-				ctx.ModuleErrorf("module %q not a static library", depName)
-				return
-			}
-
-			// Because the static library objects are included, this only makes sense
-			// in the context of proper cc.Modules.
-			if ccWholeStaticLib, ok := ccDep.(*Module); ok {
-				staticLib := ccWholeStaticLib.linker.(libraryInterface)
-				if missingDeps := staticLib.getWholeStaticMissingDeps(); missingDeps != nil {
-					postfix := " (required by " + ctx.OtherModuleName(dep) + ")"
-					for i := range missingDeps {
-						missingDeps[i] += postfix
-					}
-					ctx.AddMissingDependencies(missingDeps)
+			switch {
+			case libDepTag.header():
+				// nothing
+			case libDepTag.shared():
+				ptr = &depPaths.SharedLibs
+				switch libDepTag.Order {
+				case earlyLibraryDependency:
+					ptr = &depPaths.EarlySharedLibs
+					depPtr = &depPaths.EarlySharedLibsDeps
+				case normalLibraryDependency:
+					ptr = &depPaths.SharedLibs
+					depPtr = &depPaths.SharedLibsDeps
+					directSharedDeps = append(directSharedDeps, ccDep)
+				case lateLibraryDependency:
+					ptr = &depPaths.LateSharedLibs
+					depPtr = &depPaths.LateSharedLibsDeps
+				default:
+					panic(fmt.Errorf("unexpected library dependency order %d", libDepTag.Order))
 				}
-				if _, ok := ccWholeStaticLib.linker.(prebuiltLinkerInterface); ok {
-					depPaths.WholeStaticLibsFromPrebuilts = append(depPaths.WholeStaticLibsFromPrebuilts, linkFile.Path())
-				} else {
-					depPaths.WholeStaticLibObjs = depPaths.WholeStaticLibObjs.Append(staticLib.objs())
-				}
-			} else {
-				ctx.ModuleErrorf(
-					"non-cc.Modules cannot be included as whole static libraries.", depName)
-				return
-			}
-		case headerDepTag:
-			// Nothing
-		case objDepTag:
-			depPaths.Objs.objFiles = append(depPaths.Objs.objFiles, linkFile.Path())
-		case CrtBeginDepTag:
-			depPaths.CrtBegin = linkFile
-		case CrtEndDepTag:
-			depPaths.CrtEnd = linkFile
-		case dynamicLinkerDepTag:
-			depPaths.DynamicLinker = linkFile
-		}
-
-		switch depTag {
-		case StaticDepTag, staticExportDepTag, lateStaticDepTag:
-			if !ccDep.CcLibraryInterface() || !ccDep.Static() {
-				ctx.ModuleErrorf("module %q not a static library", depName)
-				return
-			}
-
-			// When combining coverage files for shared libraries and executables, coverage files
-			// in static libraries act as if they were whole static libraries. The same goes for
-			// source based Abi dump files.
-			if c, ok := ccDep.(*Module); ok {
-				staticLib := c.linker.(libraryInterface)
-				depPaths.StaticLibObjs.coverageFiles = append(depPaths.StaticLibObjs.coverageFiles,
-					staticLib.objs().coverageFiles...)
-				depPaths.StaticLibObjs.sAbiDumpFiles = append(depPaths.StaticLibObjs.sAbiDumpFiles,
-					staticLib.objs().sAbiDumpFiles...)
-			} else if c, ok := ccDep.(LinkableInterface); ok {
-				// Handle non-CC modules here
-				depPaths.StaticLibObjs.coverageFiles = append(depPaths.StaticLibObjs.coverageFiles,
-					c.CoverageFiles()...)
-			}
-		}
-
-		if ptr != nil {
-			if !linkFile.Valid() {
-				if !ctx.Config().AllowMissingDependencies() {
-					ctx.ModuleErrorf("module %q missing output file", depName)
-				} else {
-					ctx.AddMissingDependencies([]string{depName})
-				}
-				return
-			}
-			*ptr = append(*ptr, linkFile.Path())
-		}
-
-		if depPtr != nil {
-			dep := depFile
-			if !dep.Valid() {
-				dep = linkFile
-			}
-			*depPtr = append(*depPtr, dep.Path())
-		}
-
-		vendorSuffixModules := vendorSuffixModules(ctx.Config())
-
-		baseLibName := func(depName string) string {
-			libName := strings.TrimSuffix(depName, llndkLibrarySuffix)
-			libName = strings.TrimSuffix(libName, vendorPublicLibrarySuffix)
-			libName = strings.TrimPrefix(libName, "prebuilt_")
-			return libName
-		}
-
-		makeLibName := func(depName string) string {
-			libName := baseLibName(depName)
-			isLLndk := isLlndkLibrary(libName, ctx.Config())
-			isVendorPublicLib := inList(libName, *vendorPublicLibraries)
-			bothVendorAndCoreVariantsExist := ccDep.HasVendorVariant() || isLLndk
-
-			if c, ok := ccDep.(*Module); ok {
-				// Use base module name for snapshots when exporting to Makefile.
-				if c.isSnapshotPrebuilt() {
-					baseName := c.BaseModuleName()
-
-					if c.IsVndk() {
-						return baseName + ".vendor"
+				depFile = ccDep.Toc()
+			case libDepTag.static():
+				if libDepTag.wholeStatic {
+					ptr = &depPaths.WholeStaticLibs
+					if !ccDep.CcLibraryInterface() || !ccDep.Static() {
+						ctx.ModuleErrorf("module %q not a static library", depName)
+						return
 					}
 
-					if vendorSuffixModules[baseName] {
-						return baseName + ".vendor"
+					// Because the static library objects are included, this only makes sense
+					// in the context of proper cc.Modules.
+					if ccWholeStaticLib, ok := ccDep.(*Module); ok {
+						staticLib := ccWholeStaticLib.linker.(libraryInterface)
+						if missingDeps := staticLib.getWholeStaticMissingDeps(); missingDeps != nil {
+							postfix := " (required by " + ctx.OtherModuleName(dep) + ")"
+							for i := range missingDeps {
+								missingDeps[i] += postfix
+							}
+							ctx.AddMissingDependencies(missingDeps)
+						}
+						if _, ok := ccWholeStaticLib.linker.(prebuiltLinkerInterface); ok {
+							depPaths.WholeStaticLibsFromPrebuilts = append(depPaths.WholeStaticLibsFromPrebuilts, linkFile.Path())
+						} else {
+							depPaths.WholeStaticLibObjs = depPaths.WholeStaticLibObjs.Append(staticLib.objs())
+						}
 					} else {
-						return baseName
+						ctx.ModuleErrorf(
+							"non-cc.Modules cannot be included as whole static libraries.", depName)
+						return
+					}
+
+				} else {
+					switch libDepTag.Order {
+					case earlyLibraryDependency:
+						panic(fmt.Errorf("early static libs not suppported"))
+					case normalLibraryDependency:
+						// static dependencies will be handled separately so they can be ordered
+						// using transitive dependencies.
+						ptr = nil
+						directStaticDeps = append(directStaticDeps, ccDep)
+					case lateLibraryDependency:
+						ptr = &depPaths.LateStaticLibs
+					default:
+						panic(fmt.Errorf("unexpected library dependency order %d", libDepTag.Order))
 					}
 				}
 			}
 
-			if ctx.DeviceConfig().VndkUseCoreVariant() && ccDep.IsVndk() && !ccDep.MustUseVendorVariant() && !c.InRamdisk() && !c.InRecovery() {
-				// The vendor module is a no-vendor-variant VNDK library.  Depend on the
-				// core module instead.
-				return libName
-			} else if c.UseVndk() && bothVendorAndCoreVariantsExist {
-				// The vendor module in Make will have been renamed to not conflict with the core
-				// module, so update the dependency name here accordingly.
-				return libName + c.getNameSuffixWithVndkVersion(ctx)
-			} else if (ctx.Platform() || ctx.ProductSpecific()) && isVendorPublicLib {
-				return libName + vendorPublicLibrarySuffix
-			} else if ccDep.InRamdisk() && !ccDep.OnlyInRamdisk() {
-				return libName + ramdiskSuffix
-			} else if ccDep.InRecovery() && !ccDep.OnlyInRecovery() {
-				return libName + recoverySuffix
-			} else if ccDep.Module().Target().NativeBridge == android.NativeBridgeEnabled {
-				return libName + nativeBridgeSuffix
-			} else {
-				return libName
-			}
-		}
+			if libDepTag.static() && !libDepTag.wholeStatic {
+				if !ccDep.CcLibraryInterface() || !ccDep.Static() {
+					ctx.ModuleErrorf("module %q not a static library", depName)
+					return
+				}
 
-		// Export the shared libs to Make.
-		switch depTag {
-		case SharedDepTag, sharedExportDepTag, lateSharedDepTag, earlySharedDepTag:
-			if ccDep.CcLibrary() {
-				if ccDep.BuildStubs() && android.InAnyApex(depName) {
-					// Add the dependency to the APEX(es) providing the library so that
-					// m <module> can trigger building the APEXes as well.
-					for _, an := range android.GetApexesForModule(depName) {
-						c.Properties.ApexesProvidingSharedLibs = append(
-							c.Properties.ApexesProvidingSharedLibs, an)
-					}
+				// When combining coverage files for shared libraries and executables, coverage files
+				// in static libraries act as if they were whole static libraries. The same goes for
+				// source based Abi dump files.
+				if c, ok := ccDep.(*Module); ok {
+					staticLib := c.linker.(libraryInterface)
+					depPaths.StaticLibObjs.coverageFiles = append(depPaths.StaticLibObjs.coverageFiles,
+						staticLib.objs().coverageFiles...)
+					depPaths.StaticLibObjs.sAbiDumpFiles = append(depPaths.StaticLibObjs.sAbiDumpFiles,
+						staticLib.objs().sAbiDumpFiles...)
+				} else if c, ok := ccDep.(LinkableInterface); ok {
+					// Handle non-CC modules here
+					depPaths.StaticLibObjs.coverageFiles = append(depPaths.StaticLibObjs.coverageFiles,
+						c.CoverageFiles()...)
 				}
 			}
 
-			// Note: the order of libs in this list is not important because
-			// they merely serve as Make dependencies and do not affect this lib itself.
-			c.Properties.AndroidMkSharedLibs = append(
-				c.Properties.AndroidMkSharedLibs, makeLibName(depName))
-			// Record baseLibName for snapshots.
-			c.Properties.SnapshotSharedLibs = append(c.Properties.SnapshotSharedLibs, baseLibName(depName))
-		case ndkStubDepTag, ndkLateStubDepTag:
-			c.Properties.AndroidMkSharedLibs = append(
-				c.Properties.AndroidMkSharedLibs,
-				depName+"."+ccDep.ApiLevel())
-		case StaticDepTag, staticExportDepTag, lateStaticDepTag:
-			c.Properties.AndroidMkStaticLibs = append(
-				c.Properties.AndroidMkStaticLibs, makeLibName(depName))
-		case runtimeDepTag:
-			c.Properties.AndroidMkRuntimeLibs = append(
-				c.Properties.AndroidMkRuntimeLibs, makeLibName(depName))
-			// Record baseLibName for snapshots.
-			c.Properties.SnapshotRuntimeLibs = append(c.Properties.SnapshotRuntimeLibs, baseLibName(depName))
-		case wholeStaticDepTag:
-			c.Properties.AndroidMkWholeStaticLibs = append(
-				c.Properties.AndroidMkWholeStaticLibs, makeLibName(depName))
-		case headerDepTag:
-			c.Properties.AndroidMkHeaderLibs = append(
-				c.Properties.AndroidMkHeaderLibs, makeLibName(depName))
+			if ptr != nil {
+				if !linkFile.Valid() {
+					if !ctx.Config().AllowMissingDependencies() {
+						ctx.ModuleErrorf("module %q missing output file", depName)
+					} else {
+						ctx.AddMissingDependencies([]string{depName})
+					}
+					return
+				}
+				*ptr = append(*ptr, linkFile.Path())
+			}
+
+			if depPtr != nil {
+				dep := depFile
+				if !dep.Valid() {
+					dep = linkFile
+				}
+				*depPtr = append(*depPtr, dep.Path())
+			}
+
+			makeLibName := c.makeLibName(ctx, ccDep, depName) + libDepTag.makeSuffix
+			switch {
+			case libDepTag.header():
+				// TODO(ccross): The reexportFlags check is there to maintain previous
+				//   behavior when adding libraryDependencyTag and should be removed.
+				if !libDepTag.reexportFlags {
+					c.Properties.AndroidMkHeaderLibs = append(
+						c.Properties.AndroidMkHeaderLibs, makeLibName)
+				}
+			case libDepTag.shared():
+				if ccDep.CcLibrary() {
+					if ccDep.BuildStubs() && android.InAnyApex(depName) {
+						// Add the dependency to the APEX(es) providing the library so that
+						// m <module> can trigger building the APEXes as well.
+						for _, an := range android.GetApexesForModule(depName) {
+							c.Properties.ApexesProvidingSharedLibs = append(
+								c.Properties.ApexesProvidingSharedLibs, an)
+						}
+					}
+				}
+
+				// Note: the order of libs in this list is not important because
+				// they merely serve as Make dependencies and do not affect this lib itself.
+				// TODO(ccross): The reexportFlags, order and ndk checks are there to
+				//   maintain previous behavior when adding libraryDependencyTag and
+				//   should be removed.
+				if !c.static() || libDepTag.reexportFlags || libDepTag.Order == lateLibraryDependency || libDepTag.ndk {
+					c.Properties.AndroidMkSharedLibs = append(
+						c.Properties.AndroidMkSharedLibs, makeLibName)
+				}
+				// Record baseLibName for snapshots.
+				c.Properties.SnapshotSharedLibs = append(c.Properties.SnapshotSharedLibs, baseLibName(depName))
+			case libDepTag.static():
+				if libDepTag.wholeStatic {
+					c.Properties.AndroidMkWholeStaticLibs = append(
+						c.Properties.AndroidMkWholeStaticLibs, makeLibName)
+				} else {
+					c.Properties.AndroidMkStaticLibs = append(
+						c.Properties.AndroidMkStaticLibs, makeLibName)
+				}
+			}
+		} else {
+			switch depTag {
+			case runtimeDepTag:
+				c.Properties.AndroidMkRuntimeLibs = append(
+					c.Properties.AndroidMkRuntimeLibs, c.makeLibName(ctx, ccDep, depName)+libDepTag.makeSuffix)
+				// Record baseLibName for snapshots.
+				c.Properties.SnapshotRuntimeLibs = append(c.Properties.SnapshotRuntimeLibs, baseLibName(depName))
+			case objDepTag:
+				depPaths.Objs.objFiles = append(depPaths.Objs.objFiles, linkFile.Path())
+			case CrtBeginDepTag:
+				depPaths.CrtBegin = linkFile
+			case CrtEndDepTag:
+				depPaths.CrtEnd = linkFile
+			case dynamicLinkerDepTag:
+				depPaths.DynamicLinker = linkFile
+			}
 		}
 	})
 
@@ -2665,6 +2650,61 @@
 	return depPaths
 }
 
+// baseLibName trims known prefixes and suffixes
+func baseLibName(depName string) string {
+	libName := strings.TrimSuffix(depName, llndkLibrarySuffix)
+	libName = strings.TrimSuffix(libName, vendorPublicLibrarySuffix)
+	libName = strings.TrimPrefix(libName, "prebuilt_")
+	return libName
+}
+
+func (c *Module) makeLibName(ctx android.ModuleContext, ccDep LinkableInterface, depName string) string {
+	vendorSuffixModules := vendorSuffixModules(ctx.Config())
+	vendorPublicLibraries := vendorPublicLibraries(ctx.Config())
+
+	libName := baseLibName(depName)
+	isLLndk := isLlndkLibrary(libName, ctx.Config())
+	isVendorPublicLib := inList(libName, *vendorPublicLibraries)
+	bothVendorAndCoreVariantsExist := ccDep.HasVendorVariant() || isLLndk
+
+	if c, ok := ccDep.(*Module); ok {
+		// Use base module name for snapshots when exporting to Makefile.
+		if c.isSnapshotPrebuilt() {
+			baseName := c.BaseModuleName()
+
+			if c.IsVndk() {
+				return baseName + ".vendor"
+			}
+
+			if vendorSuffixModules[baseName] {
+				return baseName + ".vendor"
+			} else {
+				return baseName
+			}
+		}
+	}
+
+	if ctx.DeviceConfig().VndkUseCoreVariant() && ccDep.IsVndk() && !ccDep.MustUseVendorVariant() && !c.InRamdisk() && !c.InRecovery() {
+		// The vendor module is a no-vendor-variant VNDK library.  Depend on the
+		// core module instead.
+		return libName
+	} else if c.UseVndk() && bothVendorAndCoreVariantsExist {
+		// The vendor module in Make will have been renamed to not conflict with the core
+		// module, so update the dependency name here accordingly.
+		return libName + c.getNameSuffixWithVndkVersion(ctx)
+	} else if (ctx.Platform() || ctx.ProductSpecific()) && isVendorPublicLib {
+		return libName + vendorPublicLibrarySuffix
+	} else if ccDep.InRamdisk() && !ccDep.OnlyInRamdisk() {
+		return libName + ramdiskSuffix
+	} else if ccDep.InRecovery() && !ccDep.OnlyInRecovery() {
+		return libName + recoverySuffix
+	} else if ccDep.Module().Target().NativeBridge == android.NativeBridgeEnabled {
+		return libName + nativeBridgeSuffix
+	} else {
+		return libName
+	}
+}
+
 func (c *Module) InstallInData() bool {
 	if c.installer == nil {
 		return false
@@ -2690,12 +2730,12 @@
 	return c.InRecovery()
 }
 
-func (c *Module) SkipInstall() {
+func (c *Module) MakeUninstallable() {
 	if c.installer == nil {
-		c.ModuleBase.SkipInstall()
+		c.ModuleBase.MakeUninstallable()
 		return
 	}
-	c.installer.skipInstall(c)
+	c.installer.makeUninstallable(c)
 }
 
 func (c *Module) HostToolPath() android.OptionalPath {
@@ -2876,26 +2916,30 @@
 }
 
 func (c *Module) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
-	if depTag, ok := ctx.OtherModuleDependencyTag(dep).(DependencyTag); ok {
-		if cc, ok := dep.(*Module); ok {
-			if cc.HasStubsVariants() {
-				if depTag.Shared && depTag.Library {
-					// dynamic dep to a stubs lib crosses APEX boundary
-					return false
-				}
-				if IsRuntimeDepTag(depTag) {
-					// runtime dep to a stubs lib also crosses APEX boundary
-					return false
-				}
+	depTag := ctx.OtherModuleDependencyTag(dep)
+	libDepTag, isLibDepTag := depTag.(libraryDependencyTag)
+
+	if cc, ok := dep.(*Module); ok {
+		if cc.HasStubsVariants() {
+			if isLibDepTag && libDepTag.shared() {
+				// dynamic dep to a stubs lib crosses APEX boundary
+				return false
 			}
-			if depTag.FromStatic {
-				// shared_lib dependency from a static lib is considered as crossing
-				// the APEX boundary because the dependency doesn't actually is
-				// linked; the dependency is used only during the compilation phase.
+			if IsRuntimeDepTag(depTag) {
+				// runtime dep to a stubs lib also crosses APEX boundary
 				return false
 			}
 		}
-	} else if ctx.OtherModuleDependencyTag(dep) == llndkImplDep {
+		// TODO(ccross): The libDepTag.reexportFlags is there to maintain previous behavior
+		//   when adding libraryDependencyTag and should be removed.
+		if isLibDepTag && c.static() && libDepTag.shared() && !libDepTag.reexportFlags {
+			// shared_lib dependency from a static lib is considered as crossing
+			// the APEX boundary because the dependency doesn't actually is
+			// linked; the dependency is used only during the compilation phase.
+			return false
+		}
+	}
+	if depTag == llndkImplDep {
 		// We don't track beyond LLNDK
 		return false
 	}
@@ -3031,236 +3075,6 @@
 	}
 }
 
-var _ android.ImageInterface = (*Module)(nil)
-
-func (m *Module) ImageMutatorBegin(mctx android.BaseModuleContext) {
-	// Validation check
-	vendorSpecific := mctx.SocSpecific() || mctx.DeviceSpecific()
-	productSpecific := mctx.ProductSpecific()
-
-	if m.VendorProperties.Vendor_available != nil && vendorSpecific {
-		mctx.PropertyErrorf("vendor_available",
-			"doesn't make sense at the same time as `vendor: true`, `proprietary: true`, or `device_specific:true`")
-	}
-
-	if vndkdep := m.vndkdep; vndkdep != nil {
-		if vndkdep.isVndk() {
-			if vendorSpecific || productSpecific {
-				if !vndkdep.isVndkExt() {
-					mctx.PropertyErrorf("vndk",
-						"must set `extends: \"...\"` to vndk extension")
-				} else if m.VendorProperties.Vendor_available != nil {
-					mctx.PropertyErrorf("vendor_available",
-						"must not set at the same time as `vndk: {extends: \"...\"}`")
-				}
-			} else {
-				if vndkdep.isVndkExt() {
-					mctx.PropertyErrorf("vndk",
-						"must set `vendor: true` or `product_specific: true` to set `extends: %q`",
-						m.getVndkExtendsModuleName())
-				}
-				if m.VendorProperties.Vendor_available == nil {
-					mctx.PropertyErrorf("vndk",
-						"vendor_available must be set to either true or false when `vndk: {enabled: true}`")
-				}
-			}
-		} else {
-			if vndkdep.isVndkSp() {
-				mctx.PropertyErrorf("vndk",
-					"must set `enabled: true` to set `support_system_process: true`")
-			}
-			if vndkdep.isVndkExt() {
-				mctx.PropertyErrorf("vndk",
-					"must set `enabled: true` to set `extends: %q`",
-					m.getVndkExtendsModuleName())
-			}
-		}
-	}
-
-	var coreVariantNeeded bool = false
-	var ramdiskVariantNeeded bool = false
-	var recoveryVariantNeeded bool = false
-
-	var vendorVariants []string
-	var productVariants []string
-
-	platformVndkVersion := mctx.DeviceConfig().PlatformVndkVersion()
-	boardVndkVersion := mctx.DeviceConfig().VndkVersion()
-	productVndkVersion := mctx.DeviceConfig().ProductVndkVersion()
-	if boardVndkVersion == "current" {
-		boardVndkVersion = platformVndkVersion
-	}
-	if productVndkVersion == "current" {
-		productVndkVersion = platformVndkVersion
-	}
-
-	if boardVndkVersion == "" {
-		// If the device isn't compiling against the VNDK, we always
-		// use the core mode.
-		coreVariantNeeded = true
-	} else if _, ok := m.linker.(*llndkStubDecorator); ok {
-		// LL-NDK stubs only exist in the vendor and product variants,
-		// since the real libraries will be used in the core variant.
-		vendorVariants = append(vendorVariants,
-			platformVndkVersion,
-			boardVndkVersion,
-		)
-		productVariants = append(productVariants,
-			platformVndkVersion,
-			productVndkVersion,
-		)
-	} else if _, ok := m.linker.(*llndkHeadersDecorator); ok {
-		// ... and LL-NDK headers as well
-		vendorVariants = append(vendorVariants,
-			platformVndkVersion,
-			boardVndkVersion,
-		)
-		productVariants = append(productVariants,
-			platformVndkVersion,
-			productVndkVersion,
-		)
-	} else if m.isSnapshotPrebuilt() {
-		// Make vendor variants only for the versions in BOARD_VNDK_VERSION and
-		// PRODUCT_EXTRA_VNDK_VERSIONS.
-		if snapshot, ok := m.linker.(interface {
-			version() string
-		}); ok {
-			vendorVariants = append(vendorVariants, snapshot.version())
-		} else {
-			mctx.ModuleErrorf("version is unknown for snapshot prebuilt")
-		}
-	} else if m.HasVendorVariant() && !m.isVndkExt() {
-		// This will be available in /system, /vendor and /product
-		// or a /system directory that is available to vendor and product.
-		coreVariantNeeded = true
-
-		// We assume that modules under proprietary paths are compatible for
-		// BOARD_VNDK_VERSION. The other modules are regarded as AOSP, or
-		// PLATFORM_VNDK_VERSION.
-		if isVendorProprietaryPath(mctx.ModuleDir()) {
-			vendorVariants = append(vendorVariants, boardVndkVersion)
-		} else {
-			vendorVariants = append(vendorVariants, platformVndkVersion)
-		}
-
-		// vendor_available modules are also available to /product.
-		productVariants = append(productVariants, platformVndkVersion)
-		// VNDK is always PLATFORM_VNDK_VERSION
-		if !m.IsVndk() {
-			productVariants = append(productVariants, productVndkVersion)
-		}
-	} else if vendorSpecific && String(m.Properties.Sdk_version) == "" {
-		// This will be available in /vendor (or /odm) only
-
-		// kernel_headers is a special module type whose exported headers
-		// are coming from DeviceKernelHeaders() which is always vendor
-		// dependent. They'll always have both vendor variants.
-		// For other modules, we assume that modules under proprietary
-		// paths are compatible for BOARD_VNDK_VERSION. The other modules
-		// are regarded as AOSP, which is PLATFORM_VNDK_VERSION.
-		if _, ok := m.linker.(*kernelHeadersDecorator); ok {
-			vendorVariants = append(vendorVariants,
-				platformVndkVersion,
-				boardVndkVersion,
-			)
-		} else if isVendorProprietaryPath(mctx.ModuleDir()) {
-			vendorVariants = append(vendorVariants, boardVndkVersion)
-		} else {
-			vendorVariants = append(vendorVariants, platformVndkVersion)
-		}
-	} else {
-		// This is either in /system (or similar: /data), or is a
-		// modules built with the NDK. Modules built with the NDK
-		// will be restricted using the existing link type checks.
-		coreVariantNeeded = true
-	}
-
-	if boardVndkVersion != "" && productVndkVersion != "" {
-		if coreVariantNeeded && productSpecific && String(m.Properties.Sdk_version) == "" {
-			// The module has "product_specific: true" that does not create core variant.
-			coreVariantNeeded = false
-			productVariants = append(productVariants, productVndkVersion)
-		}
-	} else {
-		// Unless PRODUCT_PRODUCT_VNDK_VERSION is set, product partition has no
-		// restriction to use system libs.
-		// No product variants defined in this case.
-		productVariants = []string{}
-	}
-
-	if Bool(m.Properties.Ramdisk_available) {
-		ramdiskVariantNeeded = true
-	}
-
-	if m.ModuleBase.InstallInRamdisk() {
-		ramdiskVariantNeeded = true
-		coreVariantNeeded = false
-	}
-
-	if Bool(m.Properties.Recovery_available) {
-		recoveryVariantNeeded = true
-	}
-
-	if m.ModuleBase.InstallInRecovery() {
-		recoveryVariantNeeded = true
-		coreVariantNeeded = false
-	}
-
-	for _, variant := range android.FirstUniqueStrings(vendorVariants) {
-		m.Properties.ExtraVariants = append(m.Properties.ExtraVariants, VendorVariationPrefix+variant)
-	}
-
-	for _, variant := range android.FirstUniqueStrings(productVariants) {
-		m.Properties.ExtraVariants = append(m.Properties.ExtraVariants, ProductVariationPrefix+variant)
-	}
-
-	m.Properties.RamdiskVariantNeeded = ramdiskVariantNeeded
-	m.Properties.RecoveryVariantNeeded = recoveryVariantNeeded
-	m.Properties.CoreVariantNeeded = coreVariantNeeded
-}
-
-func (c *Module) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
-	return c.Properties.CoreVariantNeeded
-}
-
-func (c *Module) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
-	return c.Properties.RamdiskVariantNeeded
-}
-
-func (c *Module) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
-	return c.Properties.RecoveryVariantNeeded
-}
-
-func (c *Module) ExtraImageVariations(ctx android.BaseModuleContext) []string {
-	return c.Properties.ExtraVariants
-}
-
-func (c *Module) SetImageVariation(ctx android.BaseModuleContext, variant string, module android.Module) {
-	m := module.(*Module)
-	if variant == android.RamdiskVariation {
-		m.MakeAsPlatform()
-	} else if variant == android.RecoveryVariation {
-		m.MakeAsPlatform()
-		squashRecoverySrcs(m)
-	} else if strings.HasPrefix(variant, VendorVariationPrefix) {
-		m.Properties.ImageVariationPrefix = VendorVariationPrefix
-		m.Properties.VndkVersion = strings.TrimPrefix(variant, VendorVariationPrefix)
-		squashVendorSrcs(m)
-
-		// Makefile shouldn't know vendor modules other than BOARD_VNDK_VERSION.
-		// Hide other vendor variants to avoid collision.
-		vndkVersion := ctx.DeviceConfig().VndkVersion()
-		if vndkVersion != "current" && vndkVersion != "" && vndkVersion != m.Properties.VndkVersion {
-			m.Properties.HideFromMake = true
-			m.SkipInstall()
-		}
-	} else if strings.HasPrefix(variant, ProductVariationPrefix) {
-		m.Properties.ImageVariationPrefix = ProductVariationPrefix
-		m.Properties.VndkVersion = strings.TrimPrefix(variant, ProductVariationPrefix)
-		squashVendorSrcs(m)
-	}
-}
-
 func (c *Module) IsSdkVariant() bool {
 	return c.Properties.IsSdkVariant
 }
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 38a5c2d..77b5c52 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -1013,17 +1013,25 @@
 			filepath.Join(sharedDir, "libvendor_available.so.json"))
 
 		// For static libraries, all vendor:true and vendor_available modules (including VNDK) are captured.
+		// Also cfi variants are captured, except for prebuilts like toolchain_library
 		staticVariant := fmt.Sprintf("android_vendor.VER_%s_%s_static", archType, archVariant)
+		staticCfiVariant := fmt.Sprintf("android_vendor.VER_%s_%s_static_cfi", archType, archVariant)
 		staticDir := filepath.Join(snapshotVariantPath, archDir, "static")
 		checkSnapshot(t, ctx, snapshotSingleton, "libb", "libb.a", staticDir, staticVariant)
 		checkSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.a", staticDir, staticVariant)
+		checkSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.cfi.a", staticDir, staticCfiVariant)
 		checkSnapshot(t, ctx, snapshotSingleton, "libvendor", "libvendor.a", staticDir, staticVariant)
+		checkSnapshot(t, ctx, snapshotSingleton, "libvendor", "libvendor.cfi.a", staticDir, staticCfiVariant)
 		checkSnapshot(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.a", staticDir, staticVariant)
+		checkSnapshot(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.cfi.a", staticDir, staticCfiVariant)
 		jsonFiles = append(jsonFiles,
 			filepath.Join(staticDir, "libb.a.json"),
 			filepath.Join(staticDir, "libvndk.a.json"),
+			filepath.Join(staticDir, "libvndk.cfi.a.json"),
 			filepath.Join(staticDir, "libvendor.a.json"),
-			filepath.Join(staticDir, "libvendor_available.a.json"))
+			filepath.Join(staticDir, "libvendor.cfi.a.json"),
+			filepath.Join(staticDir, "libvendor_available.a.json"),
+			filepath.Join(staticDir, "libvendor_available.cfi.a.json"))
 
 		// For binary executables, all vendor:true and vendor_available modules are captured.
 		if archType == "arm64" {
@@ -1055,6 +1063,39 @@
 	}
 }
 
+func TestVendorSnapshotSanitizer(t *testing.T) {
+	bp := `
+	vendor_snapshot_static {
+		name: "libsnapshot",
+		vendor: true,
+		target_arch: "arm64",
+		version: "BOARD",
+		arch: {
+			arm64: {
+				src: "libsnapshot.a",
+				cfi: {
+					src: "libsnapshot.cfi.a",
+				}
+			},
+		},
+	}
+`
+	config := TestConfig(buildDir, android.Android, nil, bp, nil)
+	config.TestProductVariables.DeviceVndkVersion = StringPtr("BOARD")
+	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+	ctx := testCcWithConfig(t, config)
+
+	// Check non-cfi and cfi variant.
+	staticVariant := "android_vendor.BOARD_arm64_armv8-a_static"
+	staticCfiVariant := "android_vendor.BOARD_arm64_armv8-a_static_cfi"
+
+	staticModule := ctx.ModuleForTests("libsnapshot.vendor_static.BOARD.arm64", staticVariant).Module().(*Module)
+	assertString(t, staticModule.outputFile.Path().Base(), "libsnapshot.a")
+
+	staticCfiModule := ctx.ModuleForTests("libsnapshot.vendor_static.BOARD.arm64", staticCfiVariant).Module().(*Module)
+	assertString(t, staticCfiModule.outputFile.Path().Base(), "libsnapshot.cfi.a")
+}
+
 func TestDoubleLoadableDepError(t *testing.T) {
 	// Check whether an error is emitted when a LLNDK depends on a non-double_loadable VNDK lib.
 	testCcError(t, "module \".*\" variant \".*\": link.* \".*\" which is not LL-NDK, VNDK-SP, .*double_loadable", `
diff --git a/cc/coverage.go b/cc/coverage.go
index c823324..aa1fdf6 100644
--- a/cc/coverage.go
+++ b/cc/coverage.go
@@ -22,6 +22,8 @@
 	"android/soong/android"
 )
 
+const profileInstrFlag = "-fprofile-instr-generate=/data/misc/trace/clang-%p-%m.profraw"
+
 type CoverageProperties struct {
 	Native_coverage *bool
 
@@ -92,7 +94,7 @@
 			// flags that the module may use.
 			flags.Local.CFlags = append(flags.Local.CFlags, "-Wno-frame-larger-than=", "-O0")
 		} else if clangCoverage {
-			flags.Local.CommonFlags = append(flags.Local.CommonFlags, "-fprofile-instr-generate", "-fcoverage-mapping", "-Wno-pass-failed")
+			flags.Local.CommonFlags = append(flags.Local.CommonFlags, profileInstrFlag, "-fcoverage-mapping", "-Wno-pass-failed")
 		}
 	}
 
@@ -103,10 +105,14 @@
 			// For static libraries, the only thing that changes our object files
 			// are included whole static libraries, so check to see if any of
 			// those have coverage enabled.
-			ctx.VisitDirectDepsWithTag(wholeStaticDepTag, func(m android.Module) {
-				if cc, ok := m.(*Module); ok && cc.coverage != nil {
-					if cc.coverage.linkCoverage {
-						cov.linkCoverage = true
+			ctx.VisitDirectDeps(func(m android.Module) {
+				if depTag, ok := ctx.OtherModuleDependencyTag(m).(libraryDependencyTag); ok {
+					if depTag.static() && depTag.wholeStatic {
+						if cc, ok := m.(*Module); ok && cc.coverage != nil {
+							if cc.coverage.linkCoverage {
+								cov.linkCoverage = true
+							}
+						}
 					}
 				}
 			})
@@ -139,7 +145,7 @@
 
 			flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--wrap,getenv")
 		} else if clangCoverage {
-			flags.Local.LdFlags = append(flags.Local.LdFlags, "-fprofile-instr-generate")
+			flags.Local.LdFlags = append(flags.Local.LdFlags, profileInstrFlag)
 
 			coverage := ctx.GetDirectDepWithTag(getClangProfileLibraryName(ctx), CoverageDepTag).(*Module)
 			deps.WholeStaticLibs = append(deps.WholeStaticLibs, coverage.OutputFile().Path())
diff --git a/cc/genrule_test.go b/cc/genrule_test.go
index d38cf27..a366f76 100644
--- a/cc/genrule_test.go
+++ b/cc/genrule_test.go
@@ -76,3 +76,42 @@
 		t.Errorf(`want arm64 inputs %v, got %v`, expected, gen.Inputs.Strings())
 	}
 }
+
+func TestLibraryGenruleCmd(t *testing.T) {
+	bp := `
+		cc_library {
+			name: "libboth",
+		}
+
+		cc_library_shared {
+			name: "libshared",
+		}
+
+		cc_library_static {
+			name: "libstatic",
+		}
+
+		cc_genrule {
+			name: "gen",
+			tool_files: ["tool"],
+			srcs: [
+				":libboth",
+				":libshared",
+				":libstatic",
+			],
+			cmd: "$(location tool) $(in) $(out)",
+			out: ["out"],
+		}
+		`
+	ctx := testCc(t, bp)
+
+	gen := ctx.ModuleForTests("gen", "android_arm_armv7-a-neon").Output("out")
+	expected := []string{"libboth.so", "libshared.so", "libstatic.a"}
+	var got []string
+	for _, input := range gen.Inputs {
+		got = append(got, input.Base())
+	}
+	if !reflect.DeepEqual(expected, got) {
+		t.Errorf(`want inputs %v, got %v`, expected, got)
+	}
+}
diff --git a/cc/image.go b/cc/image.go
new file mode 100644
index 0000000..4daed7c
--- /dev/null
+++ b/cc/image.go
@@ -0,0 +1,348 @@
+// Copyright 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package cc
+
+// This file contains image variant related things, including image mutator functions, utility
+// functions to determine where a module is installed, etc.
+
+import (
+	"strings"
+
+	"android/soong/android"
+)
+
+var _ android.ImageInterface = (*Module)(nil)
+
+type imageVariantType string
+
+const (
+	coreImageVariant     imageVariantType = "core"
+	vendorImageVariant   imageVariantType = "vendor"
+	productImageVariant  imageVariantType = "product"
+	ramdiskImageVariant  imageVariantType = "ramdisk"
+	recoveryImageVariant imageVariantType = "recovery"
+	hostImageVariant     imageVariantType = "host"
+)
+
+func (c *Module) getImageVariantType() imageVariantType {
+	if c.Host() {
+		return hostImageVariant
+	} else if c.inVendor() {
+		return vendorImageVariant
+	} else if c.inProduct() {
+		return productImageVariant
+	} else if c.InRamdisk() {
+		return ramdiskImageVariant
+	} else if c.InRecovery() {
+		return recoveryImageVariant
+	} else {
+		return coreImageVariant
+	}
+}
+
+const (
+	// VendorVariationPrefix is the variant prefix used for /vendor code that compiles
+	// against the VNDK.
+	VendorVariationPrefix = "vendor."
+
+	// ProductVariationPrefix is the variant prefix used for /product code that compiles
+	// against the VNDK.
+	ProductVariationPrefix = "product."
+)
+
+func (ctx *moduleContext) ProductSpecific() bool {
+	return ctx.ModuleContext.ProductSpecific() ||
+		(ctx.mod.HasVendorVariant() && ctx.mod.inProduct() && !ctx.mod.IsVndk())
+}
+
+func (ctx *moduleContext) SocSpecific() bool {
+	return ctx.ModuleContext.SocSpecific() ||
+		(ctx.mod.HasVendorVariant() && ctx.mod.inVendor() && !ctx.mod.IsVndk())
+}
+
+func (ctx *moduleContextImpl) inProduct() bool {
+	return ctx.mod.inProduct()
+}
+
+func (ctx *moduleContextImpl) inVendor() bool {
+	return ctx.mod.inVendor()
+}
+
+func (ctx *moduleContextImpl) inRamdisk() bool {
+	return ctx.mod.InRamdisk()
+}
+
+func (ctx *moduleContextImpl) inRecovery() bool {
+	return ctx.mod.InRecovery()
+}
+
+// Returns true only when this module is configured to have core, product and vendor
+// variants.
+func (c *Module) HasVendorVariant() bool {
+	return c.IsVndk() || Bool(c.VendorProperties.Vendor_available)
+}
+
+// Returns true if the module is "product" variant. Usually these modules are installed in /product
+func (c *Module) inProduct() bool {
+	return c.Properties.ImageVariationPrefix == ProductVariationPrefix
+}
+
+// Returns true if the module is "vendor" variant. Usually these modules are installed in /vendor
+func (c *Module) inVendor() bool {
+	return c.Properties.ImageVariationPrefix == VendorVariationPrefix
+}
+
+func (c *Module) InRamdisk() bool {
+	return c.ModuleBase.InRamdisk() || c.ModuleBase.InstallInRamdisk()
+}
+
+func (c *Module) InRecovery() bool {
+	return c.ModuleBase.InRecovery() || c.ModuleBase.InstallInRecovery()
+}
+
+func (c *Module) OnlyInRamdisk() bool {
+	return c.ModuleBase.InstallInRamdisk()
+}
+
+func (c *Module) OnlyInRecovery() bool {
+	return c.ModuleBase.InstallInRecovery()
+}
+
+func (m *Module) ImageMutatorBegin(mctx android.BaseModuleContext) {
+	// Validation check
+	vendorSpecific := mctx.SocSpecific() || mctx.DeviceSpecific()
+	productSpecific := mctx.ProductSpecific()
+
+	if m.VendorProperties.Vendor_available != nil && vendorSpecific {
+		mctx.PropertyErrorf("vendor_available",
+			"doesn't make sense at the same time as `vendor: true`, `proprietary: true`, or `device_specific:true`")
+	}
+
+	if vndkdep := m.vndkdep; vndkdep != nil {
+		if vndkdep.isVndk() {
+			if vendorSpecific || productSpecific {
+				if !vndkdep.isVndkExt() {
+					mctx.PropertyErrorf("vndk",
+						"must set `extends: \"...\"` to vndk extension")
+				} else if m.VendorProperties.Vendor_available != nil {
+					mctx.PropertyErrorf("vendor_available",
+						"must not set at the same time as `vndk: {extends: \"...\"}`")
+				}
+			} else {
+				if vndkdep.isVndkExt() {
+					mctx.PropertyErrorf("vndk",
+						"must set `vendor: true` or `product_specific: true` to set `extends: %q`",
+						m.getVndkExtendsModuleName())
+				}
+				if m.VendorProperties.Vendor_available == nil {
+					mctx.PropertyErrorf("vndk",
+						"vendor_available must be set to either true or false when `vndk: {enabled: true}`")
+				}
+			}
+		} else {
+			if vndkdep.isVndkSp() {
+				mctx.PropertyErrorf("vndk",
+					"must set `enabled: true` to set `support_system_process: true`")
+			}
+			if vndkdep.isVndkExt() {
+				mctx.PropertyErrorf("vndk",
+					"must set `enabled: true` to set `extends: %q`",
+					m.getVndkExtendsModuleName())
+			}
+		}
+	}
+
+	var coreVariantNeeded bool = false
+	var ramdiskVariantNeeded bool = false
+	var recoveryVariantNeeded bool = false
+
+	var vendorVariants []string
+	var productVariants []string
+
+	platformVndkVersion := mctx.DeviceConfig().PlatformVndkVersion()
+	boardVndkVersion := mctx.DeviceConfig().VndkVersion()
+	productVndkVersion := mctx.DeviceConfig().ProductVndkVersion()
+	if boardVndkVersion == "current" {
+		boardVndkVersion = platformVndkVersion
+	}
+	if productVndkVersion == "current" {
+		productVndkVersion = platformVndkVersion
+	}
+
+	if boardVndkVersion == "" {
+		// If the device isn't compiling against the VNDK, we always
+		// use the core mode.
+		coreVariantNeeded = true
+	} else if _, ok := m.linker.(*llndkStubDecorator); ok {
+		// LL-NDK stubs only exist in the vendor and product variants,
+		// since the real libraries will be used in the core variant.
+		vendorVariants = append(vendorVariants,
+			platformVndkVersion,
+			boardVndkVersion,
+		)
+		productVariants = append(productVariants,
+			platformVndkVersion,
+			productVndkVersion,
+		)
+	} else if _, ok := m.linker.(*llndkHeadersDecorator); ok {
+		// ... and LL-NDK headers as well
+		vendorVariants = append(vendorVariants,
+			platformVndkVersion,
+			boardVndkVersion,
+		)
+		productVariants = append(productVariants,
+			platformVndkVersion,
+			productVndkVersion,
+		)
+	} else if m.isSnapshotPrebuilt() {
+		// Make vendor variants only for the versions in BOARD_VNDK_VERSION and
+		// PRODUCT_EXTRA_VNDK_VERSIONS.
+		if snapshot, ok := m.linker.(interface {
+			version() string
+		}); ok {
+			vendorVariants = append(vendorVariants, snapshot.version())
+		} else {
+			mctx.ModuleErrorf("version is unknown for snapshot prebuilt")
+		}
+	} else if m.HasVendorVariant() && !m.isVndkExt() {
+		// This will be available in /system, /vendor and /product
+		// or a /system directory that is available to vendor and product.
+		coreVariantNeeded = true
+
+		// We assume that modules under proprietary paths are compatible for
+		// BOARD_VNDK_VERSION. The other modules are regarded as AOSP, or
+		// PLATFORM_VNDK_VERSION.
+		if isVendorProprietaryPath(mctx.ModuleDir()) {
+			vendorVariants = append(vendorVariants, boardVndkVersion)
+		} else {
+			vendorVariants = append(vendorVariants, platformVndkVersion)
+		}
+
+		// vendor_available modules are also available to /product.
+		productVariants = append(productVariants, platformVndkVersion)
+		// VNDK is always PLATFORM_VNDK_VERSION
+		if !m.IsVndk() {
+			productVariants = append(productVariants, productVndkVersion)
+		}
+	} else if vendorSpecific && String(m.Properties.Sdk_version) == "" {
+		// This will be available in /vendor (or /odm) only
+
+		// kernel_headers is a special module type whose exported headers
+		// are coming from DeviceKernelHeaders() which is always vendor
+		// dependent. They'll always have both vendor variants.
+		// For other modules, we assume that modules under proprietary
+		// paths are compatible for BOARD_VNDK_VERSION. The other modules
+		// are regarded as AOSP, which is PLATFORM_VNDK_VERSION.
+		if _, ok := m.linker.(*kernelHeadersDecorator); ok {
+			vendorVariants = append(vendorVariants,
+				platformVndkVersion,
+				boardVndkVersion,
+			)
+		} else if isVendorProprietaryPath(mctx.ModuleDir()) {
+			vendorVariants = append(vendorVariants, boardVndkVersion)
+		} else {
+			vendorVariants = append(vendorVariants, platformVndkVersion)
+		}
+	} else {
+		// This is either in /system (or similar: /data), or is a
+		// modules built with the NDK. Modules built with the NDK
+		// will be restricted using the existing link type checks.
+		coreVariantNeeded = true
+	}
+
+	if boardVndkVersion != "" && productVndkVersion != "" {
+		if coreVariantNeeded && productSpecific && String(m.Properties.Sdk_version) == "" {
+			// The module has "product_specific: true" that does not create core variant.
+			coreVariantNeeded = false
+			productVariants = append(productVariants, productVndkVersion)
+		}
+	} else {
+		// Unless PRODUCT_PRODUCT_VNDK_VERSION is set, product partition has no
+		// restriction to use system libs.
+		// No product variants defined in this case.
+		productVariants = []string{}
+	}
+
+	if Bool(m.Properties.Ramdisk_available) {
+		ramdiskVariantNeeded = true
+	}
+
+	if m.ModuleBase.InstallInRamdisk() {
+		ramdiskVariantNeeded = true
+		coreVariantNeeded = false
+	}
+
+	if Bool(m.Properties.Recovery_available) {
+		recoveryVariantNeeded = true
+	}
+
+	if m.ModuleBase.InstallInRecovery() {
+		recoveryVariantNeeded = true
+		coreVariantNeeded = false
+	}
+
+	for _, variant := range android.FirstUniqueStrings(vendorVariants) {
+		m.Properties.ExtraVariants = append(m.Properties.ExtraVariants, VendorVariationPrefix+variant)
+	}
+
+	for _, variant := range android.FirstUniqueStrings(productVariants) {
+		m.Properties.ExtraVariants = append(m.Properties.ExtraVariants, ProductVariationPrefix+variant)
+	}
+
+	m.Properties.RamdiskVariantNeeded = ramdiskVariantNeeded
+	m.Properties.RecoveryVariantNeeded = recoveryVariantNeeded
+	m.Properties.CoreVariantNeeded = coreVariantNeeded
+}
+
+func (c *Module) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
+	return c.Properties.CoreVariantNeeded
+}
+
+func (c *Module) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+	return c.Properties.RamdiskVariantNeeded
+}
+
+func (c *Module) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
+	return c.Properties.RecoveryVariantNeeded
+}
+
+func (c *Module) ExtraImageVariations(ctx android.BaseModuleContext) []string {
+	return c.Properties.ExtraVariants
+}
+
+func (c *Module) SetImageVariation(ctx android.BaseModuleContext, variant string, module android.Module) {
+	m := module.(*Module)
+	if variant == android.RamdiskVariation {
+		m.MakeAsPlatform()
+	} else if variant == android.RecoveryVariation {
+		m.MakeAsPlatform()
+		squashRecoverySrcs(m)
+	} else if strings.HasPrefix(variant, VendorVariationPrefix) {
+		m.Properties.ImageVariationPrefix = VendorVariationPrefix
+		m.Properties.VndkVersion = strings.TrimPrefix(variant, VendorVariationPrefix)
+		squashVendorSrcs(m)
+
+		// Makefile shouldn't know vendor modules other than BOARD_VNDK_VERSION.
+		// Hide other vendor variants to avoid collision.
+		vndkVersion := ctx.DeviceConfig().VndkVersion()
+		if vndkVersion != "current" && vndkVersion != "" && vndkVersion != m.Properties.VndkVersion {
+			m.Properties.HideFromMake = true
+			m.SkipInstall()
+		}
+	} else if strings.HasPrefix(variant, ProductVariationPrefix) {
+		m.Properties.ImageVariationPrefix = ProductVariationPrefix
+		m.Properties.VndkVersion = strings.TrimPrefix(variant, ProductVariationPrefix)
+		squashVendorSrcs(m)
+	}
+}
diff --git a/cc/installer.go b/cc/installer.go
index 0b4a68c..e551c63 100644
--- a/cc/installer.go
+++ b/cc/installer.go
@@ -107,6 +107,6 @@
 	return String(installer.Properties.Relative_install_path)
 }
 
-func (installer *baseInstaller) skipInstall(mod *Module) {
-	mod.ModuleBase.SkipInstall()
+func (installer *baseInstaller) makeUninstallable(mod *Module) {
+	mod.ModuleBase.MakeUninstallable()
 }
diff --git a/cc/library.go b/cc/library.go
index 98f4d48..89f480f 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -28,7 +28,6 @@
 
 	"android/soong/android"
 	"android/soong/cc/config"
-	"android/soong/genrule"
 )
 
 type LibraryProperties struct {
@@ -1365,16 +1364,15 @@
 	return android.CheckAvailableForApex(what, list)
 }
 
-func (library *libraryDecorator) skipInstall(mod *Module) {
+func (library *libraryDecorator) makeUninstallable(mod *Module) {
 	if library.static() && library.buildStatic() && !library.buildStubs() {
-		// If we're asked to skip installation of a static library (in particular
-		// when it's not //apex_available:platform) we still want an AndroidMk entry
-		// for it to ensure we get the relevant NOTICE file targets (cf.
-		// notice_files.mk) that other libraries might depend on. AndroidMkEntries
-		// always sets LOCAL_UNINSTALLABLE_MODULE for these entries.
+		// If we're asked to make a static library uninstallable we don't do
+		// anything since AndroidMkEntries always sets LOCAL_UNINSTALLABLE_MODULE
+		// for these entries. This is done to still get the make targets for NOTICE
+		// files from notice_files.mk, which other libraries might depend on.
 		return
 	}
-	mod.ModuleBase.SkipInstall()
+	mod.ModuleBase.MakeUninstallable()
 }
 
 var versioningMacroNamesListKey = android.NewOnceKey("versioningMacroNamesList")
@@ -1470,6 +1468,12 @@
 			static.linker.(prebuiltLibraryInterface).setStatic()
 			shared.linker.(prebuiltLibraryInterface).setShared()
 
+			if library.buildShared() {
+				mctx.AliasVariation("shared")
+			} else if library.buildStatic() {
+				mctx.AliasVariation("static")
+			}
+
 			if !library.buildStatic() {
 				static.linker.(prebuiltLibraryInterface).disablePrebuilt()
 			}
@@ -1501,18 +1505,22 @@
 			if _, ok := library.(*Module); ok {
 				reuseStaticLibrary(mctx, static.(*Module), shared.(*Module))
 			}
+			mctx.AliasVariation("shared")
 		} else if library.BuildStaticVariant() {
 			variations := append([]string{"static"}, variations...)
 
 			modules := mctx.CreateLocalVariations(variations...)
 			modules[0].(LinkableInterface).SetStatic()
+			mctx.AliasVariation("static")
 		} else if library.BuildSharedVariant() {
 			variations := append([]string{"shared"}, variations...)
 
 			modules := mctx.CreateLocalVariations(variations...)
 			modules[0].(LinkableInterface).SetShared()
+			mctx.AliasVariation("shared")
 		} else if len(variations) > 0 {
 			mctx.CreateLocalVariations(variations...)
+			mctx.AliasVariation(variations[0])
 		}
 	}
 }
@@ -1559,13 +1567,14 @@
 	// "" is for the non-stubs variant
 	versions = append([]string{""}, versions...)
 
-	modules := mctx.CreateVariations(versions...)
+	modules := mctx.CreateLocalVariations(versions...)
 	for i, m := range modules {
 		if versions[i] != "" {
 			m.(LinkableInterface).SetBuildStubs()
 			m.(LinkableInterface).SetStubsVersions(versions[i])
 		}
 	}
+	mctx.AliasVariation("")
 }
 
 func VersionVariantAvailable(module interface {
@@ -1600,7 +1609,7 @@
 		if c, ok := library.(*Module); ok && c.IsStubs() {
 			stubsVersionsLock.Lock()
 			defer stubsVersionsLock.Unlock()
-			// For LLNDK llndk_library, we borrow vstubs.ersions from its implementation library.
+			// For LLNDK llndk_library, we borrow stubs.versions from its implementation library.
 			// Since llndk_library has dependency to its implementation library,
 			// we can safely access stubsVersionsFor() with its baseModuleName.
 			versions := stubsVersionsFor(mctx.Config())[c.BaseModuleName()]
@@ -1611,17 +1620,10 @@
 			return
 		}
 
-		mctx.CreateVariations("")
+		mctx.CreateLocalVariations("")
+		mctx.AliasVariation("")
 		return
 	}
-	if genrule, ok := mctx.Module().(*genrule.Module); ok {
-		if _, ok := genrule.Extra.(*GenruleExtraProperties); ok {
-			if VersionVariantAvailable(genrule) {
-				mctx.CreateVariations("")
-				return
-			}
-		}
-	}
 }
 
 // maybeInjectBoringSSLHash adds a rule to run bssl_inject_hash on the output file if the module has the
@@ -1633,8 +1635,7 @@
 	// TODO(b/137267623): Remove this in favor of a cc_genrule when they support operating on shared libraries.
 	injectBoringSSLHash := Bool(inject)
 	ctx.VisitDirectDeps(func(dep android.Module) {
-		tag := ctx.OtherModuleDependencyTag(dep)
-		if tag == StaticDepTag || tag == staticExportDepTag || tag == wholeStaticDepTag || tag == lateStaticDepTag {
+		if tag, ok := ctx.OtherModuleDependencyTag(dep).(libraryDependencyTag); ok && tag.static() {
 			if cc, ok := dep.(*Module); ok {
 				if library, ok := cc.linker.(*libraryDecorator); ok {
 					if Bool(library.Properties.Inject_bssl_hash) {
diff --git a/cc/library_headers.go b/cc/library_headers.go
index b7ab390..8b3dbeb 100644
--- a/cc/library_headers.go
+++ b/cc/library_headers.go
@@ -25,8 +25,9 @@
 
 var headersLibrarySdkMemberType = &librarySdkMemberType{
 	SdkMemberTypeBase: android.SdkMemberTypeBase{
-		PropertyName: "native_header_libs",
-		SupportsSdk:  true,
+		PropertyName:    "native_header_libs",
+		SupportsSdk:     true,
+		HostOsDependent: true,
 	},
 	prebuiltModuleType: "cc_prebuilt_library_headers",
 	noOutputFiles:      true,
diff --git a/cc/library_sdk_member.go b/cc/library_sdk_member.go
index 4b9eb30..cff00b6 100644
--- a/cc/library_sdk_member.go
+++ b/cc/library_sdk_member.go
@@ -27,8 +27,9 @@
 
 var sharedLibrarySdkMemberType = &librarySdkMemberType{
 	SdkMemberTypeBase: android.SdkMemberTypeBase{
-		PropertyName: "native_shared_libs",
-		SupportsSdk:  true,
+		PropertyName:    "native_shared_libs",
+		SupportsSdk:     true,
+		HostOsDependent: true,
 	},
 	prebuiltModuleType: "cc_prebuilt_library_shared",
 	linkTypes:          []string{"shared"},
@@ -36,8 +37,9 @@
 
 var staticLibrarySdkMemberType = &librarySdkMemberType{
 	SdkMemberTypeBase: android.SdkMemberTypeBase{
-		PropertyName: "native_static_libs",
-		SupportsSdk:  true,
+		PropertyName:    "native_static_libs",
+		SupportsSdk:     true,
+		HostOsDependent: true,
 	},
 	prebuiltModuleType: "cc_prebuilt_library_static",
 	linkTypes:          []string{"static"},
@@ -45,8 +47,9 @@
 
 var staticAndSharedLibrarySdkMemberType = &librarySdkMemberType{
 	SdkMemberTypeBase: android.SdkMemberTypeBase{
-		PropertyName: "native_libs",
-		SupportsSdk:  true,
+		PropertyName:    "native_libs",
+		SupportsSdk:     true,
+		HostOsDependent: true,
 	},
 	prebuiltModuleType: "cc_prebuilt_library",
 	linkTypes:          []string{"static", "shared"},
diff --git a/cc/linkable.go b/cc/linkable.go
index 66b1c3f..4c84163 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -1,9 +1,9 @@
 package cc
 
 import (
-	"github.com/google/blueprint"
-
 	"android/soong/android"
+
+	"github.com/google/blueprint"
 )
 
 type LinkableInterface interface {
@@ -63,27 +63,16 @@
 	StubDecorator() bool
 }
 
-type DependencyTag struct {
-	blueprint.BaseDependencyTag
-	Name    string
-	Library bool
-	Shared  bool
+var (
+	CrtBeginDepTag = dependencyTag{name: "crtbegin"}
+	CrtEndDepTag   = dependencyTag{name: "crtend"}
+	CoverageDepTag = dependencyTag{name: "coverage"}
+)
 
-	ReexportFlags bool
-
-	ExplicitlyVersioned bool
-
-	FromStatic bool
+func SharedDepTag() blueprint.DependencyTag {
+	return libraryDependencyTag{Kind: sharedLibraryDependency}
 }
 
-var (
-	SharedDepTag = DependencyTag{Name: "shared", Library: true, Shared: true}
-	StaticDepTag = DependencyTag{Name: "static", Library: true}
-
-	// Same as SharedDepTag, but from a static lib
-	SharedFromStaticDepTag = DependencyTag{Name: "shared from static", Library: true, Shared: true, FromStatic: true}
-
-	CrtBeginDepTag = DependencyTag{Name: "crtbegin"}
-	CrtEndDepTag   = DependencyTag{Name: "crtend"}
-	CoverageDepTag = DependencyTag{Name: "coverage"}
-)
+func StaticDepTag() blueprint.DependencyTag {
+	return libraryDependencyTag{Kind: staticLibraryDependency}
+}
diff --git a/cc/lto.go b/cc/lto.go
index 4489fc7..ed3abe7 100644
--- a/cc/lto.go
+++ b/cc/lto.go
@@ -148,24 +148,33 @@
 
 		mctx.WalkDeps(func(dep android.Module, parent android.Module) bool {
 			tag := mctx.OtherModuleDependencyTag(dep)
-			switch tag {
-			case StaticDepTag, staticExportDepTag, lateStaticDepTag, wholeStaticDepTag, objDepTag, reuseObjTag:
-				if dep, ok := dep.(*Module); ok && dep.lto != nil &&
-					!dep.lto.Disabled() {
-					if full && !Bool(dep.lto.Properties.Lto.Full) {
-						dep.lto.Properties.FullDep = true
-					}
-					if thin && !Bool(dep.lto.Properties.Lto.Thin) {
-						dep.lto.Properties.ThinDep = true
-					}
-				}
-
-				// Recursively walk static dependencies
-				return true
-			}
+			libTag, isLibTag := tag.(libraryDependencyTag)
 
 			// Do not recurse down non-static dependencies
-			return false
+			if isLibTag {
+				// TODO(ccross): the staticUnwinder check is there to maintain existing behavior
+				//   when adding libraryDependencyTag and should be removed.
+				if !libTag.static() || libTag.staticUnwinder {
+					return false
+				}
+			} else {
+				if tag != objDepTag && tag != reuseObjTag {
+					return false
+				}
+			}
+
+			if dep, ok := dep.(*Module); ok && dep.lto != nil &&
+				!dep.lto.Disabled() {
+				if full && !Bool(dep.lto.Properties.Lto.Full) {
+					dep.lto.Properties.FullDep = true
+				}
+				if thin && !Bool(dep.lto.Properties.Lto.Thin) {
+					dep.lto.Properties.ThinDep = true
+				}
+			}
+
+			// Recursively walk static dependencies
+			return true
 		})
 	}
 }
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index 653b43e..baf43ce 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -199,10 +199,6 @@
 	p.properties.Srcs = nil
 }
 
-func (p *prebuiltLibraryLinker) skipInstall(mod *Module) {
-	mod.ModuleBase.SkipInstall()
-}
-
 func NewPrebuiltLibrary(hod android.HostOrDeviceSupported) (*Module, *libraryDecorator) {
 	module, library := NewLibrary(hod)
 	module.compiler = nil
@@ -211,7 +207,6 @@
 		libraryDecorator: library,
 	}
 	module.linker = prebuilt
-	module.installer = prebuilt
 
 	module.AddProperties(&prebuilt.properties)
 
diff --git a/cc/rs.go b/cc/rs.go
index 9149e17..ba69f23 100644
--- a/cc/rs.go
+++ b/cc/rs.go
@@ -25,8 +25,8 @@
 
 func init() {
 	pctx.VariableFunc("rsCmd", func(ctx android.PackageVarContext) string {
-		if ctx.Config().UnbundledBuild() {
-			// Use RenderScript prebuilts for unbundled builds but not PDK builds
+		if ctx.Config().AlwaysUsePrebuiltSdks() {
+			// Use RenderScript prebuilts for unbundled builds
 			return filepath.Join("prebuilts/sdk/tools", runtime.GOOS, "bin/llvm-rs-cc")
 		} else {
 			return ctx.Config().HostToolPath(ctx, "llvm-rs-cc").String()
diff --git a/cc/sabi.go b/cc/sabi.go
index 8cef170..ef6bead 100644
--- a/cc/sabi.go
+++ b/cc/sabi.go
@@ -83,10 +83,7 @@
 		((c.IsVndk() && c.UseVndk()) || c.isLlndk(mctx.Config()) ||
 			(c.sabi != nil && c.sabi.Properties.CreateSAbiDumps)) {
 		mctx.VisitDirectDeps(func(m android.Module) {
-			tag := mctx.OtherModuleDependencyTag(m)
-			switch tag {
-			case StaticDepTag, staticExportDepTag, lateStaticDepTag, wholeStaticDepTag:
-
+			if tag, ok := mctx.OtherModuleDependencyTag(m).(libraryDependencyTag); ok && tag.static() {
 				cc, _ := m.(*Module)
 				if cc == nil {
 					return
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 4e52caa..2243082 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -57,9 +57,7 @@
 	cfiAsflags = []string{"-flto", "-fvisibility=default"}
 	cfiLdflags = []string{"-flto", "-fsanitize-cfi-cross-dso", "-fsanitize=cfi",
 		"-Wl,-plugin-opt,O1"}
-	cfiExportsMapPath     = "build/soong/cc/config/cfi_exports.map"
-	cfiStaticLibsMutex    sync.Mutex
-	hwasanStaticLibsMutex sync.Mutex
+	cfiExportsMapPath = "build/soong/cc/config/cfi_exports.map"
 
 	intOverflowCflags = []string{"-fsanitize-blacklist=build/soong/cc/config/integer_overflow_blocklist.txt"}
 
@@ -158,6 +156,9 @@
 		Scudo            *bool    `android:"arch_variant"`
 		Scs              *bool    `android:"arch_variant"`
 
+		// A modifier for ASAN and HWASAN for write only instrumentation
+		Writeonly *bool `android:"arch_variant"`
+
 		// Sanitizers to run in the diagnostic mode (as opposed to the release mode).
 		// Replaces abort() on error with a human-readable error message.
 		// Address and Thread sanitizers always run in diagnostic mode.
@@ -279,6 +280,15 @@
 			s.Hwaddress = boolPtr(true)
 		}
 
+		if found, globalSanitizers = removeFromList("writeonly", globalSanitizers); found && s.Writeonly == nil {
+			// Hwaddress and Address are set before, so we can check them here
+			// If they aren't explicitly set in the blueprint/SANITIZE_(HOST|TARGET), they would be nil instead of false
+			if s.Address == nil && s.Hwaddress == nil {
+				ctx.ModuleErrorf("writeonly modifier cannot be used without 'address' or 'hwaddress'")
+			}
+			s.Writeonly = boolPtr(true)
+		}
+
 		if len(globalSanitizers) > 0 {
 			ctx.ModuleErrorf("unknown global sanitizer option %s", globalSanitizers[0])
 		}
@@ -309,14 +319,14 @@
 
 	// Is CFI actually enabled?
 	if !ctx.Config().EnableCFI() {
-		s.Cfi = nil
-		s.Diag.Cfi = nil
+		s.Cfi = boolPtr(false)
+		s.Diag.Cfi = boolPtr(false)
 	}
 
 	// Also disable CFI for arm32 until b/35157333 is fixed.
 	if ctx.Arch().ArchType == android.Arm {
-		s.Cfi = nil
-		s.Diag.Cfi = nil
+		s.Cfi = boolPtr(false)
+		s.Diag.Cfi = boolPtr(false)
 	}
 
 	// HWASan requires AArch64 hardware feature (top-byte-ignore).
@@ -331,14 +341,14 @@
 
 	// Also disable CFI if ASAN is enabled.
 	if Bool(s.Address) || Bool(s.Hwaddress) {
-		s.Cfi = nil
-		s.Diag.Cfi = nil
+		s.Cfi = boolPtr(false)
+		s.Diag.Cfi = boolPtr(false)
 	}
 
 	// Disable sanitizers that depend on the UBSan runtime for windows/darwin builds.
 	if !ctx.Os().Linux() {
-		s.Cfi = nil
-		s.Diag.Cfi = nil
+		s.Cfi = boolPtr(false)
+		s.Diag.Cfi = boolPtr(false)
 		s.Misc_undefined = nil
 		s.Undefined = nil
 		s.All_undefined = nil
@@ -347,14 +357,15 @@
 
 	// Also disable CFI for VNDK variants of components
 	if ctx.isVndk() && ctx.useVndk() {
-		s.Cfi = nil
-		s.Diag.Cfi = nil
-	}
-
-	// Also disable CFI if building against snapshot.
-	vndkVersion := ctx.DeviceConfig().VndkVersion()
-	if ctx.useVndk() && vndkVersion != "current" && vndkVersion != "" {
-		s.Cfi = nil
+		if ctx.static() {
+			// Cfi variant for static vndk should be captured as vendor snapshot,
+			// so don't strictly disable Cfi.
+			s.Cfi = nil
+			s.Diag.Cfi = nil
+		} else {
+			s.Cfi = boolPtr(false)
+			s.Diag.Cfi = boolPtr(false)
+		}
 	}
 
 	// HWASan ramdisk (which is built from recovery) goes over some bootloader limit.
@@ -399,7 +410,7 @@
 	// TODO(b/131771163): CFI transiently depends on LTO, and thus Fuzzer is
 	// mutually incompatible.
 	if Bool(s.Fuzzer) {
-		s.Cfi = nil
+		s.Cfi = boolPtr(false)
 	}
 }
 
@@ -456,6 +467,10 @@
 		flags.Local.CFlags = append(flags.Local.CFlags, asanCflags...)
 		flags.Local.LdFlags = append(flags.Local.LdFlags, asanLdflags...)
 
+		if Bool(sanitize.Properties.Sanitize.Writeonly) {
+			flags.Local.CFlags = append(flags.Local.CFlags, "-mllvm", "-asan-instrument-reads=0")
+		}
+
 		if ctx.Host() {
 			// -nodefaultlibs (provided with libc++) prevents the driver from linking
 			// libraries needed with -fsanitize=address. http://b/18650275 (WAI)
@@ -475,6 +490,9 @@
 
 	if Bool(sanitize.Properties.Sanitize.Hwaddress) {
 		flags.Local.CFlags = append(flags.Local.CFlags, hwasanCflags...)
+		if Bool(sanitize.Properties.Sanitize.Writeonly) {
+			flags.Local.CFlags = append(flags.Local.CFlags, "-mllvm", "-hwasan-instrument-reads=0")
+		}
 	}
 
 	if Bool(sanitize.Properties.Sanitize.Fuzzer) {
@@ -706,31 +724,74 @@
 }
 
 func isSanitizableDependencyTag(tag blueprint.DependencyTag) bool {
-	t, ok := tag.(DependencyTag)
-	return ok && t.Library || t == reuseObjTag || t == objDepTag
+	switch t := tag.(type) {
+	case dependencyTag:
+		return t == reuseObjTag || t == objDepTag
+	case libraryDependencyTag:
+		return true
+	default:
+		return false
+	}
+}
+
+// Determines if the current module is a static library going to be captured
+// as vendor snapshot. Such modules must create both cfi and non-cfi variants,
+// except for ones which explicitly disable cfi.
+func needsCfiForVendorSnapshot(mctx android.TopDownMutatorContext) bool {
+	if isVendorProprietaryPath(mctx.ModuleDir()) {
+		return false
+	}
+
+	c := mctx.Module().(*Module)
+
+	if !c.inVendor() {
+		return false
+	}
+
+	if !c.static() {
+		return false
+	}
+
+	if c.Prebuilt() != nil {
+		return false
+	}
+
+	return c.sanitize != nil &&
+		!Bool(c.sanitize.Properties.Sanitize.Never) &&
+		!c.sanitize.isSanitizerExplicitlyDisabled(cfi)
 }
 
 // Propagate sanitizer requirements down from binaries
 func sanitizerDepsMutator(t sanitizerType) func(android.TopDownMutatorContext) {
 	return func(mctx android.TopDownMutatorContext) {
-		if c, ok := mctx.Module().(*Module); ok && c.sanitize.isSanitizerEnabled(t) {
-			mctx.WalkDeps(func(child, parent android.Module) bool {
-				if !isSanitizableDependencyTag(mctx.OtherModuleDependencyTag(child)) {
-					return false
-				}
-				if d, ok := child.(*Module); ok && d.sanitize != nil &&
-					!Bool(d.sanitize.Properties.Sanitize.Never) &&
-					!d.sanitize.isSanitizerExplicitlyDisabled(t) {
-					if t == cfi || t == hwasan || t == scs {
-						if d.static() {
+		if c, ok := mctx.Module().(*Module); ok {
+			enabled := c.sanitize.isSanitizerEnabled(t)
+			if t == cfi && needsCfiForVendorSnapshot(mctx) {
+				// We shouldn't change the result of isSanitizerEnabled(cfi) to correctly
+				// determine defaultVariation in sanitizerMutator below.
+				// Instead, just mark SanitizeDep to forcefully create cfi variant.
+				enabled = true
+				c.sanitize.Properties.SanitizeDep = true
+			}
+			if enabled {
+				mctx.WalkDeps(func(child, parent android.Module) bool {
+					if !isSanitizableDependencyTag(mctx.OtherModuleDependencyTag(child)) {
+						return false
+					}
+					if d, ok := child.(*Module); ok && d.sanitize != nil &&
+						!Bool(d.sanitize.Properties.Sanitize.Never) &&
+						!d.sanitize.isSanitizerExplicitlyDisabled(t) {
+						if t == cfi || t == hwasan || t == scs {
+							if d.static() {
+								d.sanitize.Properties.SanitizeDep = true
+							}
+						} else {
 							d.sanitize.Properties.SanitizeDep = true
 						}
-					} else {
-						d.sanitize.Properties.SanitizeDep = true
 					}
-				}
-				return true
-			})
+					return true
+				})
+			}
 		} else if sanitizeable, ok := mctx.Module().(Sanitizeable); ok {
 			// If an APEX module includes a lib which is enabled for a sanitizer T, then
 			// the APEX module is also enabled for the same sanitizer type.
@@ -949,10 +1010,11 @@
 				}
 
 				// static executable gets static runtime libs
+				depTag := libraryDependencyTag{Kind: staticLibraryDependency}
 				mctx.AddFarVariationDependencies(append(mctx.Target().Variations(), []blueprint.Variation{
 					{Mutator: "link", Variation: "static"},
 					c.ImageVariation(),
-				}...), StaticDepTag, deps...)
+				}...), depTag, deps...)
 			} else if !c.static() && !c.header() {
 				// If we're using snapshots and in vendor, redirect to snapshot whenever possible
 				if c.VndkVersion() == mctx.DeviceConfig().VndkVersion() {
@@ -963,10 +1025,11 @@
 				}
 
 				// dynamic executable and shared libs get shared runtime libs
+				depTag := libraryDependencyTag{Kind: sharedLibraryDependency, Order: earlyLibraryDependency}
 				mctx.AddFarVariationDependencies(append(mctx.Target().Variations(), []blueprint.Variation{
 					{Mutator: "link", Variation: "shared"},
 					c.ImageVariation(),
-				}...), earlySharedDepTag, runtimeLibrary)
+				}...), depTag, runtimeLibrary)
 			}
 			// static lib does not have dependency to the runtime library. The
 			// dependency will be added to the executables or shared libs using
@@ -1034,15 +1097,9 @@
 					// Export the static lib name to make
 					if c.static() && c.ExportedToMake() {
 						if t == cfi {
-							appendStringSync(c.Name(), cfiStaticLibs(mctx.Config()), &cfiStaticLibsMutex)
+							cfiStaticLibs(mctx.Config()).add(c, c.Name())
 						} else if t == hwasan {
-							if c.UseVndk() {
-								appendStringSync(c.Name(), hwasanVendorStaticLibs(mctx.Config()),
-									&hwasanStaticLibsMutex)
-							} else {
-								appendStringSync(c.Name(), hwasanStaticLibs(mctx.Config()),
-									&hwasanStaticLibsMutex)
-							}
+							hwasanStaticLibs(mctx.Config()).add(c, c.Name())
 						}
 					}
 				} else {
@@ -1068,38 +1125,96 @@
 			// APEX modules fall here
 			sanitizeable.AddSanitizerDependencies(mctx, t.name())
 			mctx.CreateVariations(t.variationName())
+		} else if c, ok := mctx.Module().(*Module); ok {
+			// Check if it's a snapshot module supporting sanitizer
+			if s, ok := c.linker.(snapshotSanitizer); ok && s.isSanitizerEnabled(t) {
+				// Set default variation as above.
+				defaultVariation := t.variationName()
+				mctx.SetDefaultDependencyVariation(&defaultVariation)
+				modules := mctx.CreateVariations("", t.variationName())
+				modules[0].(*Module).linker.(snapshotSanitizer).setSanitizerVariation(t, false)
+				modules[1].(*Module).linker.(snapshotSanitizer).setSanitizerVariation(t, true)
+
+				// Export the static lib name to make
+				if c.static() && c.ExportedToMake() {
+					if t == cfi {
+						// use BaseModuleName which is the name for Make.
+						cfiStaticLibs(mctx.Config()).add(c, c.BaseModuleName())
+					}
+				}
+			}
+		}
+	}
+}
+
+type sanitizerStaticLibsMap struct {
+	// libsMap contains one list of modules per each image and each arch.
+	// e.g. libs[vendor]["arm"] contains arm modules installed to vendor
+	libsMap       map[imageVariantType]map[string][]string
+	libsMapLock   sync.Mutex
+	sanitizerType sanitizerType
+}
+
+func newSanitizerStaticLibsMap(t sanitizerType) *sanitizerStaticLibsMap {
+	return &sanitizerStaticLibsMap{
+		sanitizerType: t,
+		libsMap:       make(map[imageVariantType]map[string][]string),
+	}
+}
+
+// Add the current module to sanitizer static libs maps
+// Each module should pass its exported name as names of Make and Soong can differ.
+func (s *sanitizerStaticLibsMap) add(c *Module, name string) {
+	image := c.getImageVariantType()
+	arch := c.Arch().ArchType.String()
+
+	s.libsMapLock.Lock()
+	defer s.libsMapLock.Unlock()
+
+	if _, ok := s.libsMap[image]; !ok {
+		s.libsMap[image] = make(map[string][]string)
+	}
+
+	s.libsMap[image][arch] = append(s.libsMap[image][arch], name)
+}
+
+// Exports makefile variables in the following format:
+// SOONG_{sanitizer}_{image}_{arch}_STATIC_LIBRARIES
+// e.g. SOONG_cfi_core_x86_STATIC_LIBRARIES
+// These are to be used by use_soong_sanitized_static_libraries.
+// See build/make/core/binary.mk for more details.
+func (s *sanitizerStaticLibsMap) exportToMake(ctx android.MakeVarsContext) {
+	for _, image := range android.SortedStringKeys(s.libsMap) {
+		archMap := s.libsMap[imageVariantType(image)]
+		for _, arch := range android.SortedStringKeys(archMap) {
+			libs := archMap[arch]
+			sort.Strings(libs)
+
+			key := fmt.Sprintf(
+				"SOONG_%s_%s_%s_STATIC_LIBRARIES",
+				s.sanitizerType.variationName(),
+				image, // already upper
+				arch)
+
+			ctx.Strict(key, strings.Join(libs, " "))
 		}
 	}
 }
 
 var cfiStaticLibsKey = android.NewOnceKey("cfiStaticLibs")
 
-func cfiStaticLibs(config android.Config) *[]string {
+func cfiStaticLibs(config android.Config) *sanitizerStaticLibsMap {
 	return config.Once(cfiStaticLibsKey, func() interface{} {
-		return &[]string{}
-	}).(*[]string)
+		return newSanitizerStaticLibsMap(cfi)
+	}).(*sanitizerStaticLibsMap)
 }
 
 var hwasanStaticLibsKey = android.NewOnceKey("hwasanStaticLibs")
 
-func hwasanStaticLibs(config android.Config) *[]string {
+func hwasanStaticLibs(config android.Config) *sanitizerStaticLibsMap {
 	return config.Once(hwasanStaticLibsKey, func() interface{} {
-		return &[]string{}
-	}).(*[]string)
-}
-
-var hwasanVendorStaticLibsKey = android.NewOnceKey("hwasanVendorStaticLibs")
-
-func hwasanVendorStaticLibs(config android.Config) *[]string {
-	return config.Once(hwasanVendorStaticLibsKey, func() interface{} {
-		return &[]string{}
-	}).(*[]string)
-}
-
-func appendStringSync(item string, list *[]string, mutex *sync.Mutex) {
-	mutex.Lock()
-	*list = append(*list, item)
-	mutex.Unlock()
+		return newSanitizerStaticLibsMap(hwasan)
+	}).(*sanitizerStaticLibsMap)
 }
 
 func enableMinimalRuntime(sanitize *sanitize) bool {
@@ -1129,17 +1244,9 @@
 }
 
 func cfiMakeVarsProvider(ctx android.MakeVarsContext) {
-	cfiStaticLibs := cfiStaticLibs(ctx.Config())
-	sort.Strings(*cfiStaticLibs)
-	ctx.Strict("SOONG_CFI_STATIC_LIBRARIES", strings.Join(*cfiStaticLibs, " "))
+	cfiStaticLibs(ctx.Config()).exportToMake(ctx)
 }
 
 func hwasanMakeVarsProvider(ctx android.MakeVarsContext) {
-	hwasanStaticLibs := hwasanStaticLibs(ctx.Config())
-	sort.Strings(*hwasanStaticLibs)
-	ctx.Strict("SOONG_HWASAN_STATIC_LIBRARIES", strings.Join(*hwasanStaticLibs, " "))
-
-	hwasanVendorStaticLibs := hwasanVendorStaticLibs(ctx.Config())
-	sort.Strings(*hwasanVendorStaticLibs)
-	ctx.Strict("SOONG_HWASAN_VENDOR_STATIC_LIBRARIES", strings.Join(*hwasanVendorStaticLibs, " "))
+	hwasanStaticLibs(ctx.Config()).exportToMake(ctx)
 }
diff --git a/cc/testing.go b/cc/testing.go
index 4d0b28b..4113fd2 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -20,8 +20,6 @@
 
 func RegisterRequiredBuildComponentsForTest(ctx android.RegistrationContext) {
 	RegisterPrebuiltBuildComponents(ctx)
-	android.RegisterPrebuiltMutators(ctx)
-
 	RegisterCCBuildComponents(ctx)
 	RegisterBinaryBuildComponents(ctx)
 	RegisterLibraryBuildComponents(ctx)
@@ -569,7 +567,9 @@
 	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
 	ctx.RegisterModuleType("vndk_prebuilt_shared", VndkPrebuiltSharedFactory)
 	ctx.RegisterModuleType("vndk_libraries_txt", VndkLibrariesTxtFactory)
+	ctx.RegisterModuleType("vendor_snapshot_static", VendorSnapshotStaticFactory)
 	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
+	android.RegisterPrebuiltMutators(ctx)
 	RegisterRequiredBuildComponentsForTest(ctx)
 	ctx.RegisterSingletonType("vndk-snapshot", VndkSnapshotSingleton)
 	ctx.RegisterSingletonType("vendor-snapshot", VendorSnapshotSingleton)
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index fec0c8b..e17a6d0 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -80,23 +80,75 @@
 	}).(*snapshotMap)
 }
 
-type vendorSnapshotLibraryProperties struct {
+type vendorSnapshotBaseProperties struct {
 	// snapshot version.
 	Version string
 
 	// Target arch name of the snapshot (e.g. 'arm64' for variant 'aosp_arm64')
 	Target_arch string
+}
 
+// vendorSnapshotModuleBase provides common basic functions for all snapshot modules.
+type vendorSnapshotModuleBase struct {
+	baseProperties vendorSnapshotBaseProperties
+	moduleSuffix   string
+}
+
+func (p *vendorSnapshotModuleBase) Name(name string) string {
+	return name + p.NameSuffix()
+}
+
+func (p *vendorSnapshotModuleBase) NameSuffix() string {
+	versionSuffix := p.version()
+	if p.arch() != "" {
+		versionSuffix += "." + p.arch()
+	}
+
+	return p.moduleSuffix + versionSuffix
+}
+
+func (p *vendorSnapshotModuleBase) version() string {
+	return p.baseProperties.Version
+}
+
+func (p *vendorSnapshotModuleBase) arch() string {
+	return p.baseProperties.Target_arch
+}
+
+func (p *vendorSnapshotModuleBase) isSnapshotPrebuilt() bool {
+	return true
+}
+
+// Call this after creating a snapshot module with module suffix
+// such as vendorSnapshotSharedSuffix
+func (p *vendorSnapshotModuleBase) init(m *Module, suffix string) {
+	p.moduleSuffix = suffix
+	m.AddProperties(&p.baseProperties)
+	android.AddLoadHook(m, func(ctx android.LoadHookContext) {
+		vendorSnapshotLoadHook(ctx, p)
+	})
+}
+
+func vendorSnapshotLoadHook(ctx android.LoadHookContext, p *vendorSnapshotModuleBase) {
+	if p.version() != ctx.DeviceConfig().VndkVersion() {
+		ctx.Module().Disable()
+		return
+	}
+}
+
+type vendorSnapshotLibraryProperties struct {
 	// Prebuilt file for each arch.
 	Src *string `android:"arch_variant"`
 
+	// list of directories that will be added to the include path (using -I).
+	Export_include_dirs []string `android:"arch_variant"`
+
+	// list of directories that will be added to the system path (using -isystem).
+	Export_system_include_dirs []string `android:"arch_variant"`
+
 	// list of flags that will be used for any module that links against this module.
 	Export_flags []string `android:"arch_variant"`
 
-	// Check the prebuilt ELF files (e.g. DT_SONAME, DT_NEEDED, resolution of undefined symbols,
-	// etc).
-	Check_elf_files *bool
-
 	// Whether this prebuilt needs to depend on sanitize ubsan runtime or not.
 	Sanitize_ubsan_dep *bool `android:"arch_variant"`
 
@@ -104,42 +156,24 @@
 	Sanitize_minimal_dep *bool `android:"arch_variant"`
 }
 
+type snapshotSanitizer interface {
+	isSanitizerEnabled(t sanitizerType) bool
+	setSanitizerVariation(t sanitizerType, enabled bool)
+}
+
 type vendorSnapshotLibraryDecorator struct {
+	vendorSnapshotModuleBase
 	*libraryDecorator
-	properties            vendorSnapshotLibraryProperties
+	properties          vendorSnapshotLibraryProperties
+	sanitizerProperties struct {
+		CfiEnabled bool `blueprint:"mutated"`
+
+		// Library flags for cfi variant.
+		Cfi vendorSnapshotLibraryProperties `android:"arch_variant"`
+	}
 	androidMkVendorSuffix bool
 }
 
-func (p *vendorSnapshotLibraryDecorator) Name(name string) string {
-	return name + p.NameSuffix()
-}
-
-func (p *vendorSnapshotLibraryDecorator) NameSuffix() string {
-	versionSuffix := p.version()
-	if p.arch() != "" {
-		versionSuffix += "." + p.arch()
-	}
-
-	var linkageSuffix string
-	if p.buildShared() {
-		linkageSuffix = vendorSnapshotSharedSuffix
-	} else if p.buildStatic() {
-		linkageSuffix = vendorSnapshotStaticSuffix
-	} else {
-		linkageSuffix = vendorSnapshotHeaderSuffix
-	}
-
-	return linkageSuffix + versionSuffix
-}
-
-func (p *vendorSnapshotLibraryDecorator) version() string {
-	return p.properties.Version
-}
-
-func (p *vendorSnapshotLibraryDecorator) arch() string {
-	return p.properties.Target_arch
-}
-
 func (p *vendorSnapshotLibraryDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
 	p.libraryDecorator.libName = strings.TrimSuffix(ctx.ModuleName(), p.NameSuffix())
 	return p.libraryDecorator.linkerFlags(ctx, flags)
@@ -165,11 +199,16 @@
 		return p.libraryDecorator.link(ctx, flags, deps, objs)
 	}
 
+	if p.sanitizerProperties.CfiEnabled {
+		p.properties = p.sanitizerProperties.Cfi
+	}
+
 	if !p.matchesWithDevice(ctx.DeviceConfig()) {
 		return nil
 	}
 
-	p.libraryDecorator.exportIncludes(ctx)
+	p.libraryDecorator.reexportDirs(android.PathsForModuleSrc(ctx, p.properties.Export_include_dirs)...)
+	p.libraryDecorator.reexportSystemDirs(android.PathsForModuleSrc(ctx, p.properties.Export_system_include_dirs)...)
 	p.libraryDecorator.reexportFlags(p.properties.Export_flags...)
 
 	in := android.PathForModuleSrc(ctx, *p.properties.Src)
@@ -189,32 +228,38 @@
 	return in
 }
 
-func (p *vendorSnapshotLibraryDecorator) nativeCoverage() bool {
-	return false
-}
-
-func (p *vendorSnapshotLibraryDecorator) isSnapshotPrebuilt() bool {
-	return true
-}
-
 func (p *vendorSnapshotLibraryDecorator) install(ctx ModuleContext, file android.Path) {
 	if p.matchesWithDevice(ctx.DeviceConfig()) && (p.shared() || p.static()) {
 		p.baseInstaller.install(ctx, file)
 	}
 }
 
-type vendorSnapshotInterface interface {
-	version() string
+func (p *vendorSnapshotLibraryDecorator) nativeCoverage() bool {
+	return false
 }
 
-func vendorSnapshotLoadHook(ctx android.LoadHookContext, p vendorSnapshotInterface) {
-	if p.version() != ctx.DeviceConfig().VndkVersion() {
-		ctx.Module().Disable()
+func (p *vendorSnapshotLibraryDecorator) isSanitizerEnabled(t sanitizerType) bool {
+	switch t {
+	case cfi:
+		return p.sanitizerProperties.Cfi.Src != nil
+	default:
+		return false
+	}
+}
+
+func (p *vendorSnapshotLibraryDecorator) setSanitizerVariation(t sanitizerType, enabled bool) {
+	if !enabled {
+		return
+	}
+	switch t {
+	case cfi:
+		p.sanitizerProperties.CfiEnabled = true
+	default:
 		return
 	}
 }
 
-func vendorSnapshotLibrary() (*Module, *vendorSnapshotLibraryDecorator) {
+func vendorSnapshotLibrary(suffix string) (*Module, *vendorSnapshotLibraryDecorator) {
 	module, library := NewLibrary(android.DeviceSupported)
 
 	module.stl = nil
@@ -237,77 +282,47 @@
 	module.linker = prebuilt
 	module.installer = prebuilt
 
+	prebuilt.init(module, suffix)
 	module.AddProperties(
 		&prebuilt.properties,
+		&prebuilt.sanitizerProperties,
 	)
 
 	return module, prebuilt
 }
 
 func VendorSnapshotSharedFactory() android.Module {
-	module, prebuilt := vendorSnapshotLibrary()
+	module, prebuilt := vendorSnapshotLibrary(vendorSnapshotSharedSuffix)
 	prebuilt.libraryDecorator.BuildOnlyShared()
-	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
-		vendorSnapshotLoadHook(ctx, prebuilt)
-	})
 	return module.Init()
 }
 
 func VendorSnapshotStaticFactory() android.Module {
-	module, prebuilt := vendorSnapshotLibrary()
+	module, prebuilt := vendorSnapshotLibrary(vendorSnapshotStaticSuffix)
 	prebuilt.libraryDecorator.BuildOnlyStatic()
-	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
-		vendorSnapshotLoadHook(ctx, prebuilt)
-	})
 	return module.Init()
 }
 
 func VendorSnapshotHeaderFactory() android.Module {
-	module, prebuilt := vendorSnapshotLibrary()
+	module, prebuilt := vendorSnapshotLibrary(vendorSnapshotHeaderSuffix)
 	prebuilt.libraryDecorator.HeaderOnly()
-	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
-		vendorSnapshotLoadHook(ctx, prebuilt)
-	})
 	return module.Init()
 }
 
+var _ snapshotSanitizer = (*vendorSnapshotLibraryDecorator)(nil)
+
 type vendorSnapshotBinaryProperties struct {
-	// snapshot version.
-	Version string
-
-	// Target arch name of the snapshot (e.g. 'arm64' for variant 'aosp_arm64_ab')
-	Target_arch string
-
 	// Prebuilt file for each arch.
 	Src *string `android:"arch_variant"`
 }
 
 type vendorSnapshotBinaryDecorator struct {
+	vendorSnapshotModuleBase
 	*binaryDecorator
 	properties            vendorSnapshotBinaryProperties
 	androidMkVendorSuffix bool
 }
 
-func (p *vendorSnapshotBinaryDecorator) Name(name string) string {
-	return name + p.NameSuffix()
-}
-
-func (p *vendorSnapshotBinaryDecorator) NameSuffix() string {
-	versionSuffix := p.version()
-	if p.arch() != "" {
-		versionSuffix += "." + p.arch()
-	}
-	return vendorSnapshotBinarySuffix + versionSuffix
-}
-
-func (p *vendorSnapshotBinaryDecorator) version() string {
-	return p.properties.Version
-}
-
-func (p *vendorSnapshotBinaryDecorator) arch() string {
-	return p.properties.Target_arch
-}
-
 func (p *vendorSnapshotBinaryDecorator) matchesWithDevice(config android.DeviceConfig) bool {
 	if config.DeviceArch() != p.arch() {
 		return false
@@ -349,8 +364,8 @@
 	return outputFile
 }
 
-func (p *vendorSnapshotBinaryDecorator) isSnapshotPrebuilt() bool {
-	return true
+func (p *vendorSnapshotBinaryDecorator) nativeCoverage() bool {
+	return false
 }
 
 func VendorSnapshotBinaryFactory() android.Module {
@@ -372,51 +387,23 @@
 	module.stl = nil
 	module.linker = prebuilt
 
-	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
-		vendorSnapshotLoadHook(ctx, prebuilt)
-	})
-
+	prebuilt.init(module, vendorSnapshotBinarySuffix)
 	module.AddProperties(&prebuilt.properties)
 	return module.Init()
 }
 
 type vendorSnapshotObjectProperties struct {
-	// snapshot version.
-	Version string
-
-	// Target arch name of the snapshot (e.g. 'arm64' for variant 'aosp_arm64_ab')
-	Target_arch string
-
 	// Prebuilt file for each arch.
 	Src *string `android:"arch_variant"`
 }
 
 type vendorSnapshotObjectLinker struct {
+	vendorSnapshotModuleBase
 	objectLinker
 	properties            vendorSnapshotObjectProperties
 	androidMkVendorSuffix bool
 }
 
-func (p *vendorSnapshotObjectLinker) Name(name string) string {
-	return name + p.NameSuffix()
-}
-
-func (p *vendorSnapshotObjectLinker) NameSuffix() string {
-	versionSuffix := p.version()
-	if p.arch() != "" {
-		versionSuffix += "." + p.arch()
-	}
-	return vendorSnapshotObjectSuffix + versionSuffix
-}
-
-func (p *vendorSnapshotObjectLinker) version() string {
-	return p.properties.Version
-}
-
-func (p *vendorSnapshotObjectLinker) arch() string {
-	return p.properties.Target_arch
-}
-
 func (p *vendorSnapshotObjectLinker) matchesWithDevice(config android.DeviceConfig) bool {
 	if config.DeviceArch() != p.arch() {
 		return false
@@ -443,10 +430,6 @@
 	return false
 }
 
-func (p *vendorSnapshotObjectLinker) isSnapshotPrebuilt() bool {
-	return true
-}
-
 func VendorSnapshotObjectFactory() android.Module {
 	module := newObject()
 
@@ -457,10 +440,7 @@
 	}
 	module.linker = prebuilt
 
-	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
-		vendorSnapshotLoadHook(ctx, prebuilt)
-	})
-
+	prebuilt.init(module, vendorSnapshotObjectSuffix)
 	module.AddProperties(&prebuilt.properties)
 	return module.Init()
 }
@@ -484,18 +464,22 @@
 
 var (
 	// Modules under following directories are ignored. They are OEM's and vendor's
-	// proprietary modules(device/, vendor/, and hardware/).
+	// proprietary modules(device/, kernel/, vendor/, and hardware/).
 	// TODO(b/65377115): Clean up these with more maintainable way
 	vendorProprietaryDirs = []string{
 		"device",
+		"kernel",
 		"vendor",
 		"hardware",
 	}
 
 	// Modules under following directories are included as they are in AOSP,
-	// although hardware/ is normally for vendor's own.
+	// although hardware/ and kernel/ are normally for vendor's own.
 	// TODO(b/65377115): Clean up these with more maintainable way
 	aospDirsUnderProprietary = []string{
+		"kernel/configs",
+		"kernel/prebuilts",
+		"kernel/tests",
 		"hardware/interfaces",
 		"hardware/libhardware",
 		"hardware/libhardware_legacy",
@@ -552,18 +536,29 @@
 	if _, ok := m.linker.(*kernelHeadersDecorator); ok {
 		return false
 	}
+	// skip llndk_library and llndk_headers which are backward compatible
+	if _, ok := m.linker.(*llndkStubDecorator); ok {
+		return false
+	}
+	if _, ok := m.linker.(*llndkHeadersDecorator); ok {
+		return false
+	}
 
 	// Libraries
 	if l, ok := m.linker.(snapshotLibraryInterface); ok {
 		// TODO(b/65377115): add full support for sanitizer
 		if m.sanitize != nil {
-			// cfi, scs and hwasan export both sanitized and unsanitized variants for static and header
+			// scs and hwasan export both sanitized and unsanitized variants for static and header
 			// Always use unsanitized variants of them.
-			for _, t := range []sanitizerType{cfi, scs, hwasan} {
+			for _, t := range []sanitizerType{scs, hwasan} {
 				if !l.shared() && m.sanitize.isSanitizerEnabled(t) {
 					return false
 				}
 			}
+			// cfi also exports both variants. But for static, we capture both.
+			if !l.static() && !l.shared() && m.sanitize.isSanitizerEnabled(cfi) {
+				return false
+			}
 		}
 		if l.static() {
 			return m.outputFile.Valid() && proptools.BoolDefault(m.VendorProperties.Vendor_available, true)
@@ -657,6 +652,7 @@
 			ExportedDirs       []string `json:",omitempty"`
 			ExportedSystemDirs []string `json:",omitempty"`
 			ExportedFlags      []string `json:",omitempty"`
+			Sanitize           string   `json:",omitempty"`
 			SanitizeMinimalDep bool     `json:",omitempty"`
 			SanitizeUbsanDep   bool     `json:",omitempty"`
 
@@ -706,6 +702,7 @@
 		var propOut string
 
 		if l, ok := m.linker.(snapshotLibraryInterface); ok {
+
 			// library flags
 			prop.ExportedFlags = l.exportedFlags()
 			for _, dir := range l.exportedDirs() {
@@ -738,6 +735,15 @@
 			if libType != "header" {
 				libPath := m.outputFile.Path()
 				stem = libPath.Base()
+				if l.static() && m.sanitize != nil && m.sanitize.isSanitizerEnabled(cfi) {
+					// both cfi and non-cfi variant for static libraries can exist.
+					// attach .cfi to distinguish between cfi and non-cfi.
+					// e.g. libbase.a -> libbase.cfi.a
+					ext := filepath.Ext(stem)
+					stem = strings.TrimSuffix(stem, ext) + ".cfi" + ext
+					prop.Sanitize = "cfi"
+					prop.ModuleName += ".cfi"
+				}
 				snapshotLibOut := filepath.Join(snapshotArchDir, targetArch, libType, stem)
 				ret = append(ret, copyFile(ctx, libPath, snapshotLibOut))
 			} else {
diff --git a/cc/vndk.go b/cc/vndk.go
index f9adec7..23bb095 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -26,6 +26,8 @@
 	"android/soong/android"
 	"android/soong/cc/config"
 	"android/soong/etc"
+
+	"github.com/google/blueprint"
 )
 
 const (
@@ -127,7 +129,7 @@
 	return "native:vendor:vndkspext"
 }
 
-func (vndk *vndkdep) vndkCheckLinkType(ctx android.ModuleContext, to *Module, tag DependencyTag) {
+func (vndk *vndkdep) vndkCheckLinkType(ctx android.ModuleContext, to *Module, tag blueprint.DependencyTag) {
 	if to.linker == nil {
 		return
 	}
diff --git a/cmd/diff_target_files/Android.bp b/cmd/diff_target_files/Android.bp
index 5397f4b..bc6b068 100644
--- a/cmd/diff_target_files/Android.bp
+++ b/cmd/diff_target_files/Android.bp
@@ -5,12 +5,12 @@
         "diff_target_files.go",
         "glob.go",
         "target_files.go",
-        "whitelist.go",
+        "allow_list.go",
         "zip_artifact.go",
     ],
     testSrcs: [
         "compare_test.go",
         "glob_test.go",
-        "whitelist_test.go",
+        "allow_list_test.go",
     ],
 }
diff --git a/cmd/diff_target_files/whitelist.go b/cmd/diff_target_files/allow_list.go
similarity index 77%
rename from cmd/diff_target_files/whitelist.go
rename to cmd/diff_target_files/allow_list.go
index f00fc1e..ca55b43 100644
--- a/cmd/diff_target_files/whitelist.go
+++ b/cmd/diff_target_files/allow_list.go
@@ -25,18 +25,13 @@
 	"unicode"
 )
 
-type jsonWhitelist struct {
-	Paths               []string
-	IgnoreMatchingLines []string
-}
-
-type whitelist struct {
+type allowList struct {
 	path                string
 	ignoreMatchingLines []string
 }
 
-func parseWhitelists(whitelists []string, whitelistFiles []string) ([]whitelist, error) {
-	var ret []whitelist
+func parseAllowLists(allowLists []string, allowListFiles []string) ([]allowList, error) {
+	var ret []allowList
 
 	add := func(path string, ignoreMatchingLines []string) {
 		for _, x := range ret {
@@ -46,24 +41,24 @@
 			}
 		}
 
-		ret = append(ret, whitelist{
+		ret = append(ret, allowList{
 			path:                path,
 			ignoreMatchingLines: ignoreMatchingLines,
 		})
 	}
 
-	for _, file := range whitelistFiles {
-		newWhitelists, err := parseWhitelistFile(file)
+	for _, file := range allowListFiles {
+		newAllowlists, err := parseAllowListFile(file)
 		if err != nil {
 			return nil, err
 		}
 
-		for _, w := range newWhitelists {
+		for _, w := range newAllowlists {
 			add(w.path, w.ignoreMatchingLines)
 		}
 	}
 
-	for _, s := range whitelists {
+	for _, s := range allowLists {
 		colon := strings.IndexRune(s, ':')
 		var ignoreMatchingLines []string
 		if colon >= 0 {
@@ -75,7 +70,7 @@
 	return ret, nil
 }
 
-func parseWhitelistFile(file string) ([]whitelist, error) {
+func parseAllowListFile(file string) ([]allowList, error) {
 	r, err := os.Open(file)
 	if err != nil {
 		return nil, err
@@ -84,27 +79,32 @@
 
 	d := json.NewDecoder(newJSONCommentStripper(r))
 
-	var jsonWhitelists []jsonWhitelist
+	var jsonAllowLists []struct {
+		Paths               []string
+		IgnoreMatchingLines []string
+	}
 
-	err = d.Decode(&jsonWhitelists)
+	if err := d.Decode(&jsonAllowLists); err != nil {
+		return nil, err
+	}
 
-	var whitelists []whitelist
-	for _, w := range jsonWhitelists {
+	var allowLists []allowList
+	for _, w := range jsonAllowLists {
 		for _, p := range w.Paths {
-			whitelists = append(whitelists, whitelist{
+			allowLists = append(allowLists, allowList{
 				path:                p,
 				ignoreMatchingLines: w.IgnoreMatchingLines,
 			})
 		}
 	}
 
-	return whitelists, err
+	return allowLists, err
 }
 
-func filterModifiedPaths(l [][2]*ZipArtifactFile, whitelists []whitelist) ([][2]*ZipArtifactFile, error) {
+func filterModifiedPaths(l [][2]*ZipArtifactFile, allowLists []allowList) ([][2]*ZipArtifactFile, error) {
 outer:
 	for i := 0; i < len(l); i++ {
-		for _, w := range whitelists {
+		for _, w := range allowLists {
 			if match, err := Match(w.path, l[i][0].Name); err != nil {
 				return l, err
 			} else if match {
@@ -126,10 +126,10 @@
 	return l, nil
 }
 
-func filterNewPaths(l []*ZipArtifactFile, whitelists []whitelist) ([]*ZipArtifactFile, error) {
+func filterNewPaths(l []*ZipArtifactFile, allowLists []allowList) ([]*ZipArtifactFile, error) {
 outer:
 	for i := 0; i < len(l); i++ {
-		for _, w := range whitelists {
+		for _, w := range allowLists {
 			if match, err := Match(w.path, l[i].Name); err != nil {
 				return l, err
 			} else if match && len(w.ignoreMatchingLines) == 0 {
@@ -192,18 +192,18 @@
 	return bytes.Compare(bufA, bufB) == 0, nil
 }
 
-func applyWhitelists(diff zipDiff, whitelists []whitelist) (zipDiff, error) {
+func applyAllowLists(diff zipDiff, allowLists []allowList) (zipDiff, error) {
 	var err error
 
-	diff.modified, err = filterModifiedPaths(diff.modified, whitelists)
+	diff.modified, err = filterModifiedPaths(diff.modified, allowLists)
 	if err != nil {
 		return diff, err
 	}
-	diff.onlyInA, err = filterNewPaths(diff.onlyInA, whitelists)
+	diff.onlyInA, err = filterNewPaths(diff.onlyInA, allowLists)
 	if err != nil {
 		return diff, err
 	}
-	diff.onlyInB, err = filterNewPaths(diff.onlyInB, whitelists)
+	diff.onlyInB, err = filterNewPaths(diff.onlyInB, allowLists)
 	if err != nil {
 		return diff, err
 	}
diff --git a/cmd/diff_target_files/whitelist_test.go b/cmd/diff_target_files/allow_list_test.go
similarity index 82%
rename from cmd/diff_target_files/whitelist_test.go
rename to cmd/diff_target_files/allow_list_test.go
index 4b19fdd..8410e5a 100644
--- a/cmd/diff_target_files/whitelist_test.go
+++ b/cmd/diff_target_files/allow_list_test.go
@@ -57,10 +57,10 @@
 
 var f2 = bytesToZipArtifactFile("dir/f2", nil)
 
-func Test_applyWhitelists(t *testing.T) {
+func Test_applyAllowLists(t *testing.T) {
 	type args struct {
 		diff       zipDiff
-		whitelists []whitelist
+		allowLists []allowList
 	}
 	tests := []struct {
 		name    string
@@ -74,7 +74,7 @@
 				diff: zipDiff{
 					onlyInA: []*ZipArtifactFile{f1a, f2},
 				},
-				whitelists: []whitelist{{path: "dir/f1"}},
+				allowLists: []allowList{{path: "dir/f1"}},
 			},
 			want: zipDiff{
 				onlyInA: []*ZipArtifactFile{f2},
@@ -86,7 +86,7 @@
 				diff: zipDiff{
 					onlyInA: []*ZipArtifactFile{f1a, f2},
 				},
-				whitelists: []whitelist{{path: "dir/*"}},
+				allowLists: []allowList{{path: "dir/*"}},
 			},
 			want: zipDiff{},
 		},
@@ -96,7 +96,7 @@
 				diff: zipDiff{
 					modified: [][2]*ZipArtifactFile{{f1a, f1b}},
 				},
-				whitelists: []whitelist{{path: "dir/*"}},
+				allowLists: []allowList{{path: "dir/*"}},
 			},
 			want: zipDiff{},
 		},
@@ -106,20 +106,20 @@
 				diff: zipDiff{
 					modified: [][2]*ZipArtifactFile{{f1a, f1b}},
 				},
-				whitelists: []whitelist{{path: "dir/*", ignoreMatchingLines: []string{"foo: .*"}}},
+				allowLists: []allowList{{path: "dir/*", ignoreMatchingLines: []string{"foo: .*"}}},
 			},
 			want: zipDiff{},
 		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			got, err := applyWhitelists(tt.args.diff, tt.args.whitelists)
+			got, err := applyAllowLists(tt.args.diff, tt.args.allowLists)
 			if (err != nil) != tt.wantErr {
-				t.Errorf("applyWhitelists() error = %v, wantErr %v", err, tt.wantErr)
+				t.Errorf("Test_applyAllowLists() error = %v, wantErr %v", err, tt.wantErr)
 				return
 			}
 			if !reflect.DeepEqual(got, tt.want) {
-				t.Errorf("applyWhitelists() = %v, want %v", got, tt.want)
+				t.Errorf("Test_applyAllowLists() = %v, want %v", got, tt.want)
 			}
 		})
 	}
diff --git a/cmd/diff_target_files/compare.go b/cmd/diff_target_files/compare.go
index 00cd9ca..45b6d6b 100644
--- a/cmd/diff_target_files/compare.go
+++ b/cmd/diff_target_files/compare.go
@@ -21,7 +21,7 @@
 
 // compareTargetFiles takes two ZipArtifacts and compares the files they contain by examining
 // the path, size, and CRC of each file.
-func compareTargetFiles(priZip, refZip ZipArtifact, artifact string, whitelists []whitelist, filters []string) (zipDiff, error) {
+func compareTargetFiles(priZip, refZip ZipArtifact, artifact string, allowLists []allowList, filters []string) (zipDiff, error) {
 	priZipFiles, err := priZip.Files()
 	if err != nil {
 		return zipDiff{}, fmt.Errorf("error fetching target file lists from primary zip %v", err)
@@ -45,7 +45,7 @@
 	// Compare the file lists from both builds
 	diff := diffTargetFilesLists(refZipFiles, priZipFiles)
 
-	return applyWhitelists(diff, whitelists)
+	return applyAllowLists(diff, allowLists)
 }
 
 // zipDiff contains the list of files that differ between two zip files.
diff --git a/cmd/diff_target_files/diff_target_files.go b/cmd/diff_target_files/diff_target_files.go
index 75bc8ee..634565b 100644
--- a/cmd/diff_target_files/diff_target_files.go
+++ b/cmd/diff_target_files/diff_target_files.go
@@ -22,8 +22,8 @@
 )
 
 var (
-	whitelists     = newMultiString("whitelist", "whitelist patterns in the form <pattern>[:<regex of line to ignore>]")
-	whitelistFiles = newMultiString("whitelist_file", "files containing whitelist definitions")
+	allowLists     = newMultiString("allowlist", "allowlist patterns in the form <pattern>[:<regex of line to ignore>]")
+	allowListFiles = newMultiString("allowlist_file", "files containing allowlist definitions")
 
 	filters = newMultiString("filter", "filter patterns to apply to files in target-files.zip before comparing")
 )
@@ -47,9 +47,9 @@
 		os.Exit(1)
 	}
 
-	whitelists, err := parseWhitelists(*whitelists, *whitelistFiles)
+	allowLists, err := parseAllowLists(*allowLists, *allowListFiles)
 	if err != nil {
-		fmt.Fprintf(os.Stderr, "Error parsing whitelists: %v\n", err)
+		fmt.Fprintf(os.Stderr, "Error parsing allowlists: %v\n", err)
 		os.Exit(1)
 	}
 
@@ -67,7 +67,7 @@
 	}
 	defer refZip.Close()
 
-	diff, err := compareTargetFiles(priZip, refZip, targetFilesPattern, whitelists, *filters)
+	diff, err := compareTargetFiles(priZip, refZip, targetFilesPattern, allowLists, *filters)
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "Error comparing zip files: %v\n", err)
 		os.Exit(1)
diff --git a/cmd/soong_build/Android.bp b/cmd/soong_build/Android.bp
index b559bac..7b8352b 100644
--- a/cmd/soong_build/Android.bp
+++ b/cmd/soong_build/Android.bp
@@ -26,6 +26,7 @@
     srcs: [
         "main.go",
         "writedocs.go",
+        "bazel_overlay.go",
     ],
     primaryBuilder: true,
 }
diff --git a/cmd/soong_build/bazel_overlay.go b/cmd/soong_build/bazel_overlay.go
new file mode 100644
index 0000000..e37c163
--- /dev/null
+++ b/cmd/soong_build/bazel_overlay.go
@@ -0,0 +1,173 @@
+// 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 main
+
+import (
+	"android/soong/android"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"strings"
+
+	"github.com/google/blueprint"
+)
+
+const (
+	soongModuleLoad = `package(default_visibility = ["//visibility:public"])
+load("//:soong_module.bzl", "soong_module")
+`
+
+	// A BUILD file target snippet representing a Soong module
+	soongModuleTarget = `soong_module(
+    name = "%s",
+    module_name = "%s",
+    module_type = "%s",
+    module_variant = "%s",
+    deps = [
+        %s
+    ],
+)
+`
+
+	// The soong_module rule implementation in a .bzl file
+	soongModuleBzl = `
+SoongModuleInfo = provider(
+    fields = {
+        "name": "Name of module",
+        "type": "Type of module",
+        "variant": "Variant of module",
+    },
+)
+
+def _soong_module_impl(ctx):
+    return [
+        SoongModuleInfo(
+            name = ctx.attr.module_name,
+            type = ctx.attr.module_type,
+            variant = ctx.attr.module_variant,
+        ),
+    ]
+
+soong_module = rule(
+    implementation = _soong_module_impl,
+    attrs = {
+        "module_name": attr.string(mandatory = True),
+        "module_type": attr.string(mandatory = True),
+        "module_variant": attr.string(),
+        "deps": attr.label_list(providers = [SoongModuleInfo]),
+    },
+)
+`
+)
+
+func targetNameWithVariant(c *blueprint.Context, logicModule blueprint.Module) string {
+	name := ""
+	if c.ModuleSubDir(logicModule) != "" {
+		name = c.ModuleName(logicModule) + "--" + c.ModuleSubDir(logicModule)
+	} else {
+		name = c.ModuleName(logicModule)
+	}
+
+	return strings.Replace(name, "//", "", 1)
+}
+
+func qualifiedTargetLabel(c *blueprint.Context, logicModule blueprint.Module) string {
+	return "//" +
+		packagePath(c, logicModule) +
+		":" +
+		targetNameWithVariant(c, logicModule)
+}
+
+func packagePath(c *blueprint.Context, logicModule blueprint.Module) string {
+	return filepath.Dir(c.BlueprintFile(logicModule))
+}
+
+func createBazelOverlay(ctx *android.Context, bazelOverlayDir string) error {
+	blueprintCtx := ctx.Context
+	blueprintCtx.VisitAllModules(func(module blueprint.Module) {
+		buildFile, err := buildFileForModule(blueprintCtx, module)
+		if err != nil {
+			panic(err)
+		}
+
+		// TODO(b/163018919): DirectDeps can have duplicate (module, variant)
+		// items, if the modules are added using different DependencyTag. Figure
+		// out the implications of that.
+		depLabels := map[string]bool{}
+		blueprintCtx.VisitDirectDeps(module, func(depModule blueprint.Module) {
+			depLabels[qualifiedTargetLabel(blueprintCtx, depModule)] = true
+		})
+
+		var depLabelList string
+		for depLabel, _ := range depLabels {
+			depLabelList += "\"" + depLabel + "\",\n        "
+		}
+		buildFile.Write([]byte(
+			fmt.Sprintf(
+				soongModuleTarget,
+				targetNameWithVariant(blueprintCtx, module),
+				blueprintCtx.ModuleName(module),
+				blueprintCtx.ModuleType(module),
+				// misleading name, this actually returns the variant.
+				blueprintCtx.ModuleSubDir(module),
+				depLabelList)))
+		buildFile.Close()
+	})
+
+	if err := writeReadOnlyFile(bazelOverlayDir, "WORKSPACE", ""); err != nil {
+		return err
+	}
+
+	if err := writeReadOnlyFile(bazelOverlayDir, "BUILD", ""); err != nil {
+		return err
+	}
+
+	return writeReadOnlyFile(bazelOverlayDir, "soong_module.bzl", soongModuleBzl)
+}
+
+func buildFileForModule(ctx *blueprint.Context, module blueprint.Module) (*os.File, error) {
+	// Create nested directories for the BUILD file
+	dirPath := filepath.Join(bazelOverlayDir, packagePath(ctx, module))
+	if _, err := os.Stat(dirPath); os.IsNotExist(err) {
+		os.MkdirAll(dirPath, os.ModePerm)
+	}
+	// Open the file for appending, and create it if it doesn't exist
+	f, err := os.OpenFile(
+		filepath.Join(dirPath, "BUILD.bazel"),
+		os.O_APPEND|os.O_CREATE|os.O_WRONLY,
+		0644)
+	if err != nil {
+		return nil, err
+	}
+
+	// If the file is empty, add the load statement for the `soong_module` rule
+	fi, err := f.Stat()
+	if err != nil {
+		return nil, err
+	}
+	if fi.Size() == 0 {
+		f.Write([]byte(soongModuleLoad + "\n"))
+	}
+
+	return f, nil
+}
+
+// The overlay directory should be read-only, sufficient for bazel query.
+func writeReadOnlyFile(dir string, baseName string, content string) error {
+	workspaceFile := filepath.Join(bazelOverlayDir, baseName)
+	// 0444 is read-only
+	return ioutil.WriteFile(workspaceFile, []byte(content), 0444)
+}
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 532d9e4..01a39a2 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -26,11 +26,13 @@
 )
 
 var (
-	docFile string
+	docFile         string
+	bazelOverlayDir string
 )
 
 func init() {
 	flag.StringVar(&docFile, "soong_docs", "", "build documentation file to output")
+	flag.StringVar(&bazelOverlayDir, "bazel_overlay_dir", "", "path to the bazel overlay directory")
 }
 
 func newNameResolver(config android.Config) *android.NameResolver {
@@ -65,7 +67,7 @@
 		os.Exit(1)
 	}
 
-	if docFile != "" {
+	if !shouldPrepareBuildActions() {
 		configuration.SetStopBefore(bootstrap.StopBeforePrepareBuildActions)
 	}
 
@@ -85,6 +87,13 @@
 
 	bootstrap.Main(ctx.Context, configuration, extraNinjaDeps...)
 
+	if bazelOverlayDir != "" {
+		if err := createBazelOverlay(ctx, bazelOverlayDir); err != nil {
+			fmt.Fprintf(os.Stderr, "%s", err)
+			os.Exit(1)
+		}
+	}
+
 	if docFile != "" {
 		if err := writeDocs(ctx, docFile); err != nil {
 			fmt.Fprintf(os.Stderr, "%s", err)
@@ -94,7 +103,7 @@
 
 	// TODO(ccross): make this a command line argument.  Requires plumbing through blueprint
 	//  to affect the command line of the primary builder.
-	if docFile == "" {
+	if shouldPrepareBuildActions() {
 		metricsFile := filepath.Join(bootstrap.BuildDir, "soong_build_metrics.pb")
 		err = android.WriteMetrics(configuration, metricsFile)
 		if err != nil {
@@ -103,3 +112,9 @@
 		}
 	}
 }
+
+func shouldPrepareBuildActions() bool {
+	// If we're writing soong_docs or bazel_overlay, don't write build.ninja or
+	// collect metrics.
+	return docFile == "" && bazelOverlayDir == ""
+}
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
index 2cf65fe..db5e97a 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -40,15 +40,15 @@
 	DisableGenerateProfile bool   // don't generate profiles
 	ProfileDir             string // directory to find profiles in
 
-	BootJars          []string // modules for jars that form the boot class path
-	UpdatableBootJars []string // jars within apex that form the boot class path
+	BootJars          android.ConfiguredJarList // modules for jars that form the boot class path
+	UpdatableBootJars android.ConfiguredJarList // jars within apex that form the boot class path
 
-	ArtApexJars []string // modules for jars that are in the ART APEX
+	ArtApexJars android.ConfiguredJarList // modules for jars that are in the ART APEX
 
-	SystemServerJars          []string // jars that form the system server
-	SystemServerApps          []string // apps that are loaded into system server
-	UpdatableSystemServerJars []string // jars within apex that are loaded into system server
-	SpeedApps                 []string // apps that should be speed optimized
+	SystemServerJars          []string                  // jars that form the system server
+	SystemServerApps          []string                  // apps that are loaded into system server
+	UpdatableSystemServerJars android.ConfiguredJarList // jars within apex that are loaded into system server
+	SpeedApps                 []string                  // apps that should be speed optimized
 
 	BrokenSuboptimalOrderOfSystemServerJars bool // if true, sub-optimal order does not cause a build error
 
@@ -189,8 +189,12 @@
 
 		// Copies of entries in GlobalConfig that are not constructable without extra parameters.  They will be
 		// used to construct the real value manually below.
-		DirtyImageObjects string
-		BootImageProfiles []string
+		BootJars                  []string
+		UpdatableBootJars         []string
+		ArtApexJars               []string
+		UpdatableSystemServerJars []string
+		DirtyImageObjects         string
+		BootImageProfiles         []string
 	}
 
 	config := GlobalJSONConfig{}
@@ -200,6 +204,10 @@
 	}
 
 	// Construct paths that require a PathContext.
+	config.GlobalConfig.BootJars = android.CreateConfiguredJarList(ctx, config.BootJars)
+	config.GlobalConfig.UpdatableBootJars = android.CreateConfiguredJarList(ctx, config.UpdatableBootJars)
+	config.GlobalConfig.ArtApexJars = android.CreateConfiguredJarList(ctx, config.ArtApexJars)
+	config.GlobalConfig.UpdatableSystemServerJars = android.CreateConfiguredJarList(ctx, config.UpdatableSystemServerJars)
 	config.GlobalConfig.DirtyImageObjects = android.OptionalPathForPath(constructPath(ctx, config.DirtyImageObjects))
 	config.GlobalConfig.BootImageProfiles = constructPaths(ctx, config.BootImageProfiles)
 
@@ -530,12 +538,12 @@
 		PatternsOnSystemOther:              nil,
 		DisableGenerateProfile:             false,
 		ProfileDir:                         "",
-		BootJars:                           nil,
-		UpdatableBootJars:                  nil,
-		ArtApexJars:                        nil,
+		BootJars:                           android.EmptyConfiguredJarList(),
+		UpdatableBootJars:                  android.EmptyConfiguredJarList(),
+		ArtApexJars:                        android.EmptyConfiguredJarList(),
 		SystemServerJars:                   nil,
 		SystemServerApps:                   nil,
-		UpdatableSystemServerJars:          nil,
+		UpdatableSystemServerJars:          android.EmptyConfiguredJarList(),
 		SpeedApps:                          nil,
 		PreoptFlags:                        nil,
 		DefaultCompilerFilter:              "",
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index e49fa98..8c9f0a2 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -83,7 +83,7 @@
 
 	if !dexpreoptDisabled(ctx, global, module) {
 		// Don't preopt individual boot jars, they will be preopted together.
-		if !contains(android.GetJarsFromApexJarPairs(ctx, global.BootJars), module.Name) {
+		if !global.BootJars.ContainsJar(module.Name) {
 			appImage := (generateProfile || module.ForceCreateAppImage || global.DefaultAppImages) &&
 				!module.NoCreateAppImage
 
@@ -104,17 +104,15 @@
 	}
 
 	// Don't preopt system server jars that are updatable.
-	for _, p := range global.UpdatableSystemServerJars {
-		if _, jar := android.SplitApexJarPair(ctx, p); jar == module.Name {
-			return true
-		}
+	if global.UpdatableSystemServerJars.ContainsJar(module.Name) {
+		return true
 	}
 
 	// If OnlyPreoptBootImageAndSystemServer=true and module is not in boot class path skip
 	// Also preopt system server jars since selinux prevents system server from loading anything from
 	// /data. If we don't do this they will need to be extracted which is not favorable for RAM usage
 	// or performance. If PreoptExtractedApk is true, we ignore the only preopt boot image options.
-	if global.OnlyPreoptBootImageAndSystemServer && !contains(android.GetJarsFromApexJarPairs(ctx, global.BootJars), module.Name) &&
+	if global.OnlyPreoptBootImageAndSystemServer && !global.BootJars.ContainsJar(module.Name) &&
 		!contains(global.SystemServerJars, module.Name) && !module.PreoptExtractedApk {
 		return true
 	}
@@ -571,20 +569,13 @@
 	}
 }
 
-// Expected format for apexJarValue = <apex name>:<jar name>
-func GetJarLocationFromApexJarPair(ctx android.PathContext, apexJarValue string) string {
-	apex, jar := android.SplitApexJarPair(ctx, apexJarValue)
-	return filepath.Join("/apex", apex, "javalib", jar+".jar")
-}
-
 var nonUpdatableSystemServerJarsKey = android.NewOnceKey("nonUpdatableSystemServerJars")
 
 // TODO: eliminate the superficial global config parameter by moving global config definition
 // from java subpackage to dexpreopt.
 func NonUpdatableSystemServerJars(ctx android.PathContext, global *GlobalConfig) []string {
 	return ctx.Config().Once(nonUpdatableSystemServerJarsKey, func() interface{} {
-		return android.RemoveListFromList(global.SystemServerJars,
-			android.GetJarsFromApexJarPairs(ctx, global.UpdatableSystemServerJars))
+		return android.RemoveListFromList(global.SystemServerJars, global.UpdatableSystemServerJars.CopyOfJars())
 	}).([]string)
 }
 
diff --git a/java/aar.go b/java/aar.go
index ad9b5e7..778e1cd 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -625,7 +625,7 @@
 }
 
 func (a *AARImport) DepsMutator(ctx android.BottomUpMutatorContext) {
-	if !ctx.Config().UnbundledBuildUsePrebuiltSdks() {
+	if !ctx.Config().AlwaysUsePrebuiltSdks() {
 		sdkDep := decodeSdkDep(ctx, sdkContext(a))
 		if sdkDep.useModule && sdkDep.frameworkResModule != "" {
 			ctx.AddVariationDependencies(nil, frameworkResTag, sdkDep.frameworkResModule)
@@ -641,9 +641,11 @@
 var unzipAAR = pctx.AndroidStaticRule("unzipAAR",
 	blueprint.RuleParams{
 		Command: `rm -rf $outDir && mkdir -p $outDir && ` +
-			`unzip -qoDD -d $outDir $in && rm -rf $outDir/res && touch $out`,
+			`unzip -qoDD -d $outDir $in && rm -rf $outDir/res && touch $out && ` +
+			`${config.MergeZipsCmd} $combinedClassesJar $$(ls $outDir/classes.jar 2> /dev/null) $$(ls $outDir/libs/*.jar 2> /dev/null)`,
+		CommandDeps: []string{"${config.MergeZipsCmd}"},
 	},
-	"outDir")
+	"outDir", "combinedClassesJar")
 
 func (a *AARImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	if len(a.properties.Aars) != 1 {
@@ -661,7 +663,7 @@
 	}
 
 	extractedAARDir := android.PathForModuleOut(ctx, "aar")
-	a.classpathFile = extractedAARDir.Join(ctx, "classes.jar")
+	a.classpathFile = extractedAARDir.Join(ctx, "classes-combined.jar")
 	a.proguardFlags = extractedAARDir.Join(ctx, "proguard.txt")
 	a.manifest = extractedAARDir.Join(ctx, "AndroidManifest.xml")
 
@@ -671,7 +673,8 @@
 		Outputs:     android.WritablePaths{a.classpathFile, a.proguardFlags, a.manifest},
 		Description: "unzip AAR",
 		Args: map[string]string{
-			"outDir": extractedAARDir.String(),
+			"outDir":             extractedAARDir.String(),
+			"combinedClassesJar": a.classpathFile.String(),
 		},
 	})
 
diff --git a/java/androidmk.go b/java/androidmk.go
index 25dd329..bc327cf 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -127,8 +127,8 @@
 						entries.AddStrings("LOCAL_ADDITIONAL_CHECKED_MODULE", library.additionalCheckedModules.Strings()...)
 					}
 
-					if library.proguardDictionary != nil {
-						entries.SetPath("LOCAL_SOONG_PROGUARD_DICT", library.proguardDictionary)
+					if library.dexer.proguardDictionary.Valid() {
+						entries.SetPath("LOCAL_SOONG_PROGUARD_DICT", library.dexer.proguardDictionary.Path())
 					}
 					entries.SetString("LOCAL_MODULE_STEM", library.Stem())
 
@@ -332,8 +332,8 @@
 				if app.jacocoReportClassesFile != nil {
 					entries.SetPath("LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR", app.jacocoReportClassesFile)
 				}
-				if app.proguardDictionary != nil {
-					entries.SetPath("LOCAL_SOONG_PROGUARD_DICT", app.proguardDictionary)
+				if app.dexer.proguardDictionary.Valid() {
+					entries.SetPath("LOCAL_SOONG_PROGUARD_DICT", app.dexer.proguardDictionary.Path())
 				}
 
 				if app.Name() == "framework-res" {
@@ -559,15 +559,21 @@
 	// are created in make if only the api txt file is being generated. This is
 	// needed because an invalid output file would prevent the make entries from
 	// being written.
+	//
+	// Note that dstubs.apiFile can be also be nil if WITHOUT_CHECKS_API is true.
 	// TODO(b/146727827): Revert when we do not need to generate stubs and API separately.
-	distFile := dstubs.apiFile
+
+	var distFiles android.TaggedDistFiles
+	if dstubs.apiFile != nil {
+		distFiles = android.MakeDefaultDistFiles(dstubs.apiFile)
+	}
 	outputFile := android.OptionalPathForPath(dstubs.stubsSrcJar)
 	if !outputFile.Valid() {
-		outputFile = android.OptionalPathForPath(distFile)
+		outputFile = android.OptionalPathForPath(dstubs.apiFile)
 	}
 	return []android.AndroidMkEntries{android.AndroidMkEntries{
 		Class:      "JAVA_LIBRARIES",
-		DistFiles:  android.MakeDefaultDistFiles(distFile),
+		DistFiles:  distFiles,
 		OutputFile: outputFile,
 		Include:    "$(BUILD_SYSTEM)/soong_droiddoc_prebuilt.mk",
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
@@ -728,7 +734,7 @@
 			ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 				func(entries *android.AndroidMkEntries) {
 					entries.SetBoolIfTrue("LOCAL_PRIVILEGED_MODULE", apkSet.Privileged())
-					entries.SetString("LOCAL_APK_SET_MASTER_FILE", apkSet.masterFile)
+					entries.SetString("LOCAL_APK_SET_INSTALL_FILE", apkSet.InstallFile())
 					entries.SetPath("LOCAL_APKCERTS_FILE", apkSet.apkcertsFile)
 					entries.AddStrings("LOCAL_OVERRIDES_PACKAGES", apkSet.properties.Overrides...)
 				},
diff --git a/java/app.go b/java/app.go
index 4031cfe..8a0b3db 100755
--- a/java/app.go
+++ b/java/app.go
@@ -78,7 +78,7 @@
 
 	properties   AndroidAppSetProperties
 	packedOutput android.WritablePath
-	masterFile   string
+	installFile  string
 	apkcertsFile android.ModuleOutPath
 }
 
@@ -102,8 +102,8 @@
 	return as.packedOutput
 }
 
-func (as *AndroidAppSet) MasterFile() string {
-	return as.masterFile
+func (as *AndroidAppSet) InstallFile() string {
+	return as.installFile
 }
 
 func (as *AndroidAppSet) APKCertsFile() android.Path {
@@ -136,10 +136,10 @@
 func (as *AndroidAppSet) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	as.packedOutput = android.PathForModuleOut(ctx, ctx.ModuleName()+".zip")
 	as.apkcertsFile = android.PathForModuleOut(ctx, "apkcerts.txt")
-	// We are assuming here that the master file in the APK
+	// We are assuming here that the install file in the APK
 	// set has `.apk` suffix. If it doesn't the build will fail.
 	// APK sets containing APEX files are handled elsewhere.
-	as.masterFile = as.BaseModuleName() + ".apk"
+	as.installFile = as.BaseModuleName() + ".apk"
 	screenDensities := "all"
 	if dpis := ctx.Config().ProductAAPTPrebuiltDPI(); len(dpis) > 0 {
 		screenDensities = strings.ToUpper(strings.Join(dpis, ","))
@@ -167,7 +167,7 @@
 
 // android_app_set extracts a set of APKs based on the target device
 // configuration and installs this set as "split APKs".
-// The extracted set always contains 'master' APK whose name is
+// The extracted set always contains an APK whose name is
 // _module_name_.apk and every split APK matching target device.
 // The extraction of the density-specific splits depends on
 // PRODUCT_AAPT_PREBUILT_DPI variable. If present (its value should
@@ -588,11 +588,11 @@
 
 func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext) android.Path {
 	a.dexpreopter.installPath = a.installPath(ctx)
-	if a.deviceProperties.Uncompress_dex == nil {
+	if a.dexProperties.Uncompress_dex == nil {
 		// If the value was not force-set by the user, use reasonable default based on the module.
-		a.deviceProperties.Uncompress_dex = proptools.BoolPtr(a.shouldUncompressDex(ctx))
+		a.dexProperties.Uncompress_dex = proptools.BoolPtr(a.shouldUncompressDex(ctx))
 	}
-	a.dexpreopter.uncompressedDex = *a.deviceProperties.Uncompress_dex
+	a.dexpreopter.uncompressedDex = *a.dexProperties.Uncompress_dex
 	a.dexpreopter.enforceUsesLibs = a.usesLibrary.enforceUsesLibraries()
 	a.dexpreopter.usesLibs = a.usesLibrary.usesLibraryProperties.Uses_libs
 	a.dexpreopter.optionalUsesLibs = a.usesLibrary.presentOptionalUsesLibs(ctx)
@@ -845,7 +845,9 @@
 		otherName := ctx.OtherModuleName(module)
 		tag := ctx.OtherModuleDependencyTag(module)
 
-		if IsJniDepTag(tag) || tag == cc.SharedDepTag {
+		// TODO(ccross): The tag == cc.SharedDepTag() check should be cc.IsSharedDepTag(tag) but
+		//   was left to maintain behavior when adding libraryDependencyTag.
+		if IsJniDepTag(tag) || tag == cc.SharedDepTag() {
 			if dep, ok := module.(*cc.Module); ok {
 				if dep.IsNdk() || dep.IsStubs() {
 					return false
@@ -995,8 +997,8 @@
 func AndroidAppFactory() android.Module {
 	module := &AndroidApp{}
 
-	module.Module.deviceProperties.Optimize.EnabledByDefault = true
-	module.Module.deviceProperties.Optimize.Shrink = proptools.BoolPtr(true)
+	module.Module.dexProperties.Optimize.EnabledByDefault = true
+	module.Module.dexProperties.Optimize.Shrink = proptools.BoolPtr(true)
 
 	module.Module.properties.Instrument = true
 	module.Module.properties.Installable = proptools.BoolPtr(true)
@@ -1110,7 +1112,7 @@
 func AndroidTestFactory() android.Module {
 	module := &AndroidTest{}
 
-	module.Module.deviceProperties.Optimize.EnabledByDefault = true
+	module.Module.dexProperties.Optimize.EnabledByDefault = true
 
 	module.Module.properties.Instrument = true
 	module.Module.properties.Installable = proptools.BoolPtr(true)
@@ -1161,7 +1163,7 @@
 func AndroidTestHelperAppFactory() android.Module {
 	module := &AndroidTestHelperApp{}
 
-	module.Module.deviceProperties.Optimize.EnabledByDefault = true
+	module.Module.dexProperties.Optimize.EnabledByDefault = true
 
 	module.Module.properties.Installable = proptools.BoolPtr(true)
 	module.appProperties.Use_embedded_native_libs = proptools.BoolPtr(true)
diff --git a/java/app_test.go b/java/app_test.go
index d4323bb..f50aa3a 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -161,11 +161,11 @@
 		t.Errorf("wrong partition value: '%s', expected 'system'", s)
 	}
 	mkEntries := android.AndroidMkEntriesForTest(t, config, "", module.Module())[0]
-	actualMaster := mkEntries.EntryMap["LOCAL_APK_SET_MASTER_FILE"]
-	expectedMaster := []string{"foo.apk"}
-	if !reflect.DeepEqual(actualMaster, expectedMaster) {
-		t.Errorf("Unexpected LOCAL_APK_SET_MASTER_FILE value: '%s', expected: '%s',",
-			actualMaster, expectedMaster)
+	actualInstallFile := mkEntries.EntryMap["LOCAL_APK_SET_INSTALL_FILE"]
+	expectedInstallFile := []string{"foo.apk"}
+	if !reflect.DeepEqual(actualInstallFile, expectedInstallFile) {
+		t.Errorf("Unexpected LOCAL_APK_SET_INSTALL_FILE value: '%s', expected: '%s',",
+			actualInstallFile, expectedInstallFile)
 	}
 }
 
@@ -2866,6 +2866,7 @@
 		config := testAppConfig(nil, bp, nil)
 		if unbundled {
 			config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true)
+			config.TestProductVariables.Always_use_prebuilt_sdks = proptools.BoolPtr(true)
 		}
 
 		ctx := testContext()
diff --git a/java/config/config.go b/java/config/config.go
index d2f4513..2f39c99 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -128,7 +128,7 @@
 	pctx.HostBinToolVariable("ExtractApksCmd", "extract_apks")
 	pctx.VariableFunc("TurbineJar", func(ctx android.PackageVarContext) string {
 		turbine := "turbine.jar"
-		if ctx.Config().UnbundledBuild() {
+		if ctx.Config().AlwaysUsePrebuiltSdks() {
 			return "prebuilts/build-tools/common/framework/" + turbine
 		} else {
 			return ctx.Config().HostJavaToolPath(ctx, turbine).String()
@@ -178,7 +178,7 @@
 
 func hostBinToolVariableWithSdkToolsPrebuilt(name, tool string) {
 	pctx.VariableFunc(name, func(ctx android.PackageVarContext) string {
-		if ctx.Config().UnbundledBuild() || ctx.Config().IsPdkBuild() {
+		if ctx.Config().AlwaysUsePrebuiltSdks() {
 			return filepath.Join("prebuilts/sdk/tools", runtime.GOOS, "bin", tool)
 		} else {
 			return ctx.Config().HostToolPath(ctx, tool).String()
@@ -188,7 +188,7 @@
 
 func hostJavaToolVariableWithSdkToolsPrebuilt(name, tool string) {
 	pctx.VariableFunc(name, func(ctx android.PackageVarContext) string {
-		if ctx.Config().UnbundledBuild() || ctx.Config().IsPdkBuild() {
+		if ctx.Config().AlwaysUsePrebuiltSdks() {
 			return filepath.Join("prebuilts/sdk/tools/lib", tool+".jar")
 		} else {
 			return ctx.Config().HostJavaToolPath(ctx, tool+".jar").String()
@@ -198,7 +198,7 @@
 
 func hostJNIToolVariableWithSdkToolsPrebuilt(name, tool string) {
 	pctx.VariableFunc(name, func(ctx android.PackageVarContext) string {
-		if ctx.Config().UnbundledBuild() || ctx.Config().IsPdkBuild() {
+		if ctx.Config().AlwaysUsePrebuiltSdks() {
 			ext := ".so"
 			if runtime.GOOS == "darwin" {
 				ext = ".dylib"
@@ -212,7 +212,7 @@
 
 func hostBinToolVariableWithBuildToolsPrebuilt(name, tool string) {
 	pctx.VariableFunc(name, func(ctx android.PackageVarContext) string {
-		if ctx.Config().UnbundledBuild() || ctx.Config().IsPdkBuild() {
+		if ctx.Config().AlwaysUsePrebuiltSdks() {
 			return filepath.Join("prebuilts/build-tools", ctx.Config().PrebuiltOS(), "bin", tool)
 		} else {
 			return ctx.Config().HostToolPath(ctx, tool).String()
diff --git a/java/dex.go b/java/dex.go
index 9e61e95..cd45a93 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -24,6 +24,60 @@
 	"android/soong/remoteexec"
 )
 
+type DexProperties struct {
+	// If set to true, compile dex regardless of installable.  Defaults to false.
+	Compile_dex *bool
+
+	// list of module-specific flags that will be used for dex compiles
+	Dxflags []string `android:"arch_variant"`
+
+	Optimize struct {
+		// If false, disable all optimization.  Defaults to true for android_app and android_test
+		// modules, false for java_library and java_test modules.
+		Enabled *bool
+		// True if the module containing this has it set by default.
+		EnabledByDefault bool `blueprint:"mutated"`
+
+		// If true, optimize for size by removing unused code.  Defaults to true for apps,
+		// false for libraries and tests.
+		Shrink *bool
+
+		// If true, optimize bytecode.  Defaults to false.
+		Optimize *bool
+
+		// If true, obfuscate bytecode.  Defaults to false.
+		Obfuscate *bool
+
+		// If true, do not use the flag files generated by aapt that automatically keep
+		// classes referenced by the app manifest.  Defaults to false.
+		No_aapt_flags *bool
+
+		// Flags to pass to proguard.
+		Proguard_flags []string
+
+		// Specifies the locations of files containing proguard flags.
+		Proguard_flags_files []string `android:"path"`
+	}
+
+	// Keep the data uncompressed. We always need uncompressed dex for execution,
+	// so this might actually save space by avoiding storing the same data twice.
+	// This defaults to reasonable value based on module and should not be set.
+	// It exists only to support ART tests.
+	Uncompress_dex *bool
+}
+
+type dexer struct {
+	dexProperties DexProperties
+
+	// list of extra proguard flag files
+	extraProguardFlagFiles android.Paths
+	proguardDictionary     android.OptionalPath
+}
+
+func (d *dexer) effectiveOptimizeEnabled() bool {
+	return BoolDefault(d.dexProperties.Optimize.Enabled, d.dexProperties.Optimize.EnabledByDefault)
+}
+
 var d8, d8RE = remoteexec.MultiCommandStaticRules(pctx, "d8",
 	blueprint.RuleParams{
 		Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
@@ -86,8 +140,8 @@
 		},
 	}, []string{"outDir", "outDict", "r8Flags", "zipFlags"}, []string{"implicits"})
 
-func (j *Module) dexCommonFlags(ctx android.ModuleContext) []string {
-	flags := j.deviceProperties.Dxflags
+func (d *dexer) dexCommonFlags(ctx android.ModuleContext, minSdkVersion sdkSpec) []string {
+	flags := d.dexProperties.Dxflags
 	// Translate all the DX flags to D8 ones until all the build files have been migrated
 	// to D8 flags. See: b/69377755
 	flags = android.RemoveListFromList(flags,
@@ -103,30 +157,27 @@
 			"--verbose")
 	}
 
-	minSdkVersion, err := j.minSdkVersion().effectiveVersion(ctx)
+	effectiveVersion, err := minSdkVersion.effectiveVersion(ctx)
 	if err != nil {
 		ctx.PropertyErrorf("min_sdk_version", "%s", err)
 	}
 
-	flags = append(flags, "--min-api "+minSdkVersion.asNumberString())
+	flags = append(flags, "--min-api "+effectiveVersion.asNumberString())
 	return flags
 }
 
-func (j *Module) d8Flags(ctx android.ModuleContext, flags javaBuilderFlags) ([]string, android.Paths) {
-	d8Flags := j.dexCommonFlags(ctx)
-
+func d8Flags(flags javaBuilderFlags) (d8Flags []string, d8Deps android.Paths) {
 	d8Flags = append(d8Flags, flags.bootClasspath.FormRepeatedClassPath("--lib ")...)
 	d8Flags = append(d8Flags, flags.classpath.FormRepeatedClassPath("--lib ")...)
 
-	var d8Deps android.Paths
 	d8Deps = append(d8Deps, flags.bootClasspath...)
 	d8Deps = append(d8Deps, flags.classpath...)
 
 	return d8Flags, d8Deps
 }
 
-func (j *Module) r8Flags(ctx android.ModuleContext, flags javaBuilderFlags) (r8Flags []string, r8Deps android.Paths) {
-	opt := j.deviceProperties.Optimize
+func (d *dexer) r8Flags(ctx android.ModuleContext, flags javaBuilderFlags) (r8Flags []string, r8Deps android.Paths) {
+	opt := d.dexProperties.Optimize
 
 	// When an app contains references to APIs that are not in the SDK specified by
 	// its LOCAL_SDK_VERSION for example added by support library or by runtime
@@ -140,8 +191,6 @@
 		proguardRaiseDeps = append(proguardRaiseDeps, dep.(Dependency).HeaderJars()...)
 	})
 
-	r8Flags = append(r8Flags, j.dexCommonFlags(ctx)...)
-
 	r8Flags = append(r8Flags, proguardRaiseDeps.FormJavaClassPath("-libraryjars"))
 	r8Flags = append(r8Flags, flags.bootClasspath.FormJavaClassPath("-libraryjars"))
 	r8Flags = append(r8Flags, flags.classpath.FormJavaClassPath("-libraryjars"))
@@ -154,15 +203,10 @@
 		android.PathForSource(ctx, "build/make/core/proguard.flags"),
 	}
 
-	if j.shouldInstrumentStatic(ctx) {
-		flagFiles = append(flagFiles,
-			android.PathForSource(ctx, "build/make/core/proguard.jacoco.flags"))
-	}
-
-	flagFiles = append(flagFiles, j.extraProguardFlagFiles...)
+	flagFiles = append(flagFiles, d.extraProguardFlagFiles...)
 	// TODO(ccross): static android library proguard files
 
-	flagFiles = append(flagFiles, android.PathsForModuleSrc(ctx, j.deviceProperties.Optimize.Proguard_flags_files)...)
+	flagFiles = append(flagFiles, android.PathsForModuleSrc(ctx, opt.Proguard_flags_files)...)
 
 	r8Flags = append(r8Flags, android.JoinWithPrefix(flagFiles.Strings(), "-include "))
 	r8Deps = append(r8Deps, flagFiles...)
@@ -171,7 +215,7 @@
 	r8Deps = append(r8Deps, android.PathForSource(ctx,
 		"build/make/core/proguard_basic_keeps.flags"))
 
-	r8Flags = append(r8Flags, j.deviceProperties.Optimize.Proguard_flags...)
+	r8Flags = append(r8Flags, opt.Proguard_flags...)
 
 	// TODO(ccross): Don't shrink app instrumentation tests by default.
 	if !Bool(opt.Shrink) {
@@ -197,29 +241,30 @@
 	return r8Flags, r8Deps
 }
 
-func (j *Module) compileDex(ctx android.ModuleContext, flags javaBuilderFlags,
+func (d *dexer) compileDex(ctx android.ModuleContext, flags javaBuilderFlags, minSdkVersion sdkSpec,
 	classesJar android.Path, jarName string) android.ModuleOutPath {
 
-	useR8 := j.deviceProperties.EffectiveOptimizeEnabled()
-
 	// Compile classes.jar into classes.dex and then javalib.jar
 	javalibJar := android.PathForModuleOut(ctx, "dex", jarName)
 	outDir := android.PathForModuleOut(ctx, "dex")
 
 	zipFlags := "--ignore_missing_files"
-	if proptools.Bool(j.deviceProperties.Uncompress_dex) {
+	if proptools.Bool(d.dexProperties.Uncompress_dex) {
 		zipFlags += " -L 0"
 	}
 
+	commonFlags := d.dexCommonFlags(ctx, minSdkVersion)
+
+	useR8 := d.effectiveOptimizeEnabled()
 	if useR8 {
 		proguardDictionary := android.PathForModuleOut(ctx, "proguard_dictionary")
-		j.proguardDictionary = proguardDictionary
-		r8Flags, r8Deps := j.r8Flags(ctx, flags)
+		d.proguardDictionary = android.OptionalPathForPath(proguardDictionary)
+		r8Flags, r8Deps := d.r8Flags(ctx, flags)
 		rule := r8
 		args := map[string]string{
-			"r8Flags":  strings.Join(r8Flags, " "),
+			"r8Flags":  strings.Join(append(commonFlags, r8Flags...), " "),
 			"zipFlags": zipFlags,
-			"outDict":  j.proguardDictionary.String(),
+			"outDict":  proguardDictionary.String(),
 			"outDir":   outDir.String(),
 		}
 		if ctx.Config().IsEnvTrue("RBE_R8") {
@@ -236,7 +281,7 @@
 			Args:           args,
 		})
 	} else {
-		d8Flags, d8Deps := j.d8Flags(ctx, flags)
+		d8Flags, d8Deps := d8Flags(flags)
 		rule := d8
 		if ctx.Config().IsEnvTrue("RBE_D8") {
 			rule = d8RE
@@ -248,13 +293,13 @@
 			Input:       classesJar,
 			Implicits:   d8Deps,
 			Args: map[string]string{
-				"d8Flags":  strings.Join(d8Flags, " "),
+				"d8Flags":  strings.Join(append(commonFlags, d8Flags...), " "),
 				"zipFlags": zipFlags,
 				"outDir":   outDir.String(),
 			},
 		})
 	}
-	if proptools.Bool(j.deviceProperties.Uncompress_dex) {
+	if proptools.Bool(d.dexProperties.Uncompress_dex) {
 		alignedJavalibJar := android.PathForModuleOut(ctx, "aligned", jarName)
 		TransformZipAlign(ctx, alignedJavalibJar, javalibJar)
 		javalibJar = alignedJavalibJar
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index 4120559..b445456 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -49,8 +49,8 @@
 	// Subdirectory where the image files are installed.
 	installSubdir string
 
-	// The names of jars that constitute this image.
-	modules []string
+	// A list of (location, jar) pairs for the Java modules in this image.
+	modules android.ConfiguredJarList
 
 	// File paths to jars.
 	dexPaths     android.WritablePaths // for this image
@@ -113,16 +113,16 @@
 	// Dexpreopt on the boot class path produces multiple files. The first dex file
 	// is converted into 'name'.art (to match the legacy assumption that 'name'.art
 	// exists), and the rest are converted to 'name'-<jar>.art.
-	_, m := android.SplitApexJarPair(ctx, image.modules[idx])
+	m := image.modules.Jar(idx)
 	name := image.stem
 	if idx != 0 || image.extends != nil {
-		name += "-" + stemOf(m)
+		name += "-" + android.ModuleStem(m)
 	}
 	return name
 }
 
 func (image bootImageConfig) firstModuleNameOrStem(ctx android.PathContext) string {
-	if len(image.modules) > 0 {
+	if image.modules.Len() > 0 {
 		return image.moduleName(ctx, 0)
 	} else {
 		return image.stem
@@ -130,8 +130,8 @@
 }
 
 func (image bootImageConfig) moduleFiles(ctx android.PathContext, dir android.OutputPath, exts ...string) android.OutputPaths {
-	ret := make(android.OutputPaths, 0, len(image.modules)*len(exts))
-	for i := range image.modules {
+	ret := make(android.OutputPaths, 0, image.modules.Len()*len(exts))
+	for i := 0; i < image.modules.Len(); i++ {
 		name := image.moduleName(ctx, i)
 		for _, ext := range exts {
 			ret = append(ret, dir.Join(ctx, name+ext))
@@ -253,7 +253,7 @@
 	}
 
 	name := ctx.ModuleName(module)
-	index := android.IndexList(name, android.GetJarsFromApexJarPairs(ctx, image.modules))
+	index := image.modules.IndexOfJar(name)
 	if index == -1 {
 		return -1, nil
 	}
@@ -295,7 +295,7 @@
 func buildBootImage(ctx android.SingletonContext, image *bootImageConfig) *bootImageConfig {
 	// Collect dex jar paths for the boot image modules.
 	// This logic is tested in the apex package to avoid import cycle apex <-> java.
-	bootDexJars := make(android.Paths, len(image.modules))
+	bootDexJars := make(android.Paths, image.modules.Len())
 	ctx.VisitAllModules(func(module android.Module) {
 		if i, j := getBootImageJar(ctx, image, module); i != -1 {
 			bootDexJars[i] = j
@@ -306,7 +306,7 @@
 	// Ensure all modules were converted to paths
 	for i := range bootDexJars {
 		if bootDexJars[i] == nil {
-			_, m := android.SplitApexJarPair(ctx, image.modules[i])
+			m := image.modules.Jar(i)
 			if ctx.Config().AllowMissingDependencies() {
 				missingDeps = append(missingDeps, m)
 				bootDexJars[i] = android.PathForOutput(ctx, "missing")
@@ -505,7 +505,7 @@
 	globalSoong := dexpreopt.GetCachedGlobalSoongConfig(ctx)
 	global := dexpreopt.GetGlobalConfig(ctx)
 
-	if global.DisableGenerateProfile || ctx.Config().IsPdkBuild() || ctx.Config().UnbundledBuild() {
+	if global.DisableGenerateProfile || ctx.Config().UnbundledBuild() {
 		return nil
 	}
 	profile := ctx.Config().Once(bootImageProfileRuleKey, func() interface{} {
@@ -560,7 +560,7 @@
 	globalSoong := dexpreopt.GetCachedGlobalSoongConfig(ctx)
 	global := dexpreopt.GetGlobalConfig(ctx)
 
-	if global.DisableGenerateProfile || ctx.Config().IsPdkBuild() || ctx.Config().UnbundledBuild() {
+	if global.DisableGenerateProfile || ctx.Config().UnbundledBuild() {
 		return nil
 	}
 	return ctx.Config().Once(bootFrameworkProfileRuleKey, func() interface{} {
@@ -602,13 +602,13 @@
 var bootFrameworkProfileRuleKey = android.NewOnceKey("bootFrameworkProfileRule")
 
 func updatableBcpPackagesRule(ctx android.SingletonContext, image *bootImageConfig, missingDeps []string) android.WritablePath {
-	if ctx.Config().IsPdkBuild() || ctx.Config().UnbundledBuild() {
+	if ctx.Config().UnbundledBuild() {
 		return nil
 	}
 
 	return ctx.Config().Once(updatableBcpPackagesRuleKey, func() interface{} {
 		global := dexpreopt.GetGlobalConfig(ctx)
-		updatableModules := android.GetJarsFromApexJarPairs(ctx, global.UpdatableBootJars)
+		updatableModules := global.UpdatableBootJars.CopyOfJars()
 
 		// Collect `permitted_packages` for updatable boot jars.
 		var updatablePackages []string
diff --git a/java/dexpreopt_bootjars_test.go b/java/dexpreopt_bootjars_test.go
index 9670c7f..4a8d3cd 100644
--- a/java/dexpreopt_bootjars_test.go
+++ b/java/dexpreopt_bootjars_test.go
@@ -48,7 +48,7 @@
 
 	pathCtx := android.PathContextForTesting(config)
 	dexpreoptConfig := dexpreopt.GlobalConfigForTests(pathCtx)
-	dexpreoptConfig.BootJars = []string{"platform:foo", "platform:bar", "platform:baz"}
+	dexpreoptConfig.BootJars = android.CreateConfiguredJarList(pathCtx, []string{"platform:foo", "platform:bar", "platform:baz"})
 	dexpreopt.SetTestGlobalConfig(config, dexpreoptConfig)
 
 	ctx := testContext()
diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go
index f13d9f2..f0d82ff 100644
--- a/java/dexpreopt_config.go
+++ b/java/dexpreopt_config.go
@@ -37,14 +37,12 @@
 				filepath.Join("/system/framework", m+".jar"))
 		}
 		// 2) The jars that are from an updatable apex.
-		for _, m := range global.UpdatableSystemServerJars {
-			systemServerClasspathLocations = append(systemServerClasspathLocations,
-				dexpreopt.GetJarLocationFromApexJarPair(ctx, m))
-		}
-		if len(systemServerClasspathLocations) != len(global.SystemServerJars)+len(global.UpdatableSystemServerJars) {
+		systemServerClasspathLocations = append(systemServerClasspathLocations,
+			global.UpdatableSystemServerJars.DevicePaths(ctx.Config(), android.Android)...)
+		if len(systemServerClasspathLocations) != len(global.SystemServerJars)+global.UpdatableSystemServerJars.Len() {
 			panic(fmt.Errorf("Wrong number of system server jars, got %d, expected %d",
 				len(systemServerClasspathLocations),
-				len(global.SystemServerJars)+len(global.UpdatableSystemServerJars)))
+				len(global.SystemServerJars)+global.UpdatableSystemServerJars.Len()))
 		}
 		return systemServerClasspathLocations
 	})
@@ -69,39 +67,6 @@
 	return targets
 }
 
-func stemOf(moduleName string) string {
-	// b/139391334: the stem of framework-minus-apex is framework
-	// This is hard coded here until we find a good way to query the stem
-	// of a module before any other mutators are run
-	if moduleName == "framework-minus-apex" {
-		return "framework"
-	}
-	return moduleName
-}
-
-func getDexLocation(ctx android.PathContext, target android.Target, module string) string {
-	apex, jar := android.SplitApexJarPair(ctx, module)
-
-	name := stemOf(jar) + ".jar"
-
-	var subdir string
-	if apex == "platform" {
-		// Special apex name "platform" denotes jars do not come from an apex, but are part
-		// of the platform. Such jars are installed on the /system partition on device.
-		subdir = "system/framework"
-	} else if apex == "system_ext" {
-		subdir = "system_ext/framework"
-	} else {
-		subdir = filepath.Join("apex", apex, "javalib")
-	}
-
-	if target.Os.Class == android.Host {
-		return filepath.Join(ctx.Config().Getenv("OUT_DIR"), "host", ctx.Config().PrebuiltOS(), subdir, name)
-	} else {
-		return filepath.Join("/", subdir, name)
-	}
-}
-
 var (
 	bootImageConfigKey     = android.NewOnceKey("bootImageConfig")
 	artBootImageName       = "art"
@@ -116,12 +81,13 @@
 		targets := dexpreoptTargets(ctx)
 		deviceDir := android.PathForOutput(ctx, ctx.Config().DeviceName())
 
-		artModules := global.ArtApexJars
+		artModules := global.ArtApexJars.CopyOf()
 		// With EMMA_INSTRUMENT_FRAMEWORK=true the Core libraries depend on jacoco.
 		if ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") {
-			artModules = append(artModules, "com.android.art:jacocoagent")
+			artModules.Append("com.android.art", "jacocoagent")
 		}
-		frameworkModules := android.RemoveListFromList(global.BootJars, artModules)
+		frameworkModules := global.BootJars.CopyOf()
+		frameworkModules.RemoveList(artModules)
 
 		artSubdir := "apex/com.android.art/javalib"
 		frameworkSubdir := "system/framework"
@@ -163,10 +129,7 @@
 			// Set up known paths for them, the singleton rules will copy them there.
 			// TODO(b/143682396): use module dependencies instead
 			inputDir := deviceDir.Join(ctx, "dex_"+c.name+"jars_input")
-			for _, m := range c.modules {
-				_, jar := android.SplitApexJarPair(ctx, m)
-				c.dexPaths = append(c.dexPaths, inputDir.Join(ctx, stemOf(jar)+".jar"))
-			}
+			c.dexPaths = c.modules.BuildPaths(ctx, inputDir)
 			c.dexPathsDeps = c.dexPaths
 
 			// Create target-specific variants.
@@ -178,9 +141,7 @@
 					target:          target,
 					images:          imageDir.Join(ctx, imageName),
 					imagesDeps:      c.moduleFiles(ctx, imageDir, ".art", ".oat", ".vdex"),
-				}
-				for _, m := range c.modules {
-					variant.dexLocations = append(variant.dexLocations, getDexLocation(ctx, target, m))
+					dexLocations:    c.modules.DevicePaths(ctx.Config(), target.Os),
 				}
 				variant.dexLocationsDeps = variant.dexLocations
 				c.variants = append(c.variants, variant)
@@ -213,10 +174,7 @@
 		global := dexpreopt.GetGlobalConfig(ctx)
 		image := defaultBootImageConfig(ctx)
 
-		updatableBootclasspath := make([]string, len(global.UpdatableBootJars))
-		for i, p := range global.UpdatableBootJars {
-			updatableBootclasspath[i] = dexpreopt.GetJarLocationFromApexJarPair(ctx, p)
-		}
+		updatableBootclasspath := global.UpdatableBootJars.DevicePaths(ctx.Config(), android.Android)
 
 		bootclasspath := append(copyOf(image.getAnyAndroidVariant().dexLocationsDeps), updatableBootclasspath...)
 		return bootclasspath
@@ -236,5 +194,5 @@
 	ctx.Strict("PRODUCT_DEX2OAT_BOOTCLASSPATH", strings.Join(defaultBootImageConfig(ctx).getAnyAndroidVariant().dexLocationsDeps, ":"))
 	ctx.Strict("PRODUCT_SYSTEM_SERVER_CLASSPATH", strings.Join(systemServerClasspath(ctx), ":"))
 
-	ctx.Strict("DEXPREOPT_BOOT_JARS_MODULES", strings.Join(defaultBootImageConfig(ctx).modules, ":"))
+	ctx.Strict("DEXPREOPT_BOOT_JARS_MODULES", strings.Join(defaultBootImageConfig(ctx).modules.CopyOfApexJarPairs(), ":"))
 }
diff --git a/java/droiddoc.go b/java/droiddoc.go
index 935b839..4c5f66c 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -198,7 +198,7 @@
 	// the generated removed Dex API filename by Doclava.
 	Removed_dex_api_filename *string
 
-	// if set to false, don't allow droiddoc to generate stubs source files. Defaults to true.
+	// if set to false, don't allow droiddoc to generate stubs source files. Defaults to false.
 	Create_stubs *bool
 
 	Check_api struct {
@@ -294,6 +294,9 @@
 	// the dirs which Metalava extracts API levels annotations from.
 	Api_levels_annotations_dirs []string
 
+	// the filename which Metalava extracts API levels annotations from. Defaults to android.jar.
+	Api_levels_jar_filename *string
+
 	// if set to true, collect the values used by the Dev tools and
 	// write them in files packaged with the SDK. Defaults to false.
 	Write_sdk_values *bool
@@ -870,6 +873,10 @@
 	}
 }
 
+func (d *Droiddoc) createStubs() bool {
+	return BoolDefault(d.properties.Create_stubs, false)
+}
+
 func (d *Droiddoc) stubsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsDir android.WritablePath) {
 	if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") ||
 		apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") ||
@@ -892,7 +899,7 @@
 		cmd.FlagWithOutput("-removedDexApi ", d.removedDexApiFile)
 	}
 
-	if BoolDefault(d.properties.Create_stubs, true) {
+	if d.createStubs() {
 		cmd.FlagWithArg("-stubs ", stubsDir.String())
 	}
 
@@ -1074,9 +1081,7 @@
 
 	rule.Build(pctx, ctx, "javadoc", desc)
 
-	if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") &&
-		!ctx.Config().IsPdkBuild() {
-
+	if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") {
 		apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Api_file))
 		removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Removed_api_file))
 
@@ -1143,9 +1148,7 @@
 		rule.Build(pctx, ctx, "doclavaCurrentApiUpdate", "update current API")
 	}
 
-	if apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") &&
-		!ctx.Config().IsPdkBuild() {
-
+	if apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") {
 		apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Api_file))
 		removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Removed_api_file))
 
@@ -1403,38 +1406,41 @@
 }
 
 func (d *Droidstubs) apiLevelsAnnotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
-	if Bool(d.properties.Api_levels_annotations_enabled) {
-		d.apiVersionsXml = android.PathForModuleOut(ctx, "api-versions.xml")
-
-		if len(d.properties.Api_levels_annotations_dirs) == 0 {
-			ctx.PropertyErrorf("api_levels_annotations_dirs",
-				"has to be non-empty if api levels annotations was enabled!")
-		}
-
-		cmd.FlagWithOutput("--generate-api-levels ", d.apiVersionsXml)
-		cmd.FlagWithInput("--apply-api-levels ", d.apiVersionsXml)
-		cmd.FlagWithArg("--current-version ", ctx.Config().PlatformSdkVersion())
-		cmd.FlagWithArg("--current-codename ", ctx.Config().PlatformSdkCodename())
-
-		ctx.VisitDirectDepsWithTag(metalavaAPILevelsAnnotationsDirTag, func(m android.Module) {
-			if t, ok := m.(*ExportedDroiddocDir); ok {
-				for _, dep := range t.deps {
-					if strings.HasSuffix(dep.String(), "android.jar") {
-						cmd.Implicit(dep)
-					}
-				}
-				cmd.FlagWithArg("--android-jar-pattern ", t.dir.String()+"/%/public/android.jar")
-			} else {
-				ctx.PropertyErrorf("api_levels_annotations_dirs",
-					"module %q is not a metalava api-levels-annotations dir", ctx.OtherModuleName(m))
-			}
-		})
-
+	if !Bool(d.properties.Api_levels_annotations_enabled) {
+		return
 	}
+
+	d.apiVersionsXml = android.PathForModuleOut(ctx, "api-versions.xml")
+
+	if len(d.properties.Api_levels_annotations_dirs) == 0 {
+		ctx.PropertyErrorf("api_levels_annotations_dirs",
+			"has to be non-empty if api levels annotations was enabled!")
+	}
+
+	cmd.FlagWithOutput("--generate-api-levels ", d.apiVersionsXml)
+	cmd.FlagWithInput("--apply-api-levels ", d.apiVersionsXml)
+	cmd.FlagWithArg("--current-version ", ctx.Config().PlatformSdkVersion())
+	cmd.FlagWithArg("--current-codename ", ctx.Config().PlatformSdkCodename())
+
+	filename := proptools.StringDefault(d.properties.Api_levels_jar_filename, "android.jar")
+
+	ctx.VisitDirectDepsWithTag(metalavaAPILevelsAnnotationsDirTag, func(m android.Module) {
+		if t, ok := m.(*ExportedDroiddocDir); ok {
+			for _, dep := range t.deps {
+				if strings.HasSuffix(dep.String(), filename) {
+					cmd.Implicit(dep)
+				}
+			}
+			cmd.FlagWithArg("--android-jar-pattern ", t.dir.String()+"/%/public/"+filename)
+		} else {
+			ctx.PropertyErrorf("api_levels_annotations_dirs",
+				"module %q is not a metalava api-levels-annotations dir", ctx.OtherModuleName(m))
+		}
+	})
 }
 
 func (d *Droidstubs) apiToXmlFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
-	if Bool(d.properties.Jdiff_enabled) && !ctx.Config().IsPdkBuild() && d.apiFile != nil {
+	if Bool(d.properties.Jdiff_enabled) && d.apiFile != nil {
 		if d.apiFile.String() == "" {
 			ctx.ModuleErrorf("API signature file has to be specified in Metalava when jdiff is enabled.")
 		}
@@ -1582,7 +1588,7 @@
 
 	// Add API lint options.
 
-	if BoolDefault(d.properties.Check_api.Api_lint.Enabled, false) && !ctx.Config().IsPdkBuild() {
+	if BoolDefault(d.properties.Check_api.Api_lint.Enabled, false) {
 		doApiLint = true
 
 		newSince := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Api_lint.New_since)
@@ -1640,8 +1646,7 @@
 
 	// Add "check released" options. (Detect incompatible API changes from the last public release)
 
-	if apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") &&
-		!ctx.Config().IsPdkBuild() {
+	if apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") {
 		doCheckReleased = true
 
 		if len(d.Javadoc.properties.Out) > 0 {
@@ -1718,8 +1723,7 @@
 
 	rule.Build(pctx, ctx, "metalava", "metalava merged")
 
-	if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") &&
-		!ctx.Config().IsPdkBuild() {
+	if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") {
 
 		if len(d.Javadoc.properties.Out) > 0 {
 			ctx.PropertyErrorf("out", "out property may not be combined with check_api")
@@ -1833,7 +1837,7 @@
 		rule.Build(pctx, ctx, "nullabilityWarningsCheck", "nullability warnings check")
 	}
 
-	if Bool(d.properties.Jdiff_enabled) && !ctx.Config().IsPdkBuild() {
+	if Bool(d.properties.Jdiff_enabled) {
 		if len(d.Javadoc.properties.Out) > 0 {
 			ctx.PropertyErrorf("out", "out property may not be combined with jdiff")
 		}
diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go
index 7afba2a..de4a90f 100644
--- a/java/hiddenapi_singleton.go
+++ b/java/hiddenapi_singleton.go
@@ -100,7 +100,7 @@
 	// Add the android.test.base to the set of stubs only if the android.test.base module is on
 	// the boot jars list as the runtime will only enforce hiddenapi access against modules on
 	// that list.
-	if inList("android.test.base", ctx.Config().BootJars()) && !ctx.Config().UnbundledBuildUsePrebuiltSdks() {
+	if inList("android.test.base", ctx.Config().BootJars()) && !ctx.Config().AlwaysUsePrebuiltSdks() {
 		publicStubModules = append(publicStubModules, "android.test.base.stubs")
 	}
 
@@ -248,18 +248,18 @@
 		FlagWithInput("--csv ", stubFlags).
 		Inputs(flagsCSV).
 		FlagWithInput("--unsupported ",
-			android.PathForSource(ctx, "frameworks/base/config/hiddenapi-greylist.txt")).
+			android.PathForSource(ctx, "frameworks/base/config/hiddenapi-unsupported.txt")).
 		FlagWithInput("--unsupported-ignore-conflicts ", combinedRemovedApis).
 		FlagWithInput("--max-target-q ",
-			android.PathForSource(ctx, "frameworks/base/config/hiddenapi-greylist-max-q.txt")).
+			android.PathForSource(ctx, "frameworks/base/config/hiddenapi-max-target-q.txt")).
 		FlagWithInput("--max-target-p ",
-			android.PathForSource(ctx, "frameworks/base/config/hiddenapi-greylist-max-p.txt")).
+			android.PathForSource(ctx, "frameworks/base/config/hiddenapi-max-target-p.txt")).
 		FlagWithInput("--max-target-o-ignore-conflicts ",
-			android.PathForSource(ctx, "frameworks/base/config/hiddenapi-greylist-max-o.txt")).
+			android.PathForSource(ctx, "frameworks/base/config/hiddenapi-max-target-o.txt")).
 		FlagWithInput("--blocked ",
-			android.PathForSource(ctx, "frameworks/base/config/hiddenapi-force-blacklist.txt")).
+			android.PathForSource(ctx, "frameworks/base/config/hiddenapi-force-blocked.txt")).
 		FlagWithInput("--unsupported-packages ",
-			android.PathForSource(ctx, "frameworks/base/config/hiddenapi-greylist-packages.txt")).
+			android.PathForSource(ctx, "frameworks/base/config/hiddenapi-unsupported-packages.txt")).
 		FlagWithOutput("--output ", tempPath)
 
 	commitChangeForRestat(rule, tempPath, outputPath)
diff --git a/java/java.go b/java/java.go
index bd476bc..d5375a5 100644
--- a/java/java.go
+++ b/java/java.go
@@ -264,9 +264,6 @@
 }
 
 type CompilerDeviceProperties struct {
-	// list of module-specific flags that will be used for dex compiles
-	Dxflags []string `android:"arch_variant"`
-
 	// if not blank, set to the version of the sdk to compile against.
 	// Defaults to compiling against the current platform.
 	Sdk_version *string
@@ -312,37 +309,6 @@
 		}
 	}
 
-	// If set to true, compile dex regardless of installable.  Defaults to false.
-	Compile_dex *bool
-
-	Optimize struct {
-		// If false, disable all optimization.  Defaults to true for android_app and android_test
-		// modules, false for java_library and java_test modules.
-		Enabled *bool
-		// True if the module containing this has it set by default.
-		EnabledByDefault bool `blueprint:"mutated"`
-
-		// If true, optimize for size by removing unused code.  Defaults to true for apps,
-		// false for libraries and tests.
-		Shrink *bool
-
-		// If true, optimize bytecode.  Defaults to false.
-		Optimize *bool
-
-		// If true, obfuscate bytecode.  Defaults to false.
-		Obfuscate *bool
-
-		// If true, do not use the flag files generated by aapt that automatically keep
-		// classes referenced by the app manifest.  Defaults to false.
-		No_aapt_flags *bool
-
-		// Flags to pass to proguard.
-		Proguard_flags []string
-
-		// Specifies the locations of files containing proguard flags.
-		Proguard_flags_files []string `android:"path"`
-	}
-
 	// When targeting 1.9 and above, override the modules to use with --system,
 	// otherwise provides defaults libraries to add to the bootclasspath.
 	System_modules *string
@@ -356,19 +322,9 @@
 	// set the name of the output
 	Stem *string
 
-	// Keep the data uncompressed. We always need uncompressed dex for execution,
-	// so this might actually save space by avoiding storing the same data twice.
-	// This defaults to reasonable value based on module and should not be set.
-	// It exists only to support ART tests.
-	Uncompress_dex *bool
-
 	IsSDKLibrary bool `blueprint:"mutated"`
 }
 
-func (me *CompilerDeviceProperties) EffectiveOptimizeEnabled() bool {
-	return BoolDefault(me.Optimize.Enabled, me.Optimize.EnabledByDefault)
-}
-
 // Functionality common to Module and Import
 //
 // It is embedded in Module so its functionality can be used by methods in Module
@@ -437,9 +393,6 @@
 	// output file containing uninstrumented classes that will be instrumented by jacoco
 	jacocoReportClassesFile android.Path
 
-	// output file containing mapping of obfuscated names
-	proguardDictionary android.Path
-
 	// output file of the module, which may be a classes jar or a dex jar
 	outputFile       android.Path
 	extraOutputFiles android.Paths
@@ -455,9 +408,6 @@
 	compiledJavaSrcs android.Paths
 	compiledSrcJars  android.Paths
 
-	// list of extra progurad flag files
-	extraProguardFlagFiles android.Paths
-
 	// manifest file to use instead of properties.Manifest
 	overrideManifest android.OptionalPath
 
@@ -484,6 +434,7 @@
 	extraResources android.Paths
 
 	hiddenAPI
+	dexer
 	dexpreopter
 	linter
 
@@ -507,6 +458,7 @@
 	j.addHostProperties()
 	j.AddProperties(
 		&j.deviceProperties,
+		&j.dexer.dexProperties,
 		&j.dexpreoptProperties,
 		&j.linter.properties,
 	)
@@ -519,7 +471,10 @@
 	case ".jar":
 		return android.Paths{j.implementationAndResourcesJar}, nil
 	case ".proguard_map":
-		return android.Paths{j.proguardDictionary}, nil
+		if j.dexer.proguardDictionary.Valid() {
+			return android.Paths{j.dexer.proguardDictionary.Path()}, nil
+		}
+		return nil, fmt.Errorf("%q was requested, but no output file was found.", tag)
 	default:
 		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
 	}
@@ -728,10 +683,10 @@
 			ctx.AddVariationDependencies(nil, bootClasspathTag, sdkDep.bootclasspath...)
 			ctx.AddVariationDependencies(nil, java9LibTag, sdkDep.java9Classpath...)
 			ctx.AddVariationDependencies(nil, libTag, sdkDep.classpath...)
-			if j.deviceProperties.EffectiveOptimizeEnabled() && sdkDep.hasStandardLibs() {
+			if j.effectiveOptimizeEnabled() && sdkDep.hasStandardLibs() {
 				ctx.AddVariationDependencies(nil, proguardRaiseTag, config.LegacyCorePlatformBootclasspathLibraries...)
 			}
-			if j.deviceProperties.EffectiveOptimizeEnabled() && sdkDep.hasFrameworkLibs() {
+			if j.effectiveOptimizeEnabled() && sdkDep.hasFrameworkLibs() {
 				ctx.AddVariationDependencies(nil, proguardRaiseTag, config.FrameworkLibraries...)
 			}
 		}
@@ -1647,8 +1602,8 @@
 
 	// Enable dex compilation for the APEX variants, unless it is disabled explicitly
 	if android.DirectlyInAnyApex(ctx, ctx.ModuleName()) && !j.IsForPlatform() {
-		if j.deviceProperties.Compile_dex == nil {
-			j.deviceProperties.Compile_dex = proptools.BoolPtr(true)
+		if j.dexProperties.Compile_dex == nil {
+			j.dexProperties.Compile_dex = proptools.BoolPtr(true)
 		}
 		if j.deviceProperties.Hostdex == nil {
 			j.deviceProperties.Hostdex = proptools.BoolPtr(true)
@@ -1656,10 +1611,14 @@
 	}
 
 	if ctx.Device() && j.hasCode(ctx) &&
-		(Bool(j.properties.Installable) || Bool(j.deviceProperties.Compile_dex)) {
+		(Bool(j.properties.Installable) || Bool(j.dexProperties.Compile_dex)) {
+		if j.shouldInstrumentStatic(ctx) {
+			j.dexer.extraProguardFlagFiles = append(j.dexer.extraProguardFlagFiles,
+				android.PathForSource(ctx, "build/make/core/proguard.jacoco.flags"))
+		}
 		// Dex compilation
 		var dexOutputFile android.ModuleOutPath
-		dexOutputFile = j.compileDex(ctx, flags, outputFile, jarName)
+		dexOutputFile = j.dexer.compileDex(ctx, flags, j.minSdkVersion(), outputFile, jarName)
 		if ctx.Failed() {
 			return
 		}
@@ -1669,7 +1628,7 @@
 
 		// Hidden API CSV generation and dex encoding
 		dexOutputFile = j.hiddenAPI.hiddenAPI(ctx, configurationName, primary, dexOutputFile, j.implementationJarFile,
-			proptools.Bool(j.deviceProperties.Uncompress_dex))
+			proptools.Bool(j.dexProperties.Uncompress_dex))
 
 		// merge dex jar with resources if necessary
 		if j.resourceJar != nil {
@@ -1677,7 +1636,7 @@
 			combinedJar := android.PathForModuleOut(ctx, "dex-withres", jarName)
 			TransformJarsToJar(ctx, combinedJar, "for dex resources", jars, android.OptionalPath{},
 				false, nil, nil)
-			if *j.deviceProperties.Uncompress_dex {
+			if *j.dexProperties.Uncompress_dex {
 				combinedAlignedJar := android.PathForModuleOut(ctx, "dex-withres-aligned", jarName)
 				TransformZipAlign(ctx, combinedAlignedJar, combinedJar)
 				dexOutputFile = combinedAlignedJar
@@ -2008,11 +1967,11 @@
 	j.checkSdkVersions(ctx)
 	j.dexpreopter.installPath = android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar")
 	j.dexpreopter.isSDKLibrary = j.deviceProperties.IsSDKLibrary
-	if j.deviceProperties.Uncompress_dex == nil {
+	if j.dexProperties.Uncompress_dex == nil {
 		// If the value was not force-set by the user, use reasonable default based on the module.
-		j.deviceProperties.Uncompress_dex = proptools.BoolPtr(shouldUncompressDex(ctx, &j.dexpreopter))
+		j.dexProperties.Uncompress_dex = proptools.BoolPtr(shouldUncompressDex(ctx, &j.dexpreopter))
 	}
-	j.dexpreopter.uncompressedDex = *j.deviceProperties.Uncompress_dex
+	j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex
 	j.compile(ctx, nil)
 
 	// Collect the module directory for IDE info in java/jdeps.go.
@@ -2970,6 +2929,7 @@
 	module.AddProperties(
 		&CompilerProperties{},
 		&CompilerDeviceProperties{},
+		&DexProperties{},
 		&DexpreoptProperties{},
 		&android.ProtoProperties{},
 		&aaptProperties{},
diff --git a/java/java_test.go b/java/java_test.go
index 582ebe9..50c40c3 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -96,6 +96,8 @@
 	ctx.RegisterPreSingletonType("overlay", android.SingletonFactoryAdaptor(OverlaySingletonFactory))
 	ctx.RegisterPreSingletonType("sdk_versions", android.SingletonFactoryAdaptor(sdkPreSingletonFactory))
 
+	android.RegisterPrebuiltMutators(ctx)
+
 	// Register module types and mutators from cc needed for JNI testing
 	cc.RegisterRequiredBuildComponentsForTest(ctx)
 
@@ -1107,8 +1109,13 @@
 			"bar-doc/a.java": nil,
 			"bar-doc/b.java": nil,
 		})
+	barDocModule := ctx.ModuleForTests("bar-doc", "android_common")
+	barDoc := barDocModule.Rule("javadoc")
+	notExpected := " -stubs "
+	if strings.Contains(barDoc.RuleParams.Command, notExpected) {
+		t.Errorf("bar-doc command contains flag %q to create stubs, but should not", notExpected)
+	}
 
-	barDoc := ctx.ModuleForTests("bar-doc", "android_common").Rule("javadoc")
 	var javaSrcs []string
 	for _, i := range barDoc.Inputs {
 		javaSrcs = append(javaSrcs, i.Base())
@@ -1117,7 +1124,7 @@
 		t.Errorf("inputs of bar-doc must be []string{\"a.java\"}, but was %#v.", javaSrcs)
 	}
 
-	aidl := ctx.ModuleForTests("bar-doc", "android_common").Rule("aidl")
+	aidl := barDocModule.Rule("aidl")
 	if g, w := barDoc.Implicits.Strings(), aidl.Output.String(); !inList(w, g) {
 		t.Errorf("implicits of bar-doc must contain %q, but was %q.", w, g)
 	}
@@ -1163,6 +1170,62 @@
 		`)
 }
 
+func TestDroidstubs(t *testing.T) {
+	ctx, _ := testJavaWithFS(t, `
+		droiddoc_exported_dir {
+		    name: "droiddoc-templates-sdk",
+		    path: ".",
+		}
+
+		droidstubs {
+		    name: "bar-stubs",
+		    srcs: [
+		        "bar-doc/a.java",
+				],
+				api_levels_annotations_dirs: [
+					"droiddoc-templates-sdk",
+				],
+				api_levels_annotations_enabled: true,
+		}
+
+		droidstubs {
+		    name: "bar-stubs-other",
+		    srcs: [
+		        "bar-doc/a.java",
+				],
+				api_levels_annotations_dirs: [
+					"droiddoc-templates-sdk",
+				],
+				api_levels_annotations_enabled: true,
+				api_levels_jar_filename: "android.other.jar",
+		}
+		`,
+		map[string][]byte{
+			"bar-doc/a.java": nil,
+		})
+	testcases := []struct {
+		moduleName          string
+		expectedJarFilename string
+	}{
+		{
+			moduleName:          "bar-stubs",
+			expectedJarFilename: "android.jar",
+		},
+		{
+			moduleName:          "bar-stubs-other",
+			expectedJarFilename: "android.other.jar",
+		},
+	}
+	for _, c := range testcases {
+		m := ctx.ModuleForTests(c.moduleName, "android_common")
+		metalava := m.Rule("metalava")
+		expected := "--android-jar-pattern ./%/public/" + c.expectedJarFilename
+		if actual := metalava.RuleParams.Command; !strings.Contains(actual, expected) {
+			t.Errorf("For %q, expected metalava argument %q, but was not found %q", c.moduleName, expected, actual)
+		}
+	}
+}
+
 func TestDroidstubsWithSystemModules(t *testing.T) {
 	ctx, _ := testJava(t, `
 		droidstubs {
diff --git a/java/lint.go b/java/lint.go
index 1bf7f69..f2091db 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -309,7 +309,7 @@
 	rule.Command().Text("mkdir -p").Flag(cacheDir.String()).Flag(homeDir.String())
 
 	var annotationsZipPath, apiVersionsXMLPath android.Path
-	if ctx.Config().UnbundledBuildUsePrebuiltSdks() {
+	if ctx.Config().AlwaysUsePrebuiltSdks() {
 		annotationsZipPath = android.PathForSource(ctx, "prebuilts/sdk/current/public/data/annotations.zip")
 		apiVersionsXMLPath = android.PathForSource(ctx, "prebuilts/sdk/current/public/data/api-versions.xml")
 	} else {
@@ -395,7 +395,7 @@
 }
 
 func (l *lintSingleton) copyLintDependencies(ctx android.SingletonContext) {
-	if ctx.Config().UnbundledBuildUsePrebuiltSdks() {
+	if ctx.Config().AlwaysUsePrebuiltSdks() {
 		return
 	}
 
diff --git a/java/robolectric.go b/java/robolectric.go
index 4d68fd9..3fe6626 100644
--- a/java/robolectric.go
+++ b/java/robolectric.go
@@ -357,7 +357,7 @@
 var _ android.TestSuiteModule = (*robolectricRuntimes)(nil)
 
 func (r *robolectricRuntimes) DepsMutator(ctx android.BottomUpMutatorContext) {
-	if !ctx.Config().UnbundledBuildUsePrebuiltSdks() && r.props.Lib != nil {
+	if !ctx.Config().AlwaysUsePrebuiltSdks() && r.props.Lib != nil {
 		ctx.AddVariationDependencies(nil, libTag, String(r.props.Lib))
 	}
 }
@@ -371,8 +371,16 @@
 		r.runtimes = append(r.runtimes, installedRuntime)
 	}
 
-	if !ctx.Config().UnbundledBuildUsePrebuiltSdks() && r.props.Lib != nil {
+	if !ctx.Config().AlwaysUsePrebuiltSdks() && r.props.Lib != nil {
 		runtimeFromSourceModule := ctx.GetDirectDepWithTag(String(r.props.Lib), libTag)
+		if runtimeFromSourceModule == nil {
+			if ctx.Config().AllowMissingDependencies() {
+				ctx.AddMissingDependencies([]string{String(r.props.Lib)})
+			} else {
+				ctx.PropertyErrorf("lib", "missing dependency %q", String(r.props.Lib))
+			}
+			return
+		}
 		runtimeFromSourceJar := android.OutputFileForModule(ctx, runtimeFromSourceModule, "")
 
 		runtimeName := fmt.Sprintf("android-all-%s-robolectric-r0.jar",
diff --git a/java/sdk.go b/java/sdk.go
index 5d79d1d..b44cd8e1 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -53,7 +53,7 @@
 
 func UseApiFingerprint(ctx android.BaseModuleContext) bool {
 	if ctx.Config().UnbundledBuild() &&
-		!ctx.Config().UnbundledBuildUsePrebuiltSdks() &&
+		!ctx.Config().AlwaysUsePrebuiltSdks() &&
 		ctx.Config().IsEnvTrue("UNBUNDLED_BUILD_TARGET_SDK_WITH_API_FINGERPRINT") {
 		return true
 	}
@@ -191,28 +191,11 @@
 	return s.kind != sdkPrivate && s.kind != sdkNone && s.kind != sdkCorePlatform
 }
 
-// forPdkBuild converts this sdkSpec into another sdkSpec that is for the PDK builds.
-func (s sdkSpec) forPdkBuild(ctx android.EarlyModuleContext) sdkSpec {
-	// For PDK builds, use the latest SDK version instead of "current" or ""
-	if s.kind == sdkPrivate || s.kind == sdkPublic {
-		kind := s.kind
-		if kind == sdkPrivate {
-			// We don't have prebuilt SDK for private APIs, so use the public SDK
-			// instead. This looks odd, but that's how it has been done.
-			// TODO(b/148271073): investigate the need for this.
-			kind = sdkPublic
-		}
-		version := sdkVersion(LatestSdkVersionInt(ctx))
-		return sdkSpec{kind, version, s.raw}
-	}
-	return s
-}
-
 // usePrebuilt determines whether prebuilt SDK should be used for this sdkSpec with the given context.
 func (s sdkSpec) usePrebuilt(ctx android.EarlyModuleContext) bool {
 	if s.version.isCurrent() {
 		// "current" can be built from source and be from prebuilt SDK
-		return ctx.Config().UnbundledBuildUsePrebuiltSdks()
+		return ctx.Config().AlwaysUsePrebuiltSdks()
 	} else if s.version.isNumbered() {
 		// validation check
 		if s.kind != sdkPublic && s.kind != sdkSystem && s.kind != sdkTest {
@@ -233,9 +216,6 @@
 	if !s.valid() {
 		return s.version, fmt.Errorf("invalid sdk version %q", s.raw)
 	}
-	if ctx.Config().IsPdkBuild() {
-		s = s.forPdkBuild(ctx)
-	}
 	if s.version.isNumbered() {
 		return s.version, nil
 	}
@@ -350,9 +330,6 @@
 		return sdkDep{}
 	}
 
-	if ctx.Config().IsPdkBuild() {
-		sdkVersion = sdkVersion.forPdkBuild(ctx)
-	}
 	if !sdkVersion.validateSystemSdk(ctx) {
 		return sdkDep{}
 	}
@@ -511,7 +488,7 @@
 type sdkSingleton struct{}
 
 func (sdkSingleton) GenerateBuildActions(ctx android.SingletonContext) {
-	if ctx.Config().UnbundledBuildUsePrebuiltSdks() || ctx.Config().IsPdkBuild() {
+	if ctx.Config().AlwaysUsePrebuiltSdks() {
 		return
 	}
 
@@ -631,10 +608,7 @@
 
 	if ctx.Config().PlatformSdkCodename() == "REL" {
 		cmd.Text("echo REL >").Output(out)
-	} else if ctx.Config().IsPdkBuild() {
-		// TODO: get this from the PDK artifacts?
-		cmd.Text("echo PDK >").Output(out)
-	} else if !ctx.Config().UnbundledBuildUsePrebuiltSdks() {
+	} else if !ctx.Config().AlwaysUsePrebuiltSdks() {
 		in, err := ctx.GlobWithDeps("frameworks/base/api/*current.txt", nil)
 		if err != nil {
 			ctx.Errorf("error globbing API files: %s", err)
@@ -663,7 +637,7 @@
 }
 
 func sdkMakeVars(ctx android.MakeVarsContext) {
-	if ctx.Config().UnbundledBuildUsePrebuiltSdks() || ctx.Config().IsPdkBuild() {
+	if ctx.Config().AlwaysUsePrebuiltSdks() {
 		return
 	}
 
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 8a8d0c9..25f0134 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -1112,6 +1112,7 @@
 		&module.properties,
 		&module.protoProperties,
 		&module.deviceProperties,
+		&module.dexProperties,
 		&module.dexpreoptProperties,
 		&module.linter.properties,
 		&props,
@@ -1123,22 +1124,17 @@
 // Creates a static java library that has API stubs
 func (module *SdkLibrary) createStubsLibrary(mctx android.DefaultableHookContext, apiScope *apiScope) {
 	props := struct {
-		Name              *string
-		Visibility        []string
-		Srcs              []string
-		Installable       *bool
-		Sdk_version       *string
-		System_modules    *string
-		Patch_module      *string
-		Libs              []string
-		Compile_dex       *bool
-		Java_version      *string
-		Product_variables struct {
-			Pdk struct {
-				Enabled *bool
-			}
-		}
-		Openjdk9 struct {
+		Name           *string
+		Visibility     []string
+		Srcs           []string
+		Installable    *bool
+		Sdk_version    *string
+		System_modules *string
+		Patch_module   *string
+		Libs           []string
+		Compile_dex    *bool
+		Java_version   *string
+		Openjdk9       struct {
 			Srcs       []string
 			Javacflags []string
 		}
@@ -1165,14 +1161,13 @@
 	props.Patch_module = module.properties.Patch_module
 	props.Installable = proptools.BoolPtr(false)
 	props.Libs = module.sdkLibraryProperties.Stub_only_libs
-	props.Product_variables.Pdk.Enabled = proptools.BoolPtr(false)
 	props.Openjdk9.Srcs = module.properties.Openjdk9.Srcs
 	props.Openjdk9.Javacflags = module.properties.Openjdk9.Javacflags
 	// We compile the stubs for 1.8 in line with the main android.jar stubs, and potential
 	// interop with older developer tools that don't support 1.9.
 	props.Java_version = proptools.StringPtr("1.8")
-	if module.deviceProperties.Compile_dex != nil {
-		props.Compile_dex = module.deviceProperties.Compile_dex
+	if module.dexProperties.Compile_dex != nil {
+		props.Compile_dex = module.dexProperties.Compile_dex
 	}
 
 	// Dist the class jar artifact for sdk builds.
@@ -1798,7 +1793,7 @@
 func (module *SdkLibraryImport) createInternalModules(mctx android.DefaultableHookContext) {
 
 	// If the build is configured to use prebuilts then force this to be preferred.
-	if mctx.Config().UnbundledBuildUsePrebuiltSdks() {
+	if mctx.Config().AlwaysUsePrebuiltSdks() {
 		module.prebuilt.ForcePrefer()
 	}
 
diff --git a/java/sdk_test.go b/java/sdk_test.go
index 395da79..776069d 100644
--- a/java/sdk_test.go
+++ b/java/sdk_test.go
@@ -30,7 +30,6 @@
 	var classpathTestcases = []struct {
 		name       string
 		unbundled  bool
-		pdk        bool
 		moduleType string
 		host       android.OsClass
 		properties string
@@ -217,35 +216,6 @@
 		},
 
 		{
-			name:           "pdk default",
-			pdk:            true,
-			bootclasspath:  []string{`""`},
-			system:         "sdk_public_30_system_modules",
-			java8classpath: []string{"prebuilts/sdk/30/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
-			java9classpath: []string{"prebuilts/sdk/30/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
-			aidl:           "-pprebuilts/sdk/30/public/framework.aidl",
-		},
-		{
-			name:           "pdk current",
-			pdk:            true,
-			properties:     `sdk_version: "current",`,
-			bootclasspath:  []string{`""`},
-			system:         "sdk_public_30_system_modules",
-			java8classpath: []string{"prebuilts/sdk/30/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
-			java9classpath: []string{"prebuilts/sdk/30/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
-			aidl:           "-pprebuilts/sdk/30/public/framework.aidl",
-		},
-		{
-			name:           "pdk 29",
-			pdk:            true,
-			properties:     `sdk_version: "29",`,
-			bootclasspath:  []string{`""`},
-			system:         "sdk_public_30_system_modules",
-			java8classpath: []string{"prebuilts/sdk/30/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
-			java9classpath: []string{"prebuilts/sdk/30/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
-			aidl:           "-pprebuilts/sdk/30/public/framework.aidl",
-		},
-		{
 			name:           "module_current",
 			properties:     `sdk_version: "module_current",`,
 			bootclasspath:  []string{"android_module_lib_stubs_current", "core-lambda-stubs"},
@@ -384,9 +354,7 @@
 				config := testConfig(nil, bpJava8, nil)
 				if testcase.unbundled {
 					config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true)
-				}
-				if testcase.pdk {
-					config.TestProductVariables.Pdk = proptools.BoolPtr(true)
+					config.TestProductVariables.Always_use_prebuilt_sdks = proptools.BoolPtr(true)
 				}
 				ctx := testContext()
 				run(t, ctx, config)
@@ -407,9 +375,7 @@
 				config := testConfig(nil, bp, nil)
 				if testcase.unbundled {
 					config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true)
-				}
-				if testcase.pdk {
-					config.TestProductVariables.Pdk = proptools.BoolPtr(true)
+					config.TestProductVariables.Always_use_prebuilt_sdks = proptools.BoolPtr(true)
 				}
 				ctx := testContext()
 				run(t, ctx, config)
@@ -433,9 +399,7 @@
 
 				if testcase.unbundled {
 					config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true)
-				}
-				if testcase.pdk {
-					config.TestProductVariables.Pdk = proptools.BoolPtr(true)
+					config.TestProductVariables.Always_use_prebuilt_sdks = proptools.BoolPtr(true)
 				}
 				ctx := testContext()
 				run(t, ctx, config)
@@ -451,9 +415,7 @@
 
 				if testcase.unbundled {
 					config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true)
-				}
-				if testcase.pdk {
-					config.TestProductVariables.Pdk = proptools.BoolPtr(true)
+					config.TestProductVariables.Always_use_prebuilt_sdks = proptools.BoolPtr(true)
 				}
 				ctx := testContext()
 				run(t, ctx, config)
diff --git a/phony/phony.go b/phony/phony.go
index 305a434..cb60b9f 100644
--- a/phony/phony.go
+++ b/phony/phony.go
@@ -44,11 +44,6 @@
 	p.requiredModuleNames = ctx.RequiredModuleNames()
 	p.hostRequiredModuleNames = ctx.HostRequiredModuleNames()
 	p.targetRequiredModuleNames = ctx.TargetRequiredModuleNames()
-	if len(p.requiredModuleNames) == 0 &&
-		len(p.hostRequiredModuleNames) == 0 && len(p.targetRequiredModuleNames) == 0 {
-		ctx.PropertyErrorf("required", "phony must not have empty required dependencies "+
-			"in order to be useful(and therefore permitted).")
-	}
 }
 
 func (p *phony) AndroidMk() android.AndroidMkData {
diff --git a/rust/Android.bp b/rust/Android.bp
index e03bf4f..bbeb28d 100644
--- a/rust/Android.bp
+++ b/rust/Android.bp
@@ -34,6 +34,7 @@
         "library_test.go",
         "project_json_test.go",
         "rust_test.go",
+        "source_provider_test.go",
         "test_test.go",
     ],
     pluginFor: ["soong_build"],
diff --git a/rust/androidmk.go b/rust/androidmk.go
index babbf91..fda0a25 100644
--- a/rust/androidmk.go
+++ b/rust/androidmk.go
@@ -26,18 +26,18 @@
 type AndroidMkContext interface {
 	Name() string
 	Target() android.Target
-	subAndroidMk(*android.AndroidMkData, interface{})
+	SubAndroidMk(*android.AndroidMkData, interface{})
 }
 
-type subAndroidMkProvider interface {
+type SubAndroidMkProvider interface {
 	AndroidMk(AndroidMkContext, *android.AndroidMkData)
 }
 
-func (mod *Module) subAndroidMk(data *android.AndroidMkData, obj interface{}) {
+func (mod *Module) SubAndroidMk(data *android.AndroidMkData, obj interface{}) {
 	if mod.subAndroidMkOnce == nil {
-		mod.subAndroidMkOnce = make(map[subAndroidMkProvider]bool)
+		mod.subAndroidMkOnce = make(map[SubAndroidMkProvider]bool)
 	}
-	if androidmk, ok := obj.(subAndroidMkProvider); ok {
+	if androidmk, ok := obj.(SubAndroidMkProvider); ok {
 		if !mod.subAndroidMkOnce[androidmk] {
 			mod.subAndroidMkOnce[androidmk] = true
 			androidmk.AndroidMk(mod, data)
@@ -55,7 +55,6 @@
 	ret := android.AndroidMkData{
 		OutputFile: mod.outputFile,
 		Include:    "$(BUILD_SYSTEM)/soong_rust_prebuilt.mk",
-		SubName:    mod.subName,
 		Extra: []android.AndroidMkExtraFunc{
 			func(w io.Writer, outputFile android.Path) {
 				if len(mod.Properties.AndroidMkRlibs) > 0 {
@@ -76,10 +75,12 @@
 			},
 		},
 	}
-	if mod.compiler != nil {
-		mod.subAndroidMk(&ret, mod.compiler)
+
+	if mod.compiler != nil && !mod.compiler.Disabled() {
+		mod.SubAndroidMk(&ret, mod.compiler)
 	} else if mod.sourceProvider != nil {
-		mod.subAndroidMk(&ret, mod.sourceProvider)
+		// If the compiler is disabled, this is a SourceProvider.
+		mod.SubAndroidMk(&ret, mod.sourceProvider)
 	}
 	ret.SubName += mod.Properties.SubName
 
@@ -87,7 +88,7 @@
 }
 
 func (binary *binaryDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
-	ctx.subAndroidMk(ret, binary.baseCompiler)
+	ctx.SubAndroidMk(ret, binary.baseCompiler)
 
 	if binary.distFile.Valid() {
 		ret.DistFiles = android.MakeDefaultDistFiles(binary.distFile.Path())
@@ -121,7 +122,7 @@
 }
 
 func (library *libraryDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
-	ctx.subAndroidMk(ret, library.baseCompiler)
+	ctx.SubAndroidMk(ret, library.baseCompiler)
 
 	if library.rlib() {
 		ret.Class = "RLIB_LIBRARIES"
@@ -149,7 +150,7 @@
 }
 
 func (procMacro *procMacroDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
-	ctx.subAndroidMk(ret, procMacro.baseCompiler)
+	ctx.SubAndroidMk(ret, procMacro.baseCompiler)
 
 	ret.Class = "PROC_MACRO_LIBRARIES"
 	if procMacro.distFile.Valid() {
@@ -158,10 +159,11 @@
 
 }
 
-func (sourceProvider *baseSourceProvider) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
-	outFile := sourceProvider.outputFile
+func (sourceProvider *BaseSourceProvider) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
+	outFile := sourceProvider.OutputFile
 	ret.Class = "ETC"
 	ret.OutputFile = android.OptionalPathForPath(outFile)
+	ret.SubName += sourceProvider.subName
 	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
 		_, file := filepath.Split(outFile.String())
 		stem, suffix, _ := android.SplitFileExt(file)
@@ -171,7 +173,7 @@
 }
 
 func (bindgen *bindgenDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
-	ctx.subAndroidMk(ret, bindgen.baseSourceProvider)
+	ctx.SubAndroidMk(ret, bindgen.BaseSourceProvider)
 	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
 		fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := true")
 	})
diff --git a/rust/bindgen.go b/rust/bindgen.go
index e8bbb35..9b09e61 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -18,6 +18,7 @@
 	"strings"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
 	ccConfig "android/soong/cc/config"
@@ -41,12 +42,12 @@
 	bindgen = pctx.AndroidStaticRule("bindgen",
 		blueprint.RuleParams{
 			Command: "CLANG_PATH=$bindgenClang LIBCLANG_PATH=$bindgenLibClang RUSTFMT=${config.RustBin}/rustfmt " +
-				"$bindgenCmd $flags $in -o $out -- -MD -MF $out.d $cflags",
-			CommandDeps: []string{"$bindgenCmd"},
+				"$cmd $flags $in -o $out -- -MD -MF $out.d $cflags",
+			CommandDeps: []string{"$cmd"},
 			Deps:        blueprint.DepsGCC,
 			Depfile:     "$out.d",
 		},
-		"flags", "cflags")
+		"cmd", "flags", "cflags")
 )
 
 func init() {
@@ -61,7 +62,7 @@
 	Wrapper_src *string `android:"path,arch_variant"`
 
 	// list of bindgen-specific flags and options
-	Flags []string `android:"arch_variant"`
+	Bindgen_flags []string `android:"arch_variant"`
 
 	// list of clang flags required to correctly interpret the headers.
 	Cflags []string `android:"arch_variant"`
@@ -76,16 +77,22 @@
 	// list of shared libraries that provide headers for this binding.
 	Shared_libs []string `android:"arch_variant"`
 
+	// module name of a custom binary/script which should be used instead of the 'bindgen' binary. This custom
+	// binary must expect arguments in a similar fashion to bindgen, e.g.
+	//
+	// "my_bindgen [flags] wrapper_header.h -o [output_path] -- [clang flags]"
+	Custom_bindgen string `android:"path"`
+
 	//TODO(b/161141999) Add support for headers from cc_library_header modules.
 }
 
 type bindgenDecorator struct {
-	*baseSourceProvider
+	*BaseSourceProvider
 
 	Properties BindgenProperties
 }
 
-func (b *bindgenDecorator) generateSource(ctx android.ModuleContext, deps PathDeps) android.Path {
+func (b *bindgenDecorator) GenerateSource(ctx android.ModuleContext, deps PathDeps) android.Path {
 	ccToolchain := ccConfig.FindToolchain(ctx.Os(), ctx.Arch())
 
 	var cflags []string
@@ -113,40 +120,53 @@
 		cflags = append(cflags, "-isystem "+include.String())
 	}
 
+	esc := proptools.NinjaAndShellEscapeList
+
 	// Module defined clang flags and include paths
-	cflags = append(cflags, b.Properties.Cflags...)
+	cflags = append(cflags, esc(b.Properties.Cflags)...)
 	for _, include := range b.Properties.Local_include_dirs {
 		cflags = append(cflags, "-I"+android.PathForModuleSrc(ctx, include).String())
 		implicits = append(implicits, android.PathForModuleSrc(ctx, include))
 	}
 
 	bindgenFlags := defaultBindgenFlags
-	bindgenFlags = append(bindgenFlags, strings.Join(b.Properties.Flags, " "))
+	bindgenFlags = append(bindgenFlags, esc(b.Properties.Bindgen_flags)...)
 
 	wrapperFile := android.OptionalPathForModuleSrc(ctx, b.Properties.Wrapper_src)
 	if !wrapperFile.Valid() {
 		ctx.PropertyErrorf("wrapper_src", "invalid path to wrapper source")
 	}
 
-	outputFile := android.PathForModuleOut(ctx, b.baseSourceProvider.getStem(ctx)+".rs")
+	outputFile := android.PathForModuleOut(ctx, b.BaseSourceProvider.getStem(ctx)+".rs")
+
+	var cmd, cmdDesc string
+	if b.Properties.Custom_bindgen != "" {
+		cmd = ctx.GetDirectDepWithTag(b.Properties.Custom_bindgen, customBindgenDepTag).(*Module).HostToolPath().String()
+		cmdDesc = b.Properties.Custom_bindgen
+	} else {
+		cmd = "$bindgenCmd"
+		cmdDesc = "bindgen"
+	}
 
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        bindgen,
-		Description: "bindgen " + wrapperFile.Path().Rel(),
+		Description: strings.Join([]string{cmdDesc, wrapperFile.Path().Rel()}, " "),
 		Output:      outputFile,
 		Input:       wrapperFile.Path(),
 		Implicits:   implicits,
 		Args: map[string]string{
+			"cmd":    cmd,
 			"flags":  strings.Join(bindgenFlags, " "),
 			"cflags": strings.Join(cflags, " "),
 		},
 	})
-	b.baseSourceProvider.outputFile = outputFile
+
+	b.BaseSourceProvider.OutputFile = outputFile
 	return outputFile
 }
 
-func (b *bindgenDecorator) sourceProviderProps() []interface{} {
-	return append(b.baseSourceProvider.sourceProviderProps(),
+func (b *bindgenDecorator) SourceProviderProps() []interface{} {
+	return append(b.BaseSourceProvider.SourceProviderProps(),
 		&b.Properties)
 }
 
@@ -164,19 +184,18 @@
 }
 
 func NewRustBindgen(hod android.HostOrDeviceSupported) (*Module, *bindgenDecorator) {
-	module := newModule(hod, android.MultilibBoth)
-
 	bindgen := &bindgenDecorator{
-		baseSourceProvider: NewSourceProvider(),
+		BaseSourceProvider: NewSourceProvider(),
 		Properties:         BindgenProperties{},
 	}
-	module.sourceProvider = bindgen
+
+	module := NewSourceProviderModule(hod, bindgen, false)
 
 	return module, bindgen
 }
 
-func (b *bindgenDecorator) sourceProviderDeps(ctx DepsContext, deps Deps) Deps {
-	deps = b.baseSourceProvider.sourceProviderDeps(ctx, deps)
+func (b *bindgenDecorator) SourceProviderDeps(ctx DepsContext, deps Deps) Deps {
+	deps = b.BaseSourceProvider.SourceProviderDeps(ctx, deps)
 	if ctx.toolchain().Bionic() {
 		deps = bionicDeps(deps)
 	}
diff --git a/rust/bindgen_test.go b/rust/bindgen_test.go
index 2122ec1..191da9b 100644
--- a/rust/bindgen_test.go
+++ b/rust/bindgen_test.go
@@ -24,9 +24,11 @@
 		rust_bindgen {
 			name: "libbindgen",
 			wrapper_src: "src/any.h",
-			stem: "bindings",
-			flags: ["--bindgen-flag"],
-			cflags: ["--clang-flag"],
+			crate_name: "bindgen",
+			stem: "libbindgen",
+			source_stem: "bindings",
+			bindgen_flags: ["--bindgen-flag.*"],
+			cflags: ["--clang-flag()"],
 			shared_libs: ["libfoo_shared"],
 			static_libs: ["libfoo_static"],
 		}
@@ -38,13 +40,13 @@
 			name: "libfoo_static",
 			export_include_dirs: ["static_include"],
 		}
-
 	`)
 	libbindgen := ctx.ModuleForTests("libbindgen", "android_arm64_armv8-a").Output("bindings.rs")
-	if !strings.Contains(libbindgen.Args["flags"], "--bindgen-flag") {
+	// Ensure that the flags are present and escaped
+	if !strings.Contains(libbindgen.Args["flags"], "'--bindgen-flag.*'") {
 		t.Errorf("missing bindgen flags in rust_bindgen rule: flags %#v", libbindgen.Args["flags"])
 	}
-	if !strings.Contains(libbindgen.Args["cflags"], "--clang-flag") {
+	if !strings.Contains(libbindgen.Args["cflags"], "'--clang-flag()'") {
 		t.Errorf("missing clang cflags in rust_bindgen rule: cflags %#v", libbindgen.Args["cflags"])
 	}
 	if !strings.Contains(libbindgen.Args["cflags"], "-Ishared_include") {
@@ -54,3 +56,29 @@
 		t.Errorf("missing static_libs exported includes in rust_bindgen rule: cflags %#v", libbindgen.Args["cflags"])
 	}
 }
+
+func TestRustBindgenCustomBindgen(t *testing.T) {
+	ctx := testRust(t, `
+		rust_bindgen {
+			name: "libbindgen",
+			wrapper_src: "src/any.h",
+			crate_name: "bindgen",
+			stem: "libbindgen",
+			source_stem: "bindings",
+			custom_bindgen: "my_bindgen"
+		}
+		rust_binary_host {
+			name: "my_bindgen",
+			srcs: ["foo.rs"],
+		}
+	`)
+
+	libbindgen := ctx.ModuleForTests("libbindgen", "android_arm64_armv8-a").Output("bindings.rs")
+
+	// The rule description should contain the custom binary name rather than bindgen, so checking the description
+	// should be sufficient.
+	if !strings.Contains(libbindgen.Description, "my_bindgen") {
+		t.Errorf("Custom bindgen binary %s not used for libbindgen: rule description %#v", "my_bindgen",
+			libbindgen.Description)
+	}
+}
diff --git a/rust/builder_test.go b/rust/builder_test.go
index 04b67d9..5c11cb7 100644
--- a/rust/builder_test.go
+++ b/rust/builder_test.go
@@ -28,12 +28,14 @@
 		}
 		rust_bindgen {
 			name: "libbindings1",
-			stem: "bindings",
+			source_stem: "bindings",
+			crate_name: "bindings1",
 			wrapper_src: "src/any.h",
 		}
 		rust_bindgen {
 			name: "libbindings2",
-			stem: "bindings",
+			source_stem: "bindings",
+			crate_name: "bindings2",
 			wrapper_src: "src/any.h",
 		}
 	`)
diff --git a/rust/compiler.go b/rust/compiler.go
index 040219d..c39a4a1 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -24,7 +24,7 @@
 	"android/soong/rust/config"
 )
 
-func getEdition(compiler *baseCompiler) string {
+func (compiler *baseCompiler) edition() string {
 	return proptools.StringDefault(compiler.Properties.Edition, config.DefaultEdition)
 }
 
@@ -32,6 +32,10 @@
 	compiler.Properties.No_stdlibs = proptools.BoolPtr(true)
 }
 
+func (compiler *baseCompiler) setNoLint() {
+	compiler.Properties.No_lint = proptools.BoolPtr(true)
+}
+
 func NewBaseCompiler(dir, dir64 string, location installLocation) *baseCompiler {
 	return &baseCompiler{
 		Properties: BaseCompilerProperties{},
@@ -81,7 +85,10 @@
 	// list of C static library dependencies
 	Static_libs []string `android:"arch_variant"`
 
-	// crate name, required for libraries. This must be the expected extern crate name used in source
+	// crate name, required for modules which produce Rust libraries: rust_library, rust_ffi and SourceProvider
+	// modules which create library variants (rust_bindgen). This must be the expected extern crate name used in
+	// source, and is required to conform to an enforced format matching library output files (if the output file is
+	// lib<someName><suffix>, the crate_name property must be <someName>).
 	Crate_name string `android:"arch_variant"`
 
 	// list of features to enable for this crate
@@ -120,6 +127,14 @@
 	distFile              android.OptionalPath
 }
 
+func (compiler *baseCompiler) Disabled() bool {
+	return false
+}
+
+func (compiler *baseCompiler) SetDisabled() {
+	panic("baseCompiler does not implement SetDisabled()")
+}
+
 func (compiler *baseCompiler) coverageOutputZipPath() android.OptionalPath {
 	panic("baseCompiler does not implement coverageOutputZipPath()")
 }
@@ -144,12 +159,14 @@
 
 func (compiler *baseCompiler) compilerFlags(ctx ModuleContext, flags Flags) Flags {
 
-	if !Bool(compiler.Properties.No_lint) {
+	if Bool(compiler.Properties.No_lint) {
+		flags.RustFlags = append(flags.RustFlags, config.AllowAllLints)
+	} else {
 		flags.RustFlags = append(flags.RustFlags, config.RustcLintsForDir(ctx.ModuleDir()))
 	}
 	flags.RustFlags = append(flags.RustFlags, compiler.Properties.Flags...)
 	flags.RustFlags = append(flags.RustFlags, compiler.featuresToFlags(compiler.Properties.Features)...)
-	flags.RustFlags = append(flags.RustFlags, "--edition="+getEdition(compiler))
+	flags.RustFlags = append(flags.RustFlags, "--edition="+compiler.edition())
 	flags.LinkFlags = append(flags.LinkFlags, compiler.Properties.Ld_flags...)
 	flags.GlobalRustFlags = append(flags.GlobalRustFlags, config.GlobalRustFlags...)
 	flags.GlobalRustFlags = append(flags.GlobalRustFlags, ctx.toolchain().ToolchainRustFlags())
diff --git a/rust/config/allowed_list.go b/rust/config/allowed_list.go
index 0204cd2..22a0e63 100644
--- a/rust/config/allowed_list.go
+++ b/rust/config/allowed_list.go
@@ -7,6 +7,8 @@
 		"external/crosvm",
 		"external/adhd",
 		"prebuilts/rust",
+		"system/extras/profcollectd",
+		"system/security",
 	}
 
 	RustModuleTypes = []string{
diff --git a/rust/config/lints.go b/rust/config/lints.go
index 529d094..e24ffac 100644
--- a/rust/config/lints.go
+++ b/rust/config/lints.go
@@ -128,6 +128,7 @@
 	{"vendor/google", rustcDefault, true, clippyDefault},
 	{"vendor/", rustcVendor, true, clippyVendor},
 }
+var AllowAllLints = rustcAllowAll
 
 // ClippyLintsForDir returns a boolean if Clippy should be executed and if so, the lints to be used.
 func ClippyLintsForDir(dir string) (bool, string) {
diff --git a/rust/library.go b/rust/library.go
index 4c6da9d..6766d61 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -69,6 +69,10 @@
 	VariantIsShared bool `blueprint:"mutated"`
 	// This variant is a static library
 	VariantIsStatic bool `blueprint:"mutated"`
+
+	// This variant is disabled and should not be compiled
+	// (used for SourceProvider variants that produce only source)
+	VariantIsDisabled bool `blueprint:"mutated"`
 }
 
 type libraryDecorator struct {
@@ -78,6 +82,7 @@
 	Properties        LibraryCompilerProperties
 	MutatedProperties LibraryMutatedProperties
 	includeDirs       android.Paths
+	sourceProvider    SourceProvider
 }
 
 type libraryInterface interface {
@@ -367,8 +372,13 @@
 
 func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path {
 	var outputFile android.WritablePath
+	var srcPath android.Path
 
-	srcPath, _ := srcPathFromModuleSrcs(ctx, library.baseCompiler.Properties.Srcs)
+	if library.sourceProvider != nil {
+		srcPath = library.sourceProvider.Srcs()[0]
+	} else {
+		srcPath, _ = srcPathFromModuleSrcs(ctx, library.baseCompiler.Properties.Srcs)
+	}
 
 	flags.RustFlags = append(flags.RustFlags, deps.depFlags...)
 
@@ -430,6 +440,14 @@
 	return stem + String(library.baseCompiler.Properties.Suffix)
 }
 
+func (library *libraryDecorator) Disabled() bool {
+	return library.MutatedProperties.VariantIsDisabled
+}
+
+func (library *libraryDecorator) SetDisabled() {
+	library.MutatedProperties.VariantIsDisabled = true
+}
+
 var validCrateName = regexp.MustCompile("[^a-zA-Z0-9_]+")
 
 func validateLibraryStem(ctx BaseModuleContext, filename string, crate_name string) {
@@ -454,25 +472,35 @@
 	if m, ok := mctx.Module().(*Module); ok && m.compiler != nil {
 		switch library := m.compiler.(type) {
 		case libraryInterface:
-
-			// We only build the rust library variants here. This assumes that
-			// LinkageMutator runs first and there's an empty variant
-			// if rust variants are required.
-			if !library.static() && !library.shared() {
-				if library.buildRlib() && library.buildDylib() {
-					modules := mctx.CreateLocalVariations("rlib", "dylib")
-					rlib := modules[0].(*Module)
-					dylib := modules[1].(*Module)
-
-					rlib.compiler.(libraryInterface).setRlib()
-					dylib.compiler.(libraryInterface).setDylib()
-				} else if library.buildRlib() {
-					modules := mctx.CreateLocalVariations("rlib")
-					modules[0].(*Module).compiler.(libraryInterface).setRlib()
-				} else if library.buildDylib() {
-					modules := mctx.CreateLocalVariations("dylib")
-					modules[0].(*Module).compiler.(libraryInterface).setDylib()
+			if library.buildRlib() && library.buildDylib() {
+				variants := []string{"rlib", "dylib"}
+				if m.sourceProvider != nil {
+					variants = append(variants, "")
 				}
+				modules := mctx.CreateLocalVariations(variants...)
+
+				rlib := modules[0].(*Module)
+				dylib := modules[1].(*Module)
+				rlib.compiler.(libraryInterface).setRlib()
+				dylib.compiler.(libraryInterface).setDylib()
+
+				if m.sourceProvider != nil {
+					// This library is SourceProvider generated, so the non-library-producing
+					// variant needs to disable it's compiler and skip installation.
+					sourceProvider := modules[2].(*Module)
+					sourceProvider.compiler.SetDisabled()
+				}
+			} else if library.buildRlib() {
+				modules := mctx.CreateLocalVariations("rlib")
+				modules[0].(*Module).compiler.(libraryInterface).setRlib()
+			} else if library.buildDylib() {
+				modules := mctx.CreateLocalVariations("dylib")
+				modules[0].(*Module).compiler.(libraryInterface).setDylib()
+			}
+
+			if m.sourceProvider != nil {
+				// Alias the non-library variant to the empty-string variant.
+				mctx.AliasVariation("")
 			}
 		}
 	}
diff --git a/rust/project_json.go b/rust/project_json.go
index a50e73a..8310479 100644
--- a/rust/project_json.go
+++ b/rust/project_json.go
@@ -75,24 +75,23 @@
 	knownCrates map[string]crateInfo, module android.Module,
 	crate *rustProjectCrate, deps map[string]int) {
 
-	//TODO(tweek): The stdlib dependencies do not appear here. We need to manually add them.
 	ctx.VisitDirectDeps(module, func(child android.Module) {
-		childId, childName, ok := appendLibraryAndDeps(ctx, project, knownCrates, child)
+		childId, childCrateName, ok := appendLibraryAndDeps(ctx, project, knownCrates, child)
 		if !ok {
 			return
 		}
-		if _, ok = deps[childName]; ok {
+		if _, ok = deps[ctx.ModuleName(child)]; ok {
 			return
 		}
-		crate.Deps = append(crate.Deps, rustProjectDep{Crate: childId, Name: childName})
-		deps[childName] = childId
+		crate.Deps = append(crate.Deps, rustProjectDep{Crate: childId, Name: childCrateName})
+		deps[ctx.ModuleName(child)] = childId
 	})
 }
 
 // appendLibraryAndDeps creates a rustProjectCrate for the module argument and
 // appends it to the rustProjectJson struct.  It visits the dependencies of the
 // module depth-first. If the current module is already in knownCrates, its
-// its dependencies are merged. Returns a tuple (id, crate_name, ok).
+// dependencies are merged. Returns a tuple (id, crate_name, ok).
 func appendLibraryAndDeps(ctx android.SingletonContext, project *rustProjectJson,
 	knownCrates map[string]crateInfo, module android.Module) (int, string, bool) {
 	rModule, ok := module.(*Module)
@@ -106,23 +105,28 @@
 	if !ok {
 		return 0, "", false
 	}
+	moduleName := ctx.ModuleName(module)
 	crateName := rModule.CrateName()
-	if cInfo, ok := knownCrates[crateName]; ok {
+	if cInfo, ok := knownCrates[moduleName]; ok {
 		// We have seen this crate already; merge any new dependencies.
 		crate := project.Crates[cInfo.ID]
 		mergeDependencies(ctx, project, knownCrates, module, &crate, cInfo.Deps)
+		project.Crates[cInfo.ID] = crate
 		return cInfo.ID, crateName, true
 	}
 	crate := rustProjectCrate{Deps: make([]rustProjectDep, 0), Cfgs: make([]string, 0)}
-	src := rustLib.baseCompiler.Properties.Srcs[0]
-	crate.RootModule = path.Join(ctx.ModuleDir(rModule), src)
-	crate.Edition = getEdition(rustLib.baseCompiler)
+	srcs := rustLib.baseCompiler.Properties.Srcs
+	if len(srcs) == 0 {
+		return 0, "", false
+	}
+	crate.RootModule = path.Join(ctx.ModuleDir(rModule), srcs[0])
+	crate.Edition = rustLib.baseCompiler.edition()
 
 	deps := make(map[string]int)
 	mergeDependencies(ctx, project, knownCrates, module, &crate, deps)
 
 	id := len(project.Crates)
-	knownCrates[crateName] = crateInfo{ID: id, Deps: deps}
+	knownCrates[moduleName] = crateInfo{ID: id, Deps: deps}
 	project.Crates = append(project.Crates, crate)
 	// rust-analyzer requires that all crates belong to at least one root:
 	// https://github.com/rust-analyzer/rust-analyzer/issues/4735.
diff --git a/rust/project_json_test.go b/rust/project_json_test.go
index 6786e72..8521940 100644
--- a/rust/project_json_test.go
+++ b/rust/project_json_test.go
@@ -15,6 +15,7 @@
 package rust
 
 import (
+	"encoding/json"
 	"io/ioutil"
 	"path/filepath"
 	"testing"
@@ -23,20 +24,12 @@
 	"android/soong/cc"
 )
 
-func TestProjectJson(t *testing.T) {
-	bp := `rust_library {
-		  name: "liba",
-		  srcs: ["src/lib.rs"],
-		  crate_name: "a"
-		}` + GatherRequiredDepsForTest()
-	env := map[string]string{"SOONG_GEN_RUST_PROJECT": "1"}
-	fs := map[string][]byte{
-		"foo.rs":     nil,
-		"src/lib.rs": nil,
-	}
-
+// testProjectJson run the generation of rust-project.json. It returns the raw
+// content of the generated file.
+func testProjectJson(t *testing.T, bp string, fs map[string][]byte) []byte {
 	cc.GatherRequiredFilesForTest(fs)
 
+	env := map[string]string{"SOONG_GEN_RUST_PROJECT": "1"}
 	config := android.TestArchConfig(buildDir, env, bp, fs)
 	ctx := CreateTestContext()
 	ctx.Register(config)
@@ -48,8 +41,131 @@
 	// The JSON file is generated via WriteFileToOutputDir. Therefore, it
 	// won't appear in the Output of the TestingSingleton. Manually verify
 	// it exists.
-	_, err := ioutil.ReadFile(filepath.Join(buildDir, "rust-project.json"))
+	content, err := ioutil.ReadFile(filepath.Join(buildDir, rustProjectJsonFileName))
 	if err != nil {
 		t.Errorf("rust-project.json has not been generated")
 	}
+	return content
+}
+
+// validateJsonCrates validates that content follows the basic structure of
+// rust-project.json. It returns the crates attribute if the validation
+// succeeded.
+// It uses an empty interface instead of relying on a defined structure to
+// avoid a strong dependency on our implementation.
+func validateJsonCrates(t *testing.T, rawContent []byte) []interface{} {
+	var content interface{}
+	err := json.Unmarshal(rawContent, &content)
+	if err != nil {
+		t.Errorf("Unable to parse the rust-project.json as JSON: %v", err)
+	}
+	root, ok := content.(map[string]interface{})
+	if !ok {
+		t.Errorf("Unexpected JSON format: %v", content)
+	}
+	if _, ok = root["crates"]; !ok {
+		t.Errorf("No crates attribute in rust-project.json: %v", root)
+	}
+	crates, ok := root["crates"].([]interface{})
+	if !ok {
+		t.Errorf("Unexpected crates format: %v", root["crates"])
+	}
+	return crates
+}
+
+func TestProjectJsonDep(t *testing.T) {
+	bp := `
+	rust_library {
+		name: "liba",
+		srcs: ["a/src/lib.rs"],
+		crate_name: "a"
+	}
+	rust_library {
+		name: "libb",
+		srcs: ["b/src/lib.rs"],
+		crate_name: "b",
+		rlibs: ["liba"],
+	}
+	` + GatherRequiredDepsForTest()
+	fs := map[string][]byte{
+		"a/src/lib.rs": nil,
+		"b/src/lib.rs": nil,
+	}
+	jsonContent := testProjectJson(t, bp, fs)
+	validateJsonCrates(t, jsonContent)
+}
+
+func TestProjectJsonBindGen(t *testing.T) {
+	bp := `
+	rust_library {
+		name: "liba",
+		srcs: ["src/lib.rs"],
+		rlibs: ["libbindings"],
+		crate_name: "a"
+	}
+	rust_bindgen {
+		name: "libbindings",
+		crate_name: "bindings",
+		source_stem: "bindings",
+		host_supported: true,
+		wrapper_src: "src/any.h",
+	}
+	` + GatherRequiredDepsForTest()
+	fs := map[string][]byte{
+		"src/lib.rs": nil,
+	}
+	jsonContent := testProjectJson(t, bp, fs)
+	validateJsonCrates(t, jsonContent)
+}
+
+func TestProjectJsonMultiVersion(t *testing.T) {
+	bp := `
+	rust_library {
+		name: "liba1",
+		srcs: ["a1/src/lib.rs"],
+		crate_name: "a"
+	}
+	rust_library {
+		name: "liba2",
+		srcs: ["a2/src/lib.rs"],
+		crate_name: "a",
+	}
+	rust_library {
+		name: "libb",
+		srcs: ["b/src/lib.rs"],
+		crate_name: "b",
+		rustlibs: ["liba1", "liba2"],
+	}
+	` + GatherRequiredDepsForTest()
+	fs := map[string][]byte{
+		"a1/src/lib.rs": nil,
+		"a2/src/lib.rs": nil,
+		"b/src/lib.rs":  nil,
+	}
+	jsonContent := testProjectJson(t, bp, fs)
+	crates := validateJsonCrates(t, jsonContent)
+	for _, crate := range crates {
+		c := crate.(map[string]interface{})
+		if c["root_module"] == "b/src/lib.rs" {
+			deps, ok := c["deps"].([]interface{})
+			if !ok {
+				t.Errorf("Unexpected format for deps: %v", c["deps"])
+			}
+			aCount := 0
+			for _, dep := range deps {
+				d, ok := dep.(map[string]interface{})
+				if !ok {
+					t.Errorf("Unexpected format for dep: %v", dep)
+				}
+				if d["name"] == "a" {
+					aCount++
+				}
+			}
+			if aCount != 2 {
+				t.Errorf("Unexpected number of liba dependencies want %v, got %v: %v", 2, aCount, deps)
+			}
+			return
+		}
+	}
+	t.Errorf("libb crate has not been found: %v", crates)
 }
diff --git a/rust/rust.go b/rust/rust.go
index 78bf7ad..17fd042 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -63,7 +63,8 @@
 	AndroidMkSharedLibs    []string
 	AndroidMkStaticLibs    []string
 
-	SubName        string `blueprint:"mutated"`
+	SubName string `blueprint:"mutated"`
+
 	PreventInstall bool
 	HideFromMake   bool
 }
@@ -82,10 +83,10 @@
 	clippy           *clippy
 	cachedToolchain  config.Toolchain
 	sourceProvider   SourceProvider
-	subAndroidMkOnce map[subAndroidMkProvider]bool
-	outputFile       android.OptionalPath
+	subAndroidMkOnce map[SubAndroidMkProvider]bool
 
-	subName string
+	outputFile    android.OptionalPath
+	generatedFile android.OptionalPath
 }
 
 func (mod *Module) OutputFiles(tag string) (android.Paths, error) {
@@ -141,12 +142,8 @@
 
 func (mod *Module) NonCcVariants() bool {
 	if mod.compiler != nil {
-		if library, ok := mod.compiler.(libraryInterface); ok {
-			if library.buildRlib() || library.buildDylib() {
-				return true
-			} else {
-				return false
-			}
+		if _, ok := mod.compiler.(libraryInterface); ok {
+			return false
 		}
 	}
 	panic(fmt.Errorf("NonCcVariants called on non-library module: %q", mod.BaseModuleName()))
@@ -162,16 +159,16 @@
 			return library.static()
 		}
 	}
-	panic(fmt.Errorf("Static called on non-library module: %q", mod.BaseModuleName()))
+	return false
 }
 
 func (mod *Module) Shared() bool {
 	if mod.compiler != nil {
 		if library, ok := mod.compiler.(libraryInterface); ok {
-			return library.static()
+			return library.shared()
 		}
 	}
-	panic(fmt.Errorf("Shared called on non-library module: %q", mod.BaseModuleName()))
+	return false
 }
 
 func (mod *Module) Toc() android.OptionalPath {
@@ -289,6 +286,9 @@
 	relativeInstallPath() string
 
 	nativeCoverage() bool
+
+	Disabled() bool
+	SetDisabled()
 }
 
 type exportedFlagsProducer interface {
@@ -399,7 +399,9 @@
 
 func (mod *Module) CcLibraryInterface() bool {
 	if mod.compiler != nil {
-		if _, ok := mod.compiler.(libraryInterface); ok {
+		// use build{Static,Shared}() instead of {static,shared}() here because this might be called before
+		// VariantIs{Static,Shared} is set.
+		if lib, ok := mod.compiler.(libraryInterface); ok && (lib.buildShared() || lib.buildStatic()) {
 			return true
 		}
 	}
@@ -535,7 +537,7 @@
 		mod.AddProperties(mod.clippy.props()...)
 	}
 	if mod.sourceProvider != nil {
-		mod.AddProperties(mod.sourceProvider.sourceProviderProps()...)
+		mod.AddProperties(mod.sourceProvider.SourceProviderProps()...)
 	}
 
 	android.InitAndroidArchModule(mod, mod.hod, mod.multilib)
@@ -669,16 +671,21 @@
 		flags, deps = mod.clippy.flags(ctx, flags, deps)
 	}
 
-	if mod.compiler != nil {
+	// SourceProvider needs to call GenerateSource() before compiler calls compile() so it can provide the source.
+	// TODO(b/162588681) This shouldn't have to run for every variant.
+	if mod.sourceProvider != nil {
+		generatedFile := mod.sourceProvider.GenerateSource(ctx, deps)
+		mod.generatedFile = android.OptionalPathForPath(generatedFile)
+		mod.sourceProvider.setSubName(ctx.ModuleSubDir())
+	}
+
+	if mod.compiler != nil && !mod.compiler.Disabled() {
 		outputFile := mod.compiler.compile(ctx, flags, deps)
+
 		mod.outputFile = android.OptionalPathForPath(outputFile)
-		if !mod.Properties.PreventInstall {
+		if mod.outputFile.Valid() && !mod.Properties.PreventInstall {
 			mod.compiler.install(ctx, mod.outputFile.Path())
 		}
-	} else if mod.sourceProvider != nil {
-		outputFile := mod.sourceProvider.generateSource(ctx, deps)
-		mod.outputFile = android.OptionalPathForPath(outputFile)
-		mod.subName = ctx.ModuleSubDir()
 	}
 }
 
@@ -687,8 +694,9 @@
 
 	if mod.compiler != nil {
 		deps = mod.compiler.compilerDeps(ctx, deps)
-	} else if mod.sourceProvider != nil {
-		deps = mod.sourceProvider.sourceProviderDeps(ctx, deps)
+	}
+	if mod.sourceProvider != nil {
+		deps = mod.sourceProvider.SourceProviderDeps(ctx, deps)
 	}
 
 	if mod.coverage != nil {
@@ -714,10 +722,11 @@
 }
 
 var (
-	rlibDepTag       = dependencyTag{name: "rlibTag", library: true}
-	dylibDepTag      = dependencyTag{name: "dylib", library: true}
-	procMacroDepTag  = dependencyTag{name: "procMacro", proc_macro: true}
-	testPerSrcDepTag = dependencyTag{name: "rust_unit_tests"}
+	customBindgenDepTag = dependencyTag{name: "customBindgenTag"}
+	rlibDepTag          = dependencyTag{name: "rlibTag", library: true}
+	dylibDepTag         = dependencyTag{name: "dylib", library: true}
+	procMacroDepTag     = dependencyTag{name: "procMacro", proc_macro: true}
+	testPerSrcDepTag    = dependencyTag{name: "rust_unit_tests"}
 )
 
 type autoDep struct {
@@ -754,14 +763,9 @@
 	ctx.VisitDirectDeps(func(dep android.Module) {
 		depName := ctx.OtherModuleName(dep)
 		depTag := ctx.OtherModuleDependencyTag(dep)
-		if rustDep, ok := dep.(*Module); ok {
+		if rustDep, ok := dep.(*Module); ok && !rustDep.CcLibraryInterface() {
 			//Handle Rust Modules
 
-			linkFile := rustDep.outputFile
-			if !linkFile.Valid() {
-				ctx.ModuleErrorf("Invalid output file when adding dep %q to %q", depName, ctx.ModuleName())
-			}
-
 			switch depTag {
 			case dylibDepTag:
 				dylib, ok := rustDep.compiler.(libraryInterface)
@@ -803,30 +807,26 @@
 				directSrcProvidersDeps = append(directSrcProvidersDeps, rustDep)
 			}
 
-			//Append the dependencies exportedDirs
-			if lib, ok := rustDep.compiler.(exportedFlagsProducer); ok {
+			//Append the dependencies exportedDirs, except for proc-macros which target a different arch/OS
+			if lib, ok := rustDep.compiler.(exportedFlagsProducer); ok && depTag != procMacroDepTag {
 				depPaths.linkDirs = append(depPaths.linkDirs, lib.exportedLinkDirs()...)
 				depPaths.depFlags = append(depPaths.depFlags, lib.exportedDepFlags()...)
 			}
 
 			if depTag == dylibDepTag || depTag == rlibDepTag || depTag == procMacroDepTag {
+				linkFile := rustDep.outputFile
+				if !linkFile.Valid() {
+					ctx.ModuleErrorf("Invalid output file when adding dep %q to %q",
+						depName, ctx.ModuleName())
+					return
+				}
 				linkDir := linkPathFromFilePath(linkFile.Path())
 				if lib, ok := mod.compiler.(exportedFlagsProducer); ok {
 					lib.exportLinkDirs(linkDir)
 				}
 			}
 
-		}
-
-		if srcDep, ok := dep.(android.SourceFileProducer); ok {
-			switch depTag {
-			case android.SourceDepTag:
-				// These are usually genrules which don't have per-target variants.
-				directSrcDeps = append(directSrcDeps, srcDep)
-			}
-		}
-
-		if ccDep, ok := dep.(cc.LinkableInterface); ok {
+		} else if ccDep, ok := dep.(cc.LinkableInterface); ok {
 			//Handle C dependencies
 			if _, ok := ccDep.(*Module); !ok {
 				if ccDep.Module().Target().Os != ctx.Os() {
@@ -838,7 +838,6 @@
 					return
 				}
 			}
-
 			linkFile := ccDep.OutputFile()
 			linkPath := linkPathFromFilePath(linkFile.Path())
 			libName := libNameFromFilePath(linkFile.Path())
@@ -849,8 +848,8 @@
 			}
 
 			exportDep := false
-			switch depTag {
-			case cc.StaticDepTag:
+			switch {
+			case cc.IsStaticDepTag(depTag):
 				depFlag = "-lstatic=" + libName
 				depPaths.linkDirs = append(depPaths.linkDirs, linkPath)
 				depPaths.depFlags = append(depPaths.depFlags, depFlag)
@@ -862,7 +861,7 @@
 				depPaths.coverageFiles = append(depPaths.coverageFiles, ccDep.CoverageFiles()...)
 				directStaticLibDeps = append(directStaticLibDeps, ccDep)
 				mod.Properties.AndroidMkStaticLibs = append(mod.Properties.AndroidMkStaticLibs, depName)
-			case cc.SharedDepTag:
+			case cc.IsSharedDepTag(depTag):
 				depFlag = "-ldylib=" + libName
 				depPaths.linkDirs = append(depPaths.linkDirs, linkPath)
 				depPaths.depFlags = append(depPaths.depFlags, depFlag)
@@ -874,9 +873,9 @@
 				directSharedLibDeps = append(directSharedLibDeps, ccDep)
 				mod.Properties.AndroidMkSharedLibs = append(mod.Properties.AndroidMkSharedLibs, depName)
 				exportDep = true
-			case cc.CrtBeginDepTag:
+			case depTag == cc.CrtBeginDepTag:
 				depPaths.CrtBegin = linkFile
-			case cc.CrtEndDepTag:
+			case depTag == cc.CrtEndDepTag:
 				depPaths.CrtEnd = linkFile
 			}
 
@@ -886,6 +885,14 @@
 				lib.exportDepFlags(depFlag)
 			}
 		}
+
+		if srcDep, ok := dep.(android.SourceFileProducer); ok {
+			switch depTag {
+			case android.SourceDepTag:
+				// These are usually genrules which don't have per-target variants.
+				directSrcDeps = append(directSrcDeps, srcDep)
+			}
+		}
 	})
 
 	var rlibDepFiles RustLibraries
@@ -974,30 +981,27 @@
 	}
 	actx.AddVariationDependencies(
 		append(commonDepVariations, []blueprint.Variation{
-			{Mutator: "rust_libraries", Variation: "rlib"},
-			{Mutator: "link", Variation: ""}}...),
+			{Mutator: "rust_libraries", Variation: "rlib"}}...),
 		rlibDepTag, deps.Rlibs...)
 	actx.AddVariationDependencies(
 		append(commonDepVariations, []blueprint.Variation{
-			{Mutator: "rust_libraries", Variation: "dylib"},
-			{Mutator: "link", Variation: ""}}...),
+			{Mutator: "rust_libraries", Variation: "dylib"}}...),
 		dylibDepTag, deps.Dylibs...)
 
 	if deps.Rustlibs != nil {
 		autoDep := mod.compiler.(autoDeppable).autoDep()
 		actx.AddVariationDependencies(
 			append(commonDepVariations, []blueprint.Variation{
-				{Mutator: "rust_libraries", Variation: autoDep.variation},
-				{Mutator: "link", Variation: ""}}...),
+				{Mutator: "rust_libraries", Variation: autoDep.variation}}...),
 			autoDep.depTag, deps.Rustlibs...)
 	}
 
 	actx.AddVariationDependencies(append(commonDepVariations,
 		blueprint.Variation{Mutator: "link", Variation: "shared"}),
-		cc.SharedDepTag, deps.SharedLibs...)
+		cc.SharedDepTag(), deps.SharedLibs...)
 	actx.AddVariationDependencies(append(commonDepVariations,
 		blueprint.Variation{Mutator: "link", Variation: "static"}),
-		cc.StaticDepTag, deps.StaticLibs...)
+		cc.StaticDepTag(), deps.StaticLibs...)
 
 	if deps.CrtBegin != "" {
 		actx.AddVariationDependencies(commonDepVariations, cc.CrtBeginDepTag, deps.CrtBegin)
@@ -1006,6 +1010,13 @@
 		actx.AddVariationDependencies(commonDepVariations, cc.CrtEndDepTag, deps.CrtEnd)
 	}
 
+	if mod.sourceProvider != nil {
+		if bindgen, ok := mod.sourceProvider.(*bindgenDecorator); ok &&
+			bindgen.Properties.Custom_bindgen != "" {
+			actx.AddFarVariationDependencies(ctx.Config().BuildOSTarget.Variations(), customBindgenDepTag,
+				bindgen.Properties.Custom_bindgen)
+		}
+	}
 	// proc_macros are compiler plugins, and so we need the host arch variant as a dependendcy.
 	actx.AddFarVariationDependencies(ctx.Config().BuildOSTarget.Variations(), procMacroDepTag, deps.ProcMacros...)
 }
@@ -1034,6 +1045,12 @@
 	return name
 }
 
+func (mod *Module) setClippy(clippy bool) {
+	if mod.clippy != nil {
+		mod.clippy.Properties.Clippy = proptools.BoolPtr(clippy)
+	}
+}
+
 var _ android.HostToolProvider = (*Module)(nil)
 
 func (mod *Module) HostToolPath() android.OptionalPath {
diff --git a/rust/rust_test.go b/rust/rust_test.go
index b3bbddb..04de48b 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -233,6 +233,7 @@
 				":my_generator",
 				":libbindings",
 			],
+			rlibs: ["libbindings"],
 		}
 		rust_proc_macro {
 			name: "libprocmacro",
@@ -241,6 +242,7 @@
 				":my_generator",
 				":libbindings",
 			],
+			rlibs: ["libbindings"],
 			crate_name: "procmacro",
 		}
 		rust_library {
@@ -248,7 +250,9 @@
 			srcs: [
 				"foo.rs",
 				":my_generator",
-				":libbindings"],
+				":libbindings",
+			],
+			rlibs: ["libbindings"],
 			crate_name: "foo",
 		}
 		genrule {
@@ -260,7 +264,8 @@
 		}
 		rust_bindgen {
 			name: "libbindings",
-			stem: "bindings",
+			crate_name: "bindings",
+			source_stem: "bindings",
 			host_supported: true,
 			wrapper_src: "src/any.h",
         }
@@ -289,6 +294,21 @@
 	if !android.SuffixInList(libprocmacro.Implicits.Strings(), "/out/any.rs") {
 		t.Errorf("genrule generated source not included as implicit input for libprocmacro; Implicits %#v", libfoo.Implicits.Strings())
 	}
+
+	// Check that our bindings are picked up as crate dependencies as well
+	libfooMod := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib").Module().(*Module)
+	if !android.InList("libbindings", libfooMod.Properties.AndroidMkRlibs) {
+		t.Errorf("bindgen dependency not detected as a rlib dependency (dependency missing from AndroidMkRlibs)")
+	}
+	fizzBuzzMod := ctx.ModuleForTests("fizz-buzz-dep", "android_arm64_armv8-a").Module().(*Module)
+	if !android.InList("libbindings", fizzBuzzMod.Properties.AndroidMkRlibs) {
+		t.Errorf("bindgen dependency not detected as a rlib dependency (dependency missing from AndroidMkRlibs)")
+	}
+	libprocmacroMod := ctx.ModuleForTests("libprocmacro", "linux_glibc_x86_64").Module().(*Module)
+	if !android.InList("libbindings", libprocmacroMod.Properties.AndroidMkRlibs) {
+		t.Errorf("bindgen dependency not detected as a rlib dependency (dependency missing from AndroidMkRlibs)")
+	}
+
 }
 
 func TestSourceProviderTargetMismatch(t *testing.T) {
@@ -305,7 +325,8 @@
 		}
 		rust_bindgen {
 			name: "libbindings",
-			stem: "bindings",
+			crate_name: "bindings",
+			source_stem: "bindings",
 			wrapper_src: "src/any.h",
 		}
 	`)
diff --git a/rust/source_provider.go b/rust/source_provider.go
index da6147a..76679c2 100644
--- a/rust/source_provider.go
+++ b/rust/source_provider.go
@@ -19,52 +19,79 @@
 )
 
 type SourceProviderProperties struct {
-	// sets name of the output
-	Stem *string `android:"arch_variant"`
+	// filename for the generated source file (<source_stem>.rs). This field is required.
+	// The inherited "stem" property sets the output filename for the generated library variants only.
+	Source_stem *string `android:"arch_variant"`
+
+	// crate name, used for the library variant of this source provider. See additional details in rust_library.
+	Crate_name string `android:"arch_variant"`
 }
 
-type baseSourceProvider struct {
+type BaseSourceProvider struct {
 	Properties SourceProviderProperties
 
-	outputFile       android.Path
-	subAndroidMkOnce map[subAndroidMkProvider]bool
+	OutputFile       android.Path
+	subAndroidMkOnce map[SubAndroidMkProvider]bool
+	subName          string
 }
 
-var _ SourceProvider = (*baseSourceProvider)(nil)
+var _ SourceProvider = (*BaseSourceProvider)(nil)
 
 type SourceProvider interface {
-	generateSource(ctx android.ModuleContext, deps PathDeps) android.Path
+	GenerateSource(ctx android.ModuleContext, deps PathDeps) android.Path
 	Srcs() android.Paths
-	sourceProviderProps() []interface{}
-	sourceProviderDeps(ctx DepsContext, deps Deps) Deps
+	SourceProviderProps() []interface{}
+	SourceProviderDeps(ctx DepsContext, deps Deps) Deps
+	setSubName(subName string)
 }
 
-func (sp *baseSourceProvider) Srcs() android.Paths {
-	return android.Paths{sp.outputFile}
+func (sp *BaseSourceProvider) Srcs() android.Paths {
+	return android.Paths{sp.OutputFile}
 }
 
-func (sp *baseSourceProvider) generateSource(ctx android.ModuleContext, deps PathDeps) android.Path {
-	panic("baseSourceProviderModule does not implement generateSource()")
+func (sp *BaseSourceProvider) GenerateSource(ctx android.ModuleContext, deps PathDeps) android.Path {
+	panic("BaseSourceProviderModule does not implement GenerateSource()")
 }
 
-func (sp *baseSourceProvider) sourceProviderProps() []interface{} {
+func (sp *BaseSourceProvider) SourceProviderProps() []interface{} {
 	return []interface{}{&sp.Properties}
 }
 
-func NewSourceProvider() *baseSourceProvider {
-	return &baseSourceProvider{
+func NewSourceProvider() *BaseSourceProvider {
+	return &BaseSourceProvider{
 		Properties: SourceProviderProperties{},
 	}
 }
 
-func (sp *baseSourceProvider) getStem(ctx android.ModuleContext) string {
-	stem := ctx.ModuleName()
-	if String(sp.Properties.Stem) != "" {
-		stem = String(sp.Properties.Stem)
+func NewSourceProviderModule(hod android.HostOrDeviceSupported, sourceProvider SourceProvider, enableLints bool) *Module {
+	_, library := NewRustLibrary(hod)
+	library.BuildOnlyRust()
+	library.sourceProvider = sourceProvider
+
+	module := newModule(hod, android.MultilibBoth)
+	module.sourceProvider = sourceProvider
+	module.compiler = library
+
+	if !enableLints {
+		library.setNoLint()
+		module.setClippy(false)
 	}
-	return stem
+
+	return module
 }
 
-func (sp *baseSourceProvider) sourceProviderDeps(ctx DepsContext, deps Deps) Deps {
+func (sp *BaseSourceProvider) getStem(ctx android.ModuleContext) string {
+	if String(sp.Properties.Source_stem) == "" {
+		ctx.PropertyErrorf("source_stem",
+			"source_stem property is undefined but required for rust_bindgen modules")
+	}
+	return String(sp.Properties.Source_stem)
+}
+
+func (sp *BaseSourceProvider) SourceProviderDeps(ctx DepsContext, deps Deps) Deps {
 	return deps
 }
+
+func (sp *BaseSourceProvider) setSubName(subName string) {
+	sp.subName = subName
+}
diff --git a/rust/source_provider_test.go b/rust/source_provider_test.go
new file mode 100644
index 0000000..6e68ae6
--- /dev/null
+++ b/rust/source_provider_test.go
@@ -0,0 +1,31 @@
+// Copyright 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rust
+
+import (
+	"testing"
+)
+
+var stemRequiredError = "source_stem property is undefined but required for rust_bindgen modules"
+
+func TestSourceProviderRequiredFields(t *testing.T) {
+	testRustError(t, stemRequiredError, `
+		rust_bindgen {
+			name: "libbindgen",
+			wrapper_src: "src/any.h",
+			crate_name: "bindgen",
+		}
+	`)
+}
diff --git a/rust/test.go b/rust/test.go
index e27a70c..05c361e 100644
--- a/rust/test.go
+++ b/rust/test.go
@@ -41,6 +41,9 @@
 	// doesn't exist next to the Android.bp, this attribute doesn't need to be set to true
 	// explicitly.
 	Auto_gen_config *bool
+
+	// if set, build with the standard Rust test harness. Defaults to true.
+	Test_harness *bool
 }
 
 // A test module is a binary module with extra --test compiler flag
@@ -56,6 +59,10 @@
 	return true
 }
 
+func (test *testDecorator) testHarness() bool {
+	return BoolDefault(test.Properties.Test_harness, true)
+}
+
 func NewRustTest(hod android.HostOrDeviceSupported) (*Module, *testDecorator) {
 	// Build both 32 and 64 targets for device tests.
 	// Cannot build both for host tests yet if the test depends on
@@ -101,7 +108,9 @@
 
 func (test *testDecorator) compilerFlags(ctx ModuleContext, flags Flags) Flags {
 	flags = test.binaryDecorator.compilerFlags(ctx, flags)
-	flags.RustFlags = append(flags.RustFlags, "--test")
+	if test.testHarness() {
+		flags.RustFlags = append(flags.RustFlags, "--test")
+	}
 	return flags
 }
 
diff --git a/rust/testing.go b/rust/testing.go
index f2d4c5e..925b02c 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -44,7 +44,28 @@
                                 },
 				host_supported: true,
 		}
-
+		rust_prebuilt_library {
+				name: "libstd_x86_64-apple-darwin",
+                                crate_name: "std",
+                                rlib: {
+                                    srcs: ["libstd.rlib"],
+                                },
+                                dylib: {
+                                    srcs: ["libstd.so"],
+                                },
+				host_supported: true,
+		}
+		rust_prebuilt_library {
+				name: "libtest_x86_64-apple-darwin",
+                                crate_name: "test",
+                                rlib: {
+                                    srcs: ["libtest.rlib"],
+                                },
+                                dylib: {
+                                    srcs: ["libtest.so"],
+                                },
+				host_supported: true,
+		}
 		//////////////////////////////
 		// Device module requirements
 
@@ -77,6 +98,7 @@
 
 func CreateTestContext() *android.TestContext {
 	ctx := android.NewTestArchContext()
+	android.RegisterPrebuiltMutators(ctx)
 	cc.RegisterRequiredBuildComponentsForTest(ctx)
 	ctx.RegisterModuleType("genrule", genrule.GenRuleFactory)
 	ctx.RegisterModuleType("rust_binary", RustBinaryFactory)
diff --git a/scripts/build-mainline-modules.sh b/scripts/build-mainline-modules.sh
index 1bc78e6..c5ec8d1 100755
--- a/scripts/build-mainline-modules.sh
+++ b/scripts/build-mainline-modules.sh
@@ -24,7 +24,7 @@
   i18n-module-test-exports
   i18n-module-sdk
   platform-mainline-sdk
-  platform-mainline-host-exports
+  platform-mainline-test-exports
 )
 
 # List of libraries installed on the platform that are needed for ART chroot
@@ -52,6 +52,13 @@
   "$@"
 }
 
+lib_dir() {
+  case $1 in
+    (aosp_arm|aosp_x86) echo "lib";;
+    (aosp_arm64|aosp_x86_64) echo "lib64";;
+  esac
+}
+
 OUT_DIR=$(source build/envsetup.sh > /dev/null; TARGET_PRODUCT= get_build_var OUT_DIR)
 DIST_DIR=$(source build/envsetup.sh > /dev/null; TARGET_PRODUCT= get_build_var DIST_DIR)
 
@@ -69,7 +76,8 @@
     echo_and_run cp ${PWD}/${PRODUCT_OUT}/system/apex/${module}.apex ${DIST_DIR}/${TARGET_ARCH}/
   done
   for library in "${PLATFORM_LIBRARIES[@]}"; do
-    echo_and_run cp ${PWD}/${PRODUCT_OUT}/system/lib/${library}.so ${DIST_DIR}/${TARGET_ARCH}/
+    libdir=$(lib_dir $product)
+    echo_and_run cp ${PWD}/${PRODUCT_OUT}/system/${libdir}/${library}.so ${DIST_DIR}/${TARGET_ARCH}/
   done
 done
 
diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go
index 497f14b..9501d88 100644
--- a/sdk/cc_sdk_test.go
+++ b/sdk/cc_sdk_test.go
@@ -39,6 +39,20 @@
 
 // Contains tests for SDK members provided by the cc package.
 
+func TestSingleDeviceOsAssumption(t *testing.T) {
+	// Mock a module with DeviceSupported() == true.
+	s := &sdk{}
+	android.InitAndroidArchModule(s, android.DeviceSupported, android.MultilibCommon)
+
+	osTypes := s.getPossibleOsTypes()
+	if len(osTypes) != 1 {
+		// The snapshot generation assumes there is a single device OS. If more are
+		// added it might need to disable them by default, like it does for host
+		// OS'es.
+		t.Errorf("expected a single device OS, got %v", osTypes)
+	}
+}
+
 func TestSdkIsCompileMultilibBoth(t *testing.T) {
 	result := testSdkWithCc(t, `
 		sdk {
@@ -73,7 +87,6 @@
 	result := testSdkWithCc(t, `
 		sdk {
 			name: "mysdk",
-			device_supported: false,
 			host_supported: true,
 			native_shared_libs: ["sdkmember"],
 			compile_multilib: "64",
@@ -81,7 +94,6 @@
 
 		cc_library_shared {
 			name: "sdkmember",
-			device_supported: false,
 			host_supported: true,
 			srcs: ["Test.cpp"],
 			stl: "none",
@@ -96,14 +108,22 @@
 cc_prebuilt_library_shared {
     name: "mysdk_sdkmember@current",
     sdk_member_name: "sdkmember",
-    device_supported: false,
     host_supported: true,
     installable: false,
     stl: "none",
     compile_multilib: "64",
-    arch: {
-        x86_64: {
-            srcs: ["x86_64/lib/sdkmember.so"],
+    target: {
+        host: {
+            enabled: false,
+        },
+        android_arm64: {
+            srcs: ["android/arm64/lib/sdkmember.so"],
+        },
+        linux_glibc: {
+            enabled: true,
+        },
+        linux_glibc_x86_64: {
+            srcs: ["linux_glibc/x86_64/lib/sdkmember.so"],
         },
     },
 }
@@ -111,31 +131,43 @@
 cc_prebuilt_library_shared {
     name: "sdkmember",
     prefer: false,
-    device_supported: false,
     host_supported: true,
     stl: "none",
     compile_multilib: "64",
-    arch: {
-        x86_64: {
-            srcs: ["x86_64/lib/sdkmember.so"],
+    target: {
+        host: {
+            enabled: false,
+        },
+        android_arm64: {
+            srcs: ["android/arm64/lib/sdkmember.so"],
+        },
+        linux_glibc: {
+            enabled: true,
+        },
+        linux_glibc_x86_64: {
+            srcs: ["linux_glibc/x86_64/lib/sdkmember.so"],
         },
     },
 }
 
 sdk_snapshot {
     name: "mysdk@current",
-    device_supported: false,
     host_supported: true,
     native_shared_libs: ["mysdk_sdkmember@current"],
+    compile_multilib: "64",
     target: {
+        host: {
+            enabled: false,
+        },
         linux_glibc: {
-            compile_multilib: "64",
+            enabled: true,
         },
     },
 }
 `),
 		checkAllCopyRules(`
-.intermediates/sdkmember/linux_glibc_x86_64_shared/sdkmember.so -> x86_64/lib/sdkmember.so
+.intermediates/sdkmember/android_arm64_armv8-a_shared/sdkmember.so -> android/arm64/lib/sdkmember.so
+.intermediates/sdkmember/linux_glibc_x86_64_shared/sdkmember.so -> linux_glibc/x86_64/lib/sdkmember.so
 `))
 }
 
@@ -575,7 +607,11 @@
     installable: false,
     stl: "none",
     target: {
+        host: {
+            enabled: false,
+        },
         linux_glibc: {
+            enabled: true,
             compile_multilib: "both",
         },
         linux_glibc_x86_64: {
@@ -585,6 +621,7 @@
             srcs: ["linux_glibc/x86/bin/mynativebinary"],
         },
         windows: {
+            enabled: true,
             compile_multilib: "64",
         },
         windows_x86_64: {
@@ -600,7 +637,11 @@
     host_supported: true,
     stl: "none",
     target: {
+        host: {
+            enabled: false,
+        },
         linux_glibc: {
+            enabled: true,
             compile_multilib: "both",
         },
         linux_glibc_x86_64: {
@@ -610,6 +651,7 @@
             srcs: ["linux_glibc/x86/bin/mynativebinary"],
         },
         windows: {
+            enabled: true,
             compile_multilib: "64",
         },
         windows_x86_64: {
@@ -624,7 +666,14 @@
     host_supported: true,
     native_binaries: ["myexports_mynativebinary@current"],
     target: {
+        host: {
+            enabled: false,
+        },
+        linux_glibc: {
+            enabled: true,
+        },
         windows: {
+            enabled: true,
             compile_multilib: "64",
         },
     },
@@ -638,6 +687,162 @@
 	)
 }
 
+func TestSnapshotWithSingleHostOsType(t *testing.T) {
+	ctx, config := testSdkContext(`
+		cc_defaults {
+			name: "mydefaults",
+			device_supported: false,
+			host_supported: true,
+			compile_multilib: "64",
+			target: {
+				host: {
+					enabled: false,
+				},
+				linux_bionic: {
+					enabled: true,
+				},
+			},
+		}
+
+		module_exports {
+			name: "myexports",
+			defaults: ["mydefaults"],
+			native_shared_libs: ["mynativelib"],
+			native_binaries: ["mynativebinary"],
+			compile_multilib: "64",  // The built-in default in sdk.go overrides mydefaults.
+		}
+
+		cc_library {
+			name: "mynativelib",
+			defaults: ["mydefaults"],
+			srcs: [
+				"Test.cpp",
+			],
+			stl: "none",
+		}
+
+		cc_binary {
+			name: "mynativebinary",
+			defaults: ["mydefaults"],
+			srcs: [
+				"Test.cpp",
+			],
+			stl: "none",
+		}
+	`, ccTestFs, []android.OsType{android.LinuxBionic})
+
+	result := runTests(t, ctx, config)
+
+	result.CheckSnapshot("myexports", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_binary {
+    name: "myexports_mynativebinary@current",
+    sdk_member_name: "mynativebinary",
+    device_supported: false,
+    host_supported: true,
+    installable: false,
+    stl: "none",
+    compile_multilib: "64",
+    target: {
+        host: {
+            enabled: false,
+        },
+        linux_bionic: {
+            enabled: true,
+        },
+        linux_bionic_x86_64: {
+            srcs: ["x86_64/bin/mynativebinary"],
+        },
+    },
+}
+
+cc_prebuilt_binary {
+    name: "mynativebinary",
+    prefer: false,
+    device_supported: false,
+    host_supported: true,
+    stl: "none",
+    compile_multilib: "64",
+    target: {
+        host: {
+            enabled: false,
+        },
+        linux_bionic: {
+            enabled: true,
+        },
+        linux_bionic_x86_64: {
+            srcs: ["x86_64/bin/mynativebinary"],
+        },
+    },
+}
+
+cc_prebuilt_library_shared {
+    name: "myexports_mynativelib@current",
+    sdk_member_name: "mynativelib",
+    device_supported: false,
+    host_supported: true,
+    installable: false,
+    stl: "none",
+    compile_multilib: "64",
+    target: {
+        host: {
+            enabled: false,
+        },
+        linux_bionic: {
+            enabled: true,
+        },
+        linux_bionic_x86_64: {
+            srcs: ["x86_64/lib/mynativelib.so"],
+        },
+    },
+}
+
+cc_prebuilt_library_shared {
+    name: "mynativelib",
+    prefer: false,
+    device_supported: false,
+    host_supported: true,
+    stl: "none",
+    compile_multilib: "64",
+    target: {
+        host: {
+            enabled: false,
+        },
+        linux_bionic: {
+            enabled: true,
+        },
+        linux_bionic_x86_64: {
+            srcs: ["x86_64/lib/mynativelib.so"],
+        },
+    },
+}
+
+module_exports_snapshot {
+    name: "myexports@current",
+    device_supported: false,
+    host_supported: true,
+    native_binaries: ["myexports_mynativebinary@current"],
+    native_shared_libs: ["myexports_mynativelib@current"],
+    compile_multilib: "64",
+    target: {
+        host: {
+            enabled: false,
+        },
+        linux_bionic: {
+            enabled: true,
+        },
+    },
+}
+`),
+		checkAllCopyRules(`
+.intermediates/mynativebinary/linux_bionic_x86_64/mynativebinary -> x86_64/bin/mynativebinary
+.intermediates/mynativelib/linux_bionic_x86_64_shared/mynativelib.so -> x86_64/lib/mynativelib.so
+`),
+	)
+}
+
 // Test that we support the necessary flags for the linker binary, which is
 // special in several ways.
 func TestSnapshotWithCcStaticNocrtBinary(t *testing.T) {
@@ -676,11 +881,17 @@
     compile_multilib: "both",
     static_executable: true,
     nocrt: true,
-    arch: {
-        x86_64: {
+    target: {
+        host: {
+            enabled: false,
+        },
+        linux_glibc: {
+            enabled: true,
+        },
+        linux_glibc_x86_64: {
             srcs: ["x86_64/bin/linker"],
         },
-        x86: {
+        linux_glibc_x86: {
             srcs: ["x86/bin/linker"],
         },
     },
@@ -695,11 +906,17 @@
     compile_multilib: "both",
     static_executable: true,
     nocrt: true,
-    arch: {
-        x86_64: {
+    target: {
+        host: {
+            enabled: false,
+        },
+        linux_glibc: {
+            enabled: true,
+        },
+        linux_glibc_x86_64: {
             srcs: ["x86_64/bin/linker"],
         },
-        x86: {
+        linux_glibc_x86: {
             srcs: ["x86/bin/linker"],
         },
     },
@@ -710,6 +927,14 @@
     device_supported: false,
     host_supported: true,
     native_binaries: ["mymodule_exports_linker@current"],
+    target: {
+        host: {
+            enabled: false,
+        },
+        linux_glibc: {
+            enabled: true,
+        },
+    },
 }
 `),
 		checkAllCopyRules(`
@@ -1036,12 +1261,18 @@
     stl: "none",
     compile_multilib: "both",
     export_include_dirs: ["include/include"],
-    arch: {
-        x86_64: {
+    target: {
+        host: {
+            enabled: false,
+        },
+        linux_glibc: {
+            enabled: true,
+        },
+        linux_glibc_x86_64: {
             srcs: ["x86_64/lib/mynativelib.so"],
             export_include_dirs: ["x86_64/include_gen/mynativelib"],
         },
-        x86: {
+        linux_glibc_x86: {
             srcs: ["x86/lib/mynativelib.so"],
             export_include_dirs: ["x86/include_gen/mynativelib"],
         },
@@ -1057,12 +1288,18 @@
     stl: "none",
     compile_multilib: "both",
     export_include_dirs: ["include/include"],
-    arch: {
-        x86_64: {
+    target: {
+        host: {
+            enabled: false,
+        },
+        linux_glibc: {
+            enabled: true,
+        },
+        linux_glibc_x86_64: {
             srcs: ["x86_64/lib/mynativelib.so"],
             export_include_dirs: ["x86_64/include_gen/mynativelib"],
         },
-        x86: {
+        linux_glibc_x86: {
             srcs: ["x86/lib/mynativelib.so"],
             export_include_dirs: ["x86/include_gen/mynativelib"],
         },
@@ -1074,6 +1311,14 @@
     device_supported: false,
     host_supported: true,
     native_shared_libs: ["mysdk_mynativelib@current"],
+    target: {
+        host: {
+            enabled: false,
+        },
+        linux_glibc: {
+            enabled: true,
+        },
+    },
 }
 `),
 		checkAllCopyRules(`
@@ -1132,7 +1377,11 @@
     installable: false,
     stl: "none",
     target: {
+        host: {
+            enabled: false,
+        },
         linux_glibc: {
+            enabled: true,
             compile_multilib: "both",
         },
         linux_glibc_x86_64: {
@@ -1142,6 +1391,7 @@
             srcs: ["linux_glibc/x86/lib/mynativelib.so"],
         },
         windows: {
+            enabled: true,
             compile_multilib: "64",
         },
         windows_x86_64: {
@@ -1157,7 +1407,11 @@
     host_supported: true,
     stl: "none",
     target: {
+        host: {
+            enabled: false,
+        },
         linux_glibc: {
+            enabled: true,
             compile_multilib: "both",
         },
         linux_glibc_x86_64: {
@@ -1167,6 +1421,7 @@
             srcs: ["linux_glibc/x86/lib/mynativelib.so"],
         },
         windows: {
+            enabled: true,
             compile_multilib: "64",
         },
         windows_x86_64: {
@@ -1181,7 +1436,14 @@
     host_supported: true,
     native_shared_libs: ["mysdk_mynativelib@current"],
     target: {
+        host: {
+            enabled: false,
+        },
+        linux_glibc: {
+            enabled: true,
+        },
         windows: {
+            enabled: true,
             compile_multilib: "64",
         },
     },
@@ -1314,12 +1576,18 @@
     stl: "none",
     compile_multilib: "both",
     export_include_dirs: ["include/include"],
-    arch: {
-        x86_64: {
+    target: {
+        host: {
+            enabled: false,
+        },
+        linux_glibc: {
+            enabled: true,
+        },
+        linux_glibc_x86_64: {
             srcs: ["x86_64/lib/mynativelib.a"],
             export_include_dirs: ["x86_64/include_gen/mynativelib"],
         },
-        x86: {
+        linux_glibc_x86: {
             srcs: ["x86/lib/mynativelib.a"],
             export_include_dirs: ["x86/include_gen/mynativelib"],
         },
@@ -1334,12 +1602,18 @@
     stl: "none",
     compile_multilib: "both",
     export_include_dirs: ["include/include"],
-    arch: {
-        x86_64: {
+    target: {
+        host: {
+            enabled: false,
+        },
+        linux_glibc: {
+            enabled: true,
+        },
+        linux_glibc_x86_64: {
             srcs: ["x86_64/lib/mynativelib.a"],
             export_include_dirs: ["x86_64/include_gen/mynativelib"],
         },
-        x86: {
+        linux_glibc_x86: {
             srcs: ["x86/lib/mynativelib.a"],
             export_include_dirs: ["x86/include_gen/mynativelib"],
         },
@@ -1351,6 +1625,14 @@
     device_supported: false,
     host_supported: true,
     native_static_libs: ["myexports_mynativelib@current"],
+    target: {
+        host: {
+            enabled: false,
+        },
+        linux_glibc: {
+            enabled: true,
+        },
+    },
 }
 `),
 		checkAllCopyRules(`
@@ -1498,8 +1780,14 @@
     stl: "none",
     compile_multilib: "64",
     export_include_dirs: ["include/include"],
-    arch: {
-        x86_64: {
+    target: {
+        host: {
+            enabled: false,
+        },
+        linux_glibc: {
+            enabled: true,
+        },
+        linux_glibc_x86_64: {
             srcs: ["x86_64/lib/mynativelib.a"],
             export_include_dirs: ["x86_64/include_gen/mynativelib"],
         },
@@ -1514,8 +1802,14 @@
     stl: "none",
     compile_multilib: "64",
     export_include_dirs: ["include/include"],
-    arch: {
-        x86_64: {
+    target: {
+        host: {
+            enabled: false,
+        },
+        linux_glibc: {
+            enabled: true,
+        },
+        linux_glibc_x86_64: {
             srcs: ["x86_64/lib/mynativelib.a"],
             export_include_dirs: ["x86_64/include_gen/mynativelib"],
         },
@@ -1527,9 +1821,13 @@
     device_supported: false,
     host_supported: true,
     native_static_libs: ["myexports_mynativelib@current"],
+    compile_multilib: "64",
     target: {
+        host: {
+            enabled: false,
+        },
         linux_glibc: {
-            compile_multilib: "64",
+            enabled: true,
         },
     },
 }`),
@@ -1618,6 +1916,14 @@
     stl: "none",
     compile_multilib: "both",
     export_include_dirs: ["include/include"],
+    target: {
+        host: {
+            enabled: false,
+        },
+        linux_glibc: {
+            enabled: true,
+        },
+    },
 }
 
 cc_prebuilt_library_headers {
@@ -1628,6 +1934,14 @@
     stl: "none",
     compile_multilib: "both",
     export_include_dirs: ["include/include"],
+    target: {
+        host: {
+            enabled: false,
+        },
+        linux_glibc: {
+            enabled: true,
+        },
+    },
 }
 
 sdk_snapshot {
@@ -1635,6 +1949,14 @@
     device_supported: false,
     host_supported: true,
     native_header_libs: ["mysdk_mynativeheaders@current"],
+    target: {
+        host: {
+            enabled: false,
+        },
+        linux_glibc: {
+            enabled: true,
+        },
+    },
 }
 `),
 		checkAllCopyRules(`
@@ -1679,10 +2001,14 @@
     compile_multilib: "both",
     export_system_include_dirs: ["common_os/include/include"],
     target: {
+        host: {
+            enabled: false,
+        },
         android: {
             export_include_dirs: ["android/include/include-android"],
         },
         linux_glibc: {
+            enabled: true,
             export_include_dirs: ["linux_glibc/include/include-host"],
         },
     },
@@ -1696,10 +2022,14 @@
     compile_multilib: "both",
     export_system_include_dirs: ["common_os/include/include"],
     target: {
+        host: {
+            enabled: false,
+        },
         android: {
             export_include_dirs: ["android/include/include-android"],
         },
         linux_glibc: {
+            enabled: true,
             export_include_dirs: ["linux_glibc/include/include-host"],
         },
     },
@@ -1709,6 +2039,14 @@
     name: "mysdk@current",
     host_supported: true,
     native_header_libs: ["mysdk_mynativeheaders@current"],
+    target: {
+        host: {
+            enabled: false,
+        },
+        linux_glibc: {
+            enabled: true,
+        },
+    },
 }
 `),
 		checkAllCopyRules(`
@@ -1876,6 +2214,9 @@
     installable: false,
     compile_multilib: "both",
     target: {
+        host: {
+            enabled: false,
+        },
         android: {
             system_shared_libs: [],
         },
@@ -1885,6 +2226,9 @@
         android_arm: {
             srcs: ["android/arm/lib/sslvariants.so"],
         },
+        linux_glibc: {
+            enabled: true,
+        },
         linux_glibc_x86_64: {
             srcs: ["linux_glibc/x86_64/lib/sslvariants.so"],
         },
@@ -1900,6 +2244,9 @@
     host_supported: true,
     compile_multilib: "both",
     target: {
+        host: {
+            enabled: false,
+        },
         android: {
             system_shared_libs: [],
         },
@@ -1909,6 +2256,9 @@
         android_arm: {
             srcs: ["android/arm/lib/sslvariants.so"],
         },
+        linux_glibc: {
+            enabled: true,
+        },
         linux_glibc_x86_64: {
             srcs: ["linux_glibc/x86_64/lib/sslvariants.so"],
         },
@@ -1922,6 +2272,14 @@
     name: "mysdk@current",
     host_supported: true,
     native_shared_libs: ["mysdk_sslvariants@current"],
+    target: {
+        host: {
+            enabled: false,
+        },
+        linux_glibc: {
+            enabled: true,
+        },
+    },
 }
 `))
 }
@@ -2031,12 +2389,18 @@
         versions: ["3"],
     },
     target: {
+        host: {
+            enabled: false,
+        },
         android_arm64: {
             srcs: ["android/arm64/lib/stubslib.so"],
         },
         android_arm: {
             srcs: ["android/arm/lib/stubslib.so"],
         },
+        linux_glibc: {
+            enabled: true,
+        },
         linux_glibc_x86_64: {
             srcs: ["linux_glibc/x86_64/lib/stubslib.so"],
         },
@@ -2055,12 +2419,18 @@
         versions: ["3"],
     },
     target: {
+        host: {
+            enabled: false,
+        },
         android_arm64: {
             srcs: ["android/arm64/lib/stubslib.so"],
         },
         android_arm: {
             srcs: ["android/arm/lib/stubslib.so"],
         },
+        linux_glibc: {
+            enabled: true,
+        },
         linux_glibc_x86_64: {
             srcs: ["linux_glibc/x86_64/lib/stubslib.so"],
         },
@@ -2074,6 +2444,14 @@
     name: "mysdk@current",
     host_supported: true,
     native_shared_libs: ["mysdk_stubslib@current"],
+    target: {
+        host: {
+            enabled: false,
+        },
+        linux_glibc: {
+            enabled: true,
+        },
+    },
 }
 `))
 }
@@ -2105,12 +2483,18 @@
     unique_host_soname: true,
     compile_multilib: "both",
     target: {
+        host: {
+            enabled: false,
+        },
         android_arm64: {
             srcs: ["android/arm64/lib/mylib.so"],
         },
         android_arm: {
             srcs: ["android/arm/lib/mylib.so"],
         },
+        linux_glibc: {
+            enabled: true,
+        },
         linux_glibc_x86_64: {
             srcs: ["linux_glibc/x86_64/lib/mylib-host.so"],
         },
@@ -2127,12 +2511,18 @@
     unique_host_soname: true,
     compile_multilib: "both",
     target: {
+        host: {
+            enabled: false,
+        },
         android_arm64: {
             srcs: ["android/arm64/lib/mylib.so"],
         },
         android_arm: {
             srcs: ["android/arm/lib/mylib.so"],
         },
+        linux_glibc: {
+            enabled: true,
+        },
         linux_glibc_x86_64: {
             srcs: ["linux_glibc/x86_64/lib/mylib-host.so"],
         },
@@ -2146,6 +2536,14 @@
     name: "mysdk@current",
     host_supported: true,
     native_shared_libs: ["mysdk_mylib@current"],
+    target: {
+        host: {
+            enabled: false,
+        },
+        linux_glibc: {
+            enabled: true,
+        },
+    },
 }
 `),
 		checkAllCopyRules(`
diff --git a/sdk/testing.go b/sdk/testing.go
index 34ea8f0..b53558d 100644
--- a/sdk/testing.go
+++ b/sdk/testing.go
@@ -97,6 +97,11 @@
 	ctx.PreArchMutators(android.RegisterVisibilityRuleChecker)
 	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
 	ctx.PreArchMutators(android.RegisterComponentsMutator)
+
+	android.RegisterPrebuiltMutators(ctx)
+
+	// Register these after the prebuilt mutators have been registered to match what
+	// happens at runtime.
 	ctx.PreArchMutators(android.RegisterVisibilityRuleGatherer)
 	ctx.PostDepsMutators(android.RegisterVisibilityRuleEnforcer)
 
diff --git a/sdk/update.go b/sdk/update.go
index b8d73c6..936696a 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -262,7 +262,7 @@
 		memberCtx := &memberContext{ctx, builder, memberType, member.name}
 
 		prebuiltModule := memberType.AddPrebuiltModule(memberCtx, member)
-		s.createMemberSnapshot(memberCtx, member, prebuiltModule)
+		s.createMemberSnapshot(memberCtx, member, prebuiltModule.(*bpModule))
 	}
 
 	// Create a transformer that will transform an unversioned module into a versioned module.
@@ -323,19 +323,62 @@
 	// Add properties common to all os types.
 	s.addMemberPropertiesToPropertySet(builder, snapshotModule, commonDynamicMemberProperties)
 
-	// Iterate over the os types in a fixed order.
+	// Optimize other per-variant properties, besides the dynamic member lists.
+	type variantProperties struct {
+		Compile_multilib string `android:"arch_variant"`
+	}
+	var variantPropertiesContainers []propertiesContainer
+	variantToProperties := make(map[*sdk]*variantProperties)
+	for _, sdkVariant := range sdkVariants {
+		props := &variantProperties{
+			Compile_multilib: sdkVariant.multilibUsages.String(),
+		}
+		variantPropertiesContainers = append(variantPropertiesContainers, &dynamicMemberPropertiesContainer{sdkVariant, props})
+		variantToProperties[sdkVariant] = props
+	}
+	commonVariantProperties := variantProperties{}
+	extractor = newCommonValueExtractor(commonVariantProperties)
+	extractCommonProperties(ctx, extractor, &commonVariantProperties, variantPropertiesContainers)
+	if commonVariantProperties.Compile_multilib != "" && commonVariantProperties.Compile_multilib != "both" {
+		// Compile_multilib defaults to both so only needs to be set when it's
+		// specified and not both.
+		snapshotModule.AddProperty("compile_multilib", commonVariantProperties.Compile_multilib)
+	}
+
 	targetPropertySet := snapshotModule.AddPropertySet("target")
+
+	// If host is supported and any member is host OS dependent then disable host
+	// by default, so that we can enable each host OS variant explicitly. This
+	// avoids problems with implicitly enabled OS variants when the snapshot is
+	// used, which might be different from this run (e.g. different build OS).
+	hasHostOsDependentMember := false
+	if s.HostSupported() {
+		for _, memberRef := range memberRefs {
+			if memberRef.memberType.IsHostOsDependent() {
+				hasHostOsDependentMember = true
+				break
+			}
+		}
+		if hasHostOsDependentMember {
+			hostPropertySet := targetPropertySet.AddPropertySet("host")
+			hostPropertySet.AddProperty("enabled", false)
+		}
+	}
+
+	// Iterate over the os types in a fixed order.
 	for _, osType := range s.getPossibleOsTypes() {
 		if sdkVariant, ok := osTypeToMemberProperties[osType]; ok {
 			osPropertySet := targetPropertySet.AddPropertySet(sdkVariant.Target().Os.Name)
 
-			// Compile_multilib defaults to both and must always be set to both on the
-			// device and so only needs to be set when targeted at the host and is neither
-			// unspecified or both.
-			multilib := sdkVariant.multilibUsages
-			if (osType.Class == android.Host || osType.Class == android.HostCross) &&
-				multilib != multilibNone && multilib != multilibBoth {
-				osPropertySet.AddProperty("compile_multilib", multilib.String())
+			// Enable the variant explicitly when we've disabled it by default on host.
+			if hasHostOsDependentMember &&
+				(osType.Class == android.Host || osType.Class == android.HostCross) {
+				osPropertySet.AddProperty("enabled", true)
+			}
+
+			variantProps := variantToProperties[sdkVariant]
+			if variantProps.Compile_multilib != "" && variantProps.Compile_multilib != "both" {
+				osPropertySet.AddProperty("compile_multilib", variantProps.Compile_multilib)
 			}
 
 			s.addMemberPropertiesToPropertySet(builder, osPropertySet, sdkVariant.dynamicMemberTypeListProperties)
@@ -975,9 +1018,12 @@
 	var osPropertySet android.BpPropertySet
 	var archPropertySet android.BpPropertySet
 	var archOsPrefix string
-	if osInfo.Properties.Base().Os_count == 1 {
-		// There is only one os type present in the variants so don't bother
-		// with adding target specific properties.
+	if osInfo.Properties.Base().Os_count == 1 &&
+		(osInfo.osType.Class == android.Device || !ctx.memberType.IsHostOsDependent()) {
+		// There is only one OS type present in the variants and it shouldn't have a
+		// variant-specific target. The latter is the case if it's either for device
+		// where there is only one OS (android), or for host and the member type
+		// isn't host OS dependent.
 
 		// Create a structure that looks like:
 		// module_type {
@@ -1014,6 +1060,12 @@
 		osPropertySet = targetPropertySet.AddPropertySet(osType.Name)
 		archPropertySet = targetPropertySet
 
+		// Enable the variant explicitly when we've disabled it by default on host.
+		if ctx.memberType.IsHostOsDependent() &&
+			(osType.Class == android.Host || osType.Class == android.HostCross) {
+			osPropertySet.AddProperty("enabled", true)
+		}
+
 		// Arch specific properties need to be added to an os and arch specific
 		// section prefixed with <os>_.
 		archOsPrefix = osType.Name + "_"
@@ -1184,7 +1236,7 @@
 	return m.name
 }
 
-func (s *sdk) createMemberSnapshot(ctx *memberContext, member *sdkMember, bpModule android.BpModule) {
+func (s *sdk) createMemberSnapshot(ctx *memberContext, member *sdkMember, bpModule *bpModule) {
 
 	memberType := member.memberType
 
@@ -1238,6 +1290,18 @@
 	// added.
 	targetPropertySet := bpModule.AddPropertySet("target")
 
+	// If the member is host OS dependent and has host_supported then disable by
+	// default and enable each host OS variant explicitly. This avoids problems
+	// with implicitly enabled OS variants when the snapshot is used, which might
+	// be different from this run (e.g. different build OS).
+	if ctx.memberType.IsHostOsDependent() {
+		hostSupported := bpModule.getValue("host_supported") == true // Missing means false.
+		if hostSupported {
+			hostPropertySet := targetPropertySet.AddPropertySet("host")
+			hostPropertySet.AddProperty("enabled", false)
+		}
+	}
+
 	// Iterate over the os types in a fixed order.
 	for _, osType := range s.getPossibleOsTypes() {
 		osInfo := osTypeToInfo[osType]
diff --git a/sysprop/sysprop_test.go b/sysprop/sysprop_test.go
index 8503386..711129c 100644
--- a/sysprop/sysprop_test.go
+++ b/sysprop/sysprop_test.go
@@ -67,6 +67,8 @@
 		ctx.BottomUp("sysprop_deps", syspropDepsMutator).Parallel()
 	})
 
+	android.RegisterPrebuiltMutators(ctx)
+
 	cc.RegisterRequiredBuildComponentsForTest(ctx)
 	ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
 		ctx.BottomUp("sysprop_java", java.SyspropMutator).Parallel()
diff --git a/third_party/zip/android.go b/third_party/zip/android.go
index 8d387cc..f8e45c5 100644
--- a/third_party/zip/android.go
+++ b/third_party/zip/android.go
@@ -43,6 +43,15 @@
 		offset:     uint64(w.cw.count),
 	}
 	w.dir = append(w.dir, h)
+	if !fh.isZip64() {
+		// Some writers will generate 64 bit sizes and set 32 bit fields to
+		// uint32max even if the actual size fits in 32 bit. So we should
+		// make sure CompressedSize contains the correct value in such
+		// cases. With out the two lines below we would be writing invalid(-1)
+		// sizes in such case.
+		fh.CompressedSize = uint32(fh.CompressedSize64)
+		fh.UncompressedSize = uint32(fh.UncompressedSize64)
+	}
 
 	if err := writeHeader(w.cw, fh); err != nil {
 		return err
diff --git a/ui/build/config.go b/ui/build/config.go
index 3fa0479..b263a98 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -53,8 +53,6 @@
 	// Autodetected
 	totalRAM uint64
 
-	pdkBuild bool
-
 	brokenDupRules     bool
 	brokenUsesNetwork  bool
 	brokenNinjaEnvVars []string
@@ -968,14 +966,6 @@
 	return c.targetDeviceDir
 }
 
-func (c *configImpl) SetPdkBuild(pdk bool) {
-	c.pdkBuild = pdk
-}
-
-func (c *configImpl) IsPdkBuild() bool {
-	return c.pdkBuild
-}
-
 func (c *configImpl) BuildDateTime() string {
 	return c.buildDateTime
 }
diff --git a/ui/build/dumpvars.go b/ui/build/dumpvars.go
index e229856..999af07 100644
--- a/ui/build/dumpvars.go
+++ b/ui/build/dumpvars.go
@@ -161,8 +161,6 @@
 	"BUILD_ID",
 	"OUT_DIR",
 	"AUX_OS_VARIANT_LIST",
-	"TARGET_BUILD_PDK",
-	"PDK_FUSION_PLATFORM_ZIP",
 	"PRODUCT_SOONG_NAMESPACES",
 }
 
@@ -281,7 +279,6 @@
 	config.SetTargetDevice(make_vars["TARGET_DEVICE"])
 	config.SetTargetDeviceDir(make_vars["TARGET_DEVICE_DIR"])
 
-	config.SetPdkBuild(make_vars["TARGET_BUILD_PDK"] == "true")
 	config.SetBuildBrokenDupRules(make_vars["BUILD_BROKEN_DUP_RULES"] == "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 1cd5fea..f6d3a57 100644
--- a/ui/build/kati.go
+++ b/ui/build/kati.go
@@ -134,14 +134,10 @@
 
 	args := []string{
 		"--writable", config.OutDir() + "/",
+		"--werror_implicit_rules",
 		"-f", "build/make/core/main.mk",
 	}
 
-	// PDK builds still uses a few implicit rules
-	if !config.IsPdkBuild() {
-		args = append(args, "--werror_implicit_rules")
-	}
-
 	if !config.BuildBrokenDupRules() {
 		args = append(args, "--werror_overriding_commands")
 	}
diff --git a/ui/build/paths/config.go b/ui/build/paths/config.go
index 5717401..81c500d 100644
--- a/ui/build/paths/config.go
+++ b/ui/build/paths/config.go
@@ -88,7 +88,6 @@
 	"javap":   Allowed,
 	"lsof":    Allowed,
 	"openssl": Allowed,
-	"patch":   Allowed,
 	"pstree":  Allowed,
 	"rsync":   Allowed,
 	"sh":      Allowed,
diff --git a/ui/build/rbe_test.go b/ui/build/rbe_test.go
index 2c4995b..23a53b4 100644
--- a/ui/build/rbe_test.go
+++ b/ui/build/rbe_test.go
@@ -94,12 +94,12 @@
 	}, {
 		description:         "stopRBE failed",
 		rbeOutputDirDefined: true,
-		bootstrapProgram:    "#!/bin/bash\nexit 1",
+		bootstrapProgram:    "#!/bin/bash\nexit 1\n",
 		expectedErr:         "shutdown failed",
 	}, {
 		description:         "failed to copy metrics file",
 		rbeOutputDirDefined: true,
-		bootstrapProgram:    "#!/bin/bash",
+		bootstrapProgram:    "#!/bin/bash\n",
 		expectedErr:         "failed to copy",
 	}}
 
@@ -139,4 +139,4 @@
 	}
 }
 
-var rbeBootstrapProgram = fmt.Sprintf("#!/bin/bash\necho 1 > $RBE_output_dir/%s", rbeMetricsPBFilename)
+var rbeBootstrapProgram = fmt.Sprintf("#!/bin/bash\necho 1 > $RBE_output_dir/%s\n", rbeMetricsPBFilename)
diff --git a/ui/metrics/metrics.go b/ui/metrics/metrics.go
index 2b5c4c3..12f74dd 100644
--- a/ui/metrics/metrics.go
+++ b/ui/metrics/metrics.go
@@ -17,6 +17,7 @@
 import (
 	"io/ioutil"
 	"os"
+	"runtime"
 	"time"
 
 	"github.com/golang/protobuf/proto"
@@ -98,8 +99,6 @@
 			m.metrics.HostArch = m.getArch(v)
 		case "HOST_2ND_ARCH":
 			m.metrics.Host_2NdArch = m.getArch(v)
-		case "HOST_OS":
-			m.metrics.HostOs = proto.String(v)
 		case "HOST_OS_EXTRA":
 			m.metrics.HostOsExtra = proto.String(v)
 		case "HOST_CROSS_OS":
@@ -137,6 +136,7 @@
 
 // exports the output to the file at outputPath
 func (m *Metrics) Dump(outputPath string) (err error) {
+	m.metrics.HostOs = proto.String(runtime.GOOS)
 	return writeMessageToFile(&m.metrics, outputPath)
 }