Merge "Export Rust SourceProvider types and methods"
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 cd84f8a..8c06b63 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -296,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 3387706..a6284f0 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,8 +733,9 @@
 	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 {
@@ -913,34 +914,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
@@ -1275,3 +1248,185 @@
 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 a12cd9b..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
@@ -568,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}
 }
@@ -1046,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
 }
@@ -1839,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.
@@ -1850,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/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/variable.go b/android/variable.go
index c1e1b42..9fb8577 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -214,30 +214,30 @@
 
 	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"`
+	Pdk                          *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"`
 
@@ -345,7 +345,8 @@
 
 	BoardUsesRecoveryAsBoot *bool `json:",omitempty"`
 
-	BoardKernelBinaries []string `json:",omitempty"`
+	BoardKernelBinaries                []string `json:",omitempty"`
+	BoardKernelModuleInterfaceVersions []string `json:",omitempty"`
 }
 
 func boolPtr(v bool) *bool {
diff --git a/apex/apex_test.go b/apex/apex_test.go
index a6f3805..d13ec5f 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -4450,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",
@@ -5602,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)
 	})
@@ -5616,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)
 	})
@@ -5624,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)
 	})
@@ -5632,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)
 	})
@@ -5640,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)
 	})
@@ -5655,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)
 	})
@@ -5663,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)
 	})
@@ -5671,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/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/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/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..de3e64b 100644
--- a/cc/rs.go
+++ b/cc/rs.go
@@ -25,7 +25,7 @@
 
 func init() {
 	pctx.VariableFunc("rsCmd", func(ctx android.PackageVarContext) string {
-		if ctx.Config().UnbundledBuild() {
+		if ctx.Config().AlwaysUsePrebuiltSdks() {
 			// Use RenderScript prebuilts for unbundled builds but not PDK builds
 			return filepath.Join("prebuilts/sdk/tools", runtime.GOOS, "bin/llvm-rs-cc")
 		} else {
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 300bc8f..6db6348 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.
@@ -281,6 +282,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])
 		}
@@ -311,14 +321,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).
@@ -333,14 +343,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
@@ -349,14 +359,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.
@@ -401,7 +412,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)
 	}
 }
 
@@ -458,6 +469,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)
@@ -477,6 +492,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) {
@@ -714,31 +732,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.
@@ -957,10 +1018,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() {
@@ -971,10 +1033,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
@@ -1042,15 +1105,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 {
@@ -1076,38 +1133,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 {
@@ -1137,17 +1252,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 c2353d8..4113fd2 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -567,6 +567,7 @@
 	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)
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index 6df940c..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()
 }
@@ -568,13 +548,17 @@
 	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)
@@ -668,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"`
 
@@ -717,6 +702,7 @@
 		var propOut string
 
 		if l, ok := m.linker.(snapshotLibraryInterface); ok {
+
 			// library flags
 			prop.ExportedFlags = l.exportedFlags()
 			for _, dir := range l.exportedDirs() {
@@ -749,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/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 650d126..bc327cf 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -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{
diff --git a/java/app.go b/java/app.go
index fcb2e99..8a0b3db 100755
--- a/java/app.go
+++ b/java/app.go
@@ -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
diff --git a/java/app_test.go b/java/app_test.go
index efb4fd2..f50aa3a 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -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..d0296ff 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() || ctx.Config().IsPdkBuild() {
 			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() || ctx.Config().IsPdkBuild() {
 			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() || ctx.Config().IsPdkBuild() {
 			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() || ctx.Config().IsPdkBuild() {
 			return filepath.Join("prebuilts/build-tools", ctx.Config().PrebuiltOS(), "bin", tool)
 		} else {
 			return ctx.Config().HostToolPath(ctx, tool).String()
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index 4120559..3abe782 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")
@@ -608,7 +608,7 @@
 
 	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/hiddenapi_singleton.go b/java/hiddenapi_singleton.go
index 1e6becb..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")
 	}
 
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..fa4c053 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
 	}
@@ -212,7 +212,7 @@
 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 {
@@ -511,7 +511,7 @@
 type sdkSingleton struct{}
 
 func (sdkSingleton) GenerateBuildActions(ctx android.SingletonContext) {
-	if ctx.Config().UnbundledBuildUsePrebuiltSdks() || ctx.Config().IsPdkBuild() {
+	if ctx.Config().AlwaysUsePrebuiltSdks() || ctx.Config().IsPdkBuild() {
 		return
 	}
 
@@ -634,7 +634,7 @@
 	} 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 +663,7 @@
 }
 
 func sdkMakeVars(ctx android.MakeVarsContext) {
-	if ctx.Config().UnbundledBuildUsePrebuiltSdks() || ctx.Config().IsPdkBuild() {
+	if ctx.Config().AlwaysUsePrebuiltSdks() || ctx.Config().IsPdkBuild() {
 		return
 	}
 
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 0379a31..0ed8394 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -1799,7 +1799,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..13fc915 100644
--- a/java/sdk_test.go
+++ b/java/sdk_test.go
@@ -384,6 +384,7 @@
 				config := testConfig(nil, bpJava8, nil)
 				if testcase.unbundled {
 					config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true)
+					config.TestProductVariables.Always_use_prebuilt_sdks = proptools.BoolPtr(true)
 				}
 				if testcase.pdk {
 					config.TestProductVariables.Pdk = proptools.BoolPtr(true)
@@ -407,6 +408,7 @@
 				config := testConfig(nil, bp, nil)
 				if testcase.unbundled {
 					config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true)
+					config.TestProductVariables.Always_use_prebuilt_sdks = proptools.BoolPtr(true)
 				}
 				if testcase.pdk {
 					config.TestProductVariables.Pdk = proptools.BoolPtr(true)
@@ -433,6 +435,7 @@
 
 				if testcase.unbundled {
 					config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true)
+					config.TestProductVariables.Always_use_prebuilt_sdks = proptools.BoolPtr(true)
 				}
 				if testcase.pdk {
 					config.TestProductVariables.Pdk = proptools.BoolPtr(true)
@@ -451,6 +454,7 @@
 
 				if testcase.unbundled {
 					config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true)
+					config.TestProductVariables.Always_use_prebuilt_sdks = proptools.BoolPtr(true)
 				}
 				if testcase.pdk {
 					config.TestProductVariables.Pdk = proptools.BoolPtr(true)
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/bindgen.go b/rust/bindgen.go
index 8442907..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"
@@ -119,15 +120,17 @@
 		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.Bindgen_flags, " "))
+	bindgenFlags = append(bindgenFlags, esc(b.Properties.Bindgen_flags)...)
 
 	wrapperFile := android.OptionalPathForModuleSrc(ctx, b.Properties.Wrapper_src)
 	if !wrapperFile.Valid() {
diff --git a/rust/bindgen_test.go b/rust/bindgen_test.go
index 0b529ca..191da9b 100644
--- a/rust/bindgen_test.go
+++ b/rust/bindgen_test.go
@@ -27,8 +27,8 @@
 			crate_name: "bindgen",
 			stem: "libbindgen",
 			source_stem: "bindings",
-			bindgen_flags: ["--bindgen-flag"],
-			cflags: ["--clang-flag"],
+			bindgen_flags: ["--bindgen-flag.*"],
+			cflags: ["--clang-flag()"],
 			shared_libs: ["libfoo_shared"],
 			static_libs: ["libfoo_static"],
 		}
@@ -42,10 +42,11 @@
 		}
 	`)
 	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") {
diff --git a/rust/compiler.go b/rust/compiler.go
index 0274015..c39a4a1 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -159,7 +159,9 @@
 
 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...)
diff --git a/rust/config/allowed_list.go b/rust/config/allowed_list.go
index 9c9a136..22a0e63 100644
--- a/rust/config/allowed_list.go
+++ b/rust/config/allowed_list.go
@@ -7,6 +7,7 @@
 		"external/crosvm",
 		"external/adhd",
 		"prebuilts/rust",
+		"system/extras/profcollectd",
 		"system/security",
 	}
 
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/rust.go b/rust/rust.go
index e56a6ae..17fd042 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -807,8 +807,8 @@
 				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()...)
 			}
@@ -848,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)
@@ -861,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)
@@ -873,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
 			}
 
@@ -998,10 +998,10 @@
 
 	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)
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 83b2828..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
 
diff --git a/scripts/build-mainline-modules.sh b/scripts/build-mainline-modules.sh
index c7baca7..c5ec8d1 100755
--- a/scripts/build-mainline-modules.sh
+++ b/scripts/build-mainline-modules.sh
@@ -24,6 +24,7 @@
   i18n-module-test-exports
   i18n-module-sdk
   platform-mainline-sdk
+  platform-mainline-test-exports
 )
 
 # List of libraries installed on the platform that are needed for ART chroot
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/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/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)
 }