Merge "Merge libs/ directory of imported aars into classes.jar"
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/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..10a78a5 100644
--- a/android/config.go
+++ b/android/config.go
@@ -913,34 +913,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 +1247,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 8605954..2dc2ac7 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
@@ -1046,6 +1047,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
 }
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..8799d58 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -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 587092a..d13ec5f 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -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 d099b04..6f1a06d 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -418,7 +418,7 @@
 	inSanitizerDir() bool
 	hostToolPath() android.OptionalPath
 	relativeInstallPath() string
-	skipInstall(mod *Module)
+	makeUninstallable(mod *Module)
 }
 
 type xref interface {
@@ -1027,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()
@@ -1176,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
@@ -1281,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") {
@@ -2798,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 {
@@ -3143,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/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 2a329ac..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
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/sanitize.go b/cc/sanitize.go
index 33e4a4a..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) {
@@ -724,27 +742,64 @@
 	}
 }
 
+// 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.
@@ -1050,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 {
@@ -1084,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 {
@@ -1145,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/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/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/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 2224a9c..74b2777 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/rust.go b/rust/rust.go
index edfa5d8..113b0b2 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()...)
 			}
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/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)
 }