Merge "Don't create SDK variants for native bridge modules"
diff --git a/android/Android.bp b/android/Android.bp
index 69aa037..eabb137 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -21,6 +21,7 @@
         "bazel_handler.go",
         "config.go",
         "csuite_config.go",
+        "deapexer.go",
         "defaults.go",
         "defs.go",
         "depset_generic.go",
@@ -59,6 +60,7 @@
         "sandbox.go",
         "sdk.go",
         "singleton.go",
+        "singleton_module.go",
         "soong_config_modules.go",
         "test_suites.go",
         "testing.go",
@@ -95,6 +97,7 @@
         "paths_test.go",
         "prebuilt_test.go",
         "rule_builder_test.go",
+        "singleton_module_test.go",
         "soong_config_modules_test.go",
         "util_test.go",
         "variable_test.go",
diff --git a/android/apex.go b/android/apex.go
index 47d14cc..31c62e9 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -64,6 +64,14 @@
 	// module is part of. The ApexContents gives information about which modules the apexBundle
 	// has and whether a module became part of the apexBundle via a direct dependency or not.
 	ApexContents []*ApexContents
+
+	// True if this is for a prebuilt_apex.
+	//
+	// If true then this will customize the apex processing to make it suitable for handling
+	// prebuilt_apex, e.g. it will prevent ApexInfos from being merged together.
+	//
+	// See Prebuilt.ApexInfoMutator for more information.
+	ForPrebuiltApex bool
 }
 
 var ApexInfoProvider = blueprint.NewMutatorProvider(ApexInfo{}, "apex")
@@ -412,6 +420,16 @@
 	sort.Sort(byApexName(apexInfos))
 	seen := make(map[string]int)
 	for _, apexInfo := range apexInfos {
+		// If this is for a prebuilt apex then use the actual name of the apex variation to prevent this
+		// from being merged with other ApexInfo. See Prebuilt.ApexInfoMutator for more information.
+		if apexInfo.ForPrebuiltApex {
+			merged = append(merged, apexInfo)
+			continue
+		}
+
+		// Merge the ApexInfo together. If a compatible ApexInfo exists then merge the information from
+		// this one into it, otherwise create a new merged ApexInfo from this one and save it away so
+		// other ApexInfo instances can be merged into it.
 		apexName := apexInfo.ApexVariationName
 		mergedName := apexInfo.mergedName(ctx)
 		if index, exists := seen[mergedName]; exists {
@@ -582,10 +600,14 @@
 // apexContents, and modules in that apex have a provider containing the apexContents of each
 // apexBundle they are part of.
 type ApexContents struct {
-	// map from a module name to its membership to this apexBUndle
+	// map from a module name to its membership in this apexBundle
 	contents map[string]ApexMembership
 }
 
+// NewApexContents creates and initializes an ApexContents that is suitable
+// for use with an apex module.
+// * contents is a map from a module name to information about its membership within
+//   the apex.
 func NewApexContents(contents map[string]ApexMembership) *ApexContents {
 	return &ApexContents{
 		contents: contents,
diff --git a/android/apex_test.go b/android/apex_test.go
index 512b50f..1f786e6 100644
--- a/android/apex_test.go
+++ b/android/apex_test.go
@@ -20,6 +20,10 @@
 )
 
 func Test_mergeApexVariations(t *testing.T) {
+	const (
+		ForPrebuiltApex    = true
+		NotForPrebuiltApex = false
+	)
 	tests := []struct {
 		name        string
 		in          []ApexInfo
@@ -29,10 +33,10 @@
 		{
 			name: "single",
 			in: []ApexInfo{
-				{"foo", "current", false, nil, []string{"foo"}, nil},
+				{"foo", "current", false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
 			},
 			wantMerged: []ApexInfo{
-				{"apex10000", "current", false, nil, []string{"foo"}, nil},
+				{"apex10000", "current", false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
 			},
 			wantAliases: [][2]string{
 				{"foo", "apex10000"},
@@ -41,11 +45,11 @@
 		{
 			name: "merge",
 			in: []ApexInfo{
-				{"foo", "current", false, SdkRefs{{"baz", "1"}}, []string{"foo"}, nil},
-				{"bar", "current", false, SdkRefs{{"baz", "1"}}, []string{"bar"}, nil},
+				{"foo", "current", false, SdkRefs{{"baz", "1"}}, []string{"foo"}, nil, NotForPrebuiltApex},
+				{"bar", "current", false, SdkRefs{{"baz", "1"}}, []string{"bar"}, nil, NotForPrebuiltApex},
 			},
 			wantMerged: []ApexInfo{
-				{"apex10000_baz_1", "current", false, SdkRefs{{"baz", "1"}}, []string{"bar", "foo"}, nil}},
+				{"apex10000_baz_1", "current", false, SdkRefs{{"baz", "1"}}, []string{"bar", "foo"}, nil, false}},
 			wantAliases: [][2]string{
 				{"bar", "apex10000_baz_1"},
 				{"foo", "apex10000_baz_1"},
@@ -54,12 +58,12 @@
 		{
 			name: "don't merge version",
 			in: []ApexInfo{
-				{"foo", "current", false, nil, []string{"foo"}, nil},
-				{"bar", "30", false, nil, []string{"bar"}, nil},
+				{"foo", "current", false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
+				{"bar", "30", false, nil, []string{"bar"}, nil, NotForPrebuiltApex},
 			},
 			wantMerged: []ApexInfo{
-				{"apex30", "30", false, nil, []string{"bar"}, nil},
-				{"apex10000", "current", false, nil, []string{"foo"}, nil},
+				{"apex30", "30", false, nil, []string{"bar"}, nil, NotForPrebuiltApex},
+				{"apex10000", "current", false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
 			},
 			wantAliases: [][2]string{
 				{"bar", "apex30"},
@@ -69,11 +73,11 @@
 		{
 			name: "merge updatable",
 			in: []ApexInfo{
-				{"foo", "current", false, nil, []string{"foo"}, nil},
-				{"bar", "current", true, nil, []string{"bar"}, nil},
+				{"foo", "current", false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
+				{"bar", "current", true, nil, []string{"bar"}, nil, NotForPrebuiltApex},
 			},
 			wantMerged: []ApexInfo{
-				{"apex10000", "current", true, nil, []string{"bar", "foo"}, nil},
+				{"apex10000", "current", true, nil, []string{"bar", "foo"}, nil, NotForPrebuiltApex},
 			},
 			wantAliases: [][2]string{
 				{"bar", "apex10000"},
@@ -83,19 +87,38 @@
 		{
 			name: "don't merge sdks",
 			in: []ApexInfo{
-				{"foo", "current", false, SdkRefs{{"baz", "1"}}, []string{"foo"}, nil},
-				{"bar", "current", false, SdkRefs{{"baz", "2"}}, []string{"bar"}, nil},
+				{"foo", "current", false, SdkRefs{{"baz", "1"}}, []string{"foo"}, nil, NotForPrebuiltApex},
+				{"bar", "current", false, SdkRefs{{"baz", "2"}}, []string{"bar"}, nil, NotForPrebuiltApex},
 			},
 			wantMerged: []ApexInfo{
-				{"apex10000_baz_2", "current", false, SdkRefs{{"baz", "2"}}, []string{"bar"}, nil},
-				{"apex10000_baz_1", "current", false, SdkRefs{{"baz", "1"}}, []string{"foo"}, nil},
+				{"apex10000_baz_2", "current", false, SdkRefs{{"baz", "2"}}, []string{"bar"}, nil, NotForPrebuiltApex},
+				{"apex10000_baz_1", "current", false, SdkRefs{{"baz", "1"}}, []string{"foo"}, nil, NotForPrebuiltApex},
 			},
 			wantAliases: [][2]string{
 				{"bar", "apex10000_baz_2"},
 				{"foo", "apex10000_baz_1"},
 			},
 		},
+		{
+			name: "don't merge when for prebuilt_apex",
+			in: []ApexInfo{
+				{"foo", "current", false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
+				{"bar", "current", true, nil, []string{"bar"}, nil, NotForPrebuiltApex},
+				// This one should not be merged in with the others because it is for
+				// a prebuilt_apex.
+				{"baz", "current", true, nil, []string{"baz"}, nil, ForPrebuiltApex},
+			},
+			wantMerged: []ApexInfo{
+				{"apex10000", "current", true, nil, []string{"bar", "foo"}, nil, NotForPrebuiltApex},
+				{"baz", "current", true, nil, []string{"baz"}, nil, ForPrebuiltApex},
+			},
+			wantAliases: [][2]string{
+				{"bar", "apex10000"},
+				{"foo", "apex10000"},
+			},
+		},
 	}
+
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
 			config := TestConfig(buildDir, nil, "", nil)
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index a00a54d..4a25119 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -371,7 +371,11 @@
 	var cqueryOutput string
 	var err error
 
-	err = os.Mkdir(absolutePath(context.intermediatesDir()), 0777)
+	intermediatesDirPath := absolutePath(context.intermediatesDir())
+	if _, err := os.Stat(intermediatesDirPath); os.IsNotExist(err) {
+		err = os.Mkdir(intermediatesDirPath, 0777)
+	}
+
 	if err != nil {
 		return err
 	}
@@ -441,7 +445,10 @@
 		return err
 	}
 
-	context.buildStatements = bazel.AqueryBuildStatements([]byte(aqueryOutput))
+	context.buildStatements, err = bazel.AqueryBuildStatements([]byte(aqueryOutput))
+	if err != nil {
+		return err
+	}
 
 	// Issue a build command of the phony root to generate symlink forests for dependencies of the
 	// Bazel build. This is necessary because aquery invocations do not generate this symlink forest,
diff --git a/android/config.go b/android/config.go
index ddb2de3..8090889 100644
--- a/android/config.go
+++ b/android/config.go
@@ -1026,6 +1026,10 @@
 	return c.multilibConflicts[arch]
 }
 
+func (c *config) PrebuiltHiddenApiDir(ctx PathContext) string {
+	return String(c.productVariables.PrebuiltHiddenApiDir)
+}
+
 func (c *deviceConfig) Arches() []Arch {
 	var arches []Arch
 	for _, target := range c.config.Targets[Android] {
@@ -1256,6 +1260,27 @@
 	return HasAnyPrefix(path, c.productVariables.CFIIncludePaths)
 }
 
+func (c *config) MemtagHeapDisabledForPath(path string) bool {
+	if len(c.productVariables.MemtagHeapExcludePaths) == 0 {
+		return false
+	}
+	return HasAnyPrefix(path, c.productVariables.MemtagHeapExcludePaths)
+}
+
+func (c *config) MemtagHeapAsyncEnabledForPath(path string) bool {
+	if len(c.productVariables.MemtagHeapAsyncIncludePaths) == 0 {
+		return false
+	}
+	return HasAnyPrefix(path, c.productVariables.MemtagHeapAsyncIncludePaths)
+}
+
+func (c *config) MemtagHeapSyncEnabledForPath(path string) bool {
+	if len(c.productVariables.MemtagHeapSyncIncludePaths) == 0 {
+		return false
+	}
+	return HasAnyPrefix(path, c.productVariables.MemtagHeapSyncIncludePaths)
+}
+
 func (c *config) VendorConfig(name string) VendorConfig {
 	return soongconfig.Config(c.productVariables.VendorVars[name])
 }
@@ -1380,6 +1405,14 @@
 	return c.config.productVariables.BoardReqdMaskPolicy
 }
 
+func (c *deviceConfig) DirectedVendorSnapshot() bool {
+	return c.config.productVariables.DirectedVendorSnapshot
+}
+
+func (c *deviceConfig) VendorSnapshotModules() map[string]bool {
+	return c.config.productVariables.VendorSnapshotModules
+}
+
 // 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
diff --git a/android/deapexer.go b/android/deapexer.go
new file mode 100644
index 0000000..63508d7
--- /dev/null
+++ b/android/deapexer.go
@@ -0,0 +1,83 @@
+// Copyright (C) 2021 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 android
+
+import (
+	"fmt"
+	"strings"
+
+	"github.com/google/blueprint"
+)
+
+// Provides support for interacting with the `deapexer` module to which a `prebuilt_apex` module
+// will delegate the work to export files from a prebuilt '.apex` file.
+
+// The information exported by the `deapexer` module, access it using `DeapxerInfoProvider`.
+type DeapexerInfo struct {
+	// map from the name of an exported file from a prebuilt_apex to the path to that file. The
+	// exported file name is of the form <module>{<tag>} where <tag> is currently only allowed to be
+	// ".dexjar".
+	//
+	// See Prebuilt.ApexInfoMutator for more information.
+	exports map[string]Path
+}
+
+// The set of supported prebuilt export tags. Used to verify the tag parameter for
+// `PrebuiltExportPath`.
+var supportedPrebuiltExportTags = map[string]struct{}{
+	".dexjar": {},
+}
+
+// PrebuiltExportPath provides the path, or nil if not available, of a file exported from the
+// prebuilt_apex that created this ApexInfo.
+//
+// The exported file is identified by the module name and the tag:
+// * The module name is the name of the module that contributed the file when the .apex file
+//   referenced by the prebuilt_apex was built. It must be specified in one of the exported_...
+//   properties on the prebuilt_apex module.
+// * The tag identifies the type of file and is dependent on the module type.
+//
+// See apex/deapexer.go for more information.
+func (i DeapexerInfo) PrebuiltExportPath(name, tag string) Path {
+
+	if _, ok := supportedPrebuiltExportTags[tag]; !ok {
+		panic(fmt.Errorf("unsupported prebuilt export tag %q, expected one of %s",
+			tag, strings.Join(SortedStringKeys(supportedPrebuiltExportTags), ", ")))
+	}
+
+	path := i.exports[name+"{"+tag+"}"]
+	return path
+}
+
+// Provider that can be used from within the `GenerateAndroidBuildActions` of a module that depends
+// on a `deapexer` module to retrieve its `DeapexerInfo`.
+var DeapexerProvider = blueprint.NewProvider(DeapexerInfo{})
+
+// NewDeapexerInfo creates and initializes a DeapexerInfo that is suitable
+// for use with a prebuilt_apex module.
+//
+// See apex/deapexer.go for more information.
+func NewDeapexerInfo(exports map[string]Path) DeapexerInfo {
+	return DeapexerInfo{
+		exports: exports,
+	}
+}
+
+type deapexerTagStruct struct {
+	blueprint.BaseDependencyTag
+}
+
+// A tag that is used for dependencies on the `deapexer` module.
+var DeapexerTag = deapexerTagStruct{}
diff --git a/android/makevars.go b/android/makevars.go
index 546abcf..40c0ccd 100644
--- a/android/makevars.go
+++ b/android/makevars.go
@@ -134,8 +134,6 @@
 
 // SingletonMakeVarsProvider is a Singleton with an extra method to provide extra values to be exported to Make.
 type SingletonMakeVarsProvider interface {
-	Singleton
-
 	// MakeVars uses a MakeVarsContext to provide extra values to be exported to Make.
 	MakeVars(ctx MakeVarsContext)
 }
diff --git a/android/module.go b/android/module.go
index b2a04b6..d189d1a 100644
--- a/android/module.go
+++ b/android/module.go
@@ -454,6 +454,7 @@
 	InstallForceOS() (*OsType, *ArchType)
 	HideFromMake()
 	IsHideFromMake() bool
+	IsSkipInstall() bool
 	MakeUninstallable()
 	ReplacedByPrebuilt()
 	IsReplacedByPrebuilt() bool
@@ -1398,6 +1399,12 @@
 	m.commonProperties.SkipInstall = true
 }
 
+// IsSkipInstall returns true if this variant is marked to not create install
+// rules when ctx.Install* are called.
+func (m *ModuleBase) IsSkipInstall() bool {
+	return m.commonProperties.SkipInstall
+}
+
 // Similar to HideFromMake, 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
diff --git a/android/paths.go b/android/paths.go
index 10d8d0d..592b9e1 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -1742,6 +1742,19 @@
 	return ioutil.WriteFile(absolutePath(path.String()), data, perm)
 }
 
+func RemoveAllOutputDir(path WritablePath) error {
+	return os.RemoveAll(absolutePath(path.String()))
+}
+
+func CreateOutputDirIfNonexistent(path WritablePath, perm os.FileMode) error {
+	dir := absolutePath(path.String())
+	if _, err := os.Stat(dir); os.IsNotExist(err) {
+		return os.MkdirAll(dir, os.ModePerm)
+	} else {
+		return err
+	}
+}
+
 func absolutePath(path string) string {
 	if filepath.IsAbs(path) {
 		return path
diff --git a/android/queryview.go b/android/queryview.go
index 1b7e77d..9e3e45a 100644
--- a/android/queryview.go
+++ b/android/queryview.go
@@ -26,7 +26,6 @@
 // for calling the soong_build primary builder in the main build.ninja file.
 func init() {
 	RegisterSingletonType("bazel_queryview", BazelQueryViewSingleton)
-	RegisterSingletonType("bazel_converter", BazelConverterSingleton)
 }
 
 // BazelQueryViewSingleton is the singleton responsible for registering the
@@ -52,13 +51,7 @@
 
 func generateBuildActionsForBazelConversion(ctx SingletonContext, converterMode bool) {
 	name := "queryview"
-	additionalEnvVars := ""
 	descriptionTemplate := "[EXPERIMENTAL, PRE-PRODUCTION] Creating the Bazel QueryView workspace with %s at $outDir"
-	if converterMode {
-		name = "bp2build"
-		additionalEnvVars = "CONVERT_TO_BAZEL=true"
-		descriptionTemplate = "[EXPERIMENTAL, PRE-PRODUCTION] Converting all Android.bp to Bazel BUILD files with %s at $outDir"
-	}
 
 	// Create a build and rule statement, using the Bazel QueryView's WORKSPACE
 	// file as the output file marker.
@@ -74,9 +67,8 @@
 		blueprint.RuleParams{
 			Command: fmt.Sprintf(
 				"rm -rf ${outDir}/* && "+
-					"%s %s --bazel_queryview_dir ${outDir} %s && "+
+					"%s --bazel_queryview_dir ${outDir} %s && "+
 					"echo WORKSPACE: `cat %s` > ${outDir}/.queryview-depfile.d",
-				additionalEnvVars,
 				primaryBuilder.String(),
 				strings.Join(os.Args[1:], " "),
 				moduleListFilePath.String(), // Use the contents of Android.bp.list as the depfile.
diff --git a/android/register.go b/android/register.go
index b26f9b9..f84acad 100644
--- a/android/register.go
+++ b/android/register.go
@@ -16,6 +16,7 @@
 
 import (
 	"fmt"
+	"reflect"
 
 	"github.com/google/blueprint"
 )
@@ -26,6 +27,7 @@
 }
 
 var moduleTypes []moduleType
+var moduleTypesForDocs = map[string]reflect.Value{}
 
 type singleton struct {
 	name    string
@@ -69,6 +71,16 @@
 
 func RegisterModuleType(name string, factory ModuleFactory) {
 	moduleTypes = append(moduleTypes, moduleType{name, factory})
+	RegisterModuleTypeForDocs(name, reflect.ValueOf(factory))
+}
+
+// RegisterModuleTypeForDocs associates a module type name with a reflect.Value of the factory
+// function that has documentation for the module type.  It is normally called automatically
+// by RegisterModuleType, but can be called manually after RegisterModuleType in order to
+// override the factory method used for documentation, for example if the method passed to
+// RegisterModuleType was a lambda.
+func RegisterModuleTypeForDocs(name string, factory reflect.Value) {
+	moduleTypesForDocs[name] = factory
 }
 
 func RegisterSingletonType(name string, factory SingletonFactory) {
@@ -98,13 +110,16 @@
 		ctx.RegisterModuleType(t.name, ModuleFactoryAdaptor(t.factory))
 	}
 
-	bazelConverterSingleton := singleton{"bp2build", BazelConverterSingleton}
-	ctx.RegisterSingletonType(bazelConverterSingleton.name,
-		SingletonFactoryAdaptor(ctx, bazelConverterSingleton.factory))
+	// Required for SingletonModule types, even though we are not using them.
+	for _, t := range singletons {
+		ctx.RegisterSingletonType(t.name, SingletonFactoryAdaptor(ctx, t.factory))
+	}
 
 	registerMutatorsForBazelConversion(ctx.Context)
 }
 
+// Register the pipeline of singletons, module types, and mutators for
+// generating build.ninja and other files for Kati, from Android.bp files.
 func (ctx *Context) Register() {
 	for _, t := range preSingletons {
 		ctx.RegisterPreSingletonType(t.name, SingletonFactoryAdaptor(ctx, t.factory))
@@ -142,12 +157,17 @@
 	return ret
 }
 
+func ModuleTypeFactoriesForDocs() map[string]reflect.Value {
+	return moduleTypesForDocs
+}
+
 // Interface for registering build components.
 //
 // Provided to allow registration of build components to be shared between the runtime
 // and test environments.
 type RegistrationContext interface {
 	RegisterModuleType(name string, factory ModuleFactory)
+	RegisterSingletonModuleType(name string, factory SingletonModuleFactory)
 	RegisterSingletonType(name string, factory SingletonFactory)
 	PreArchMutators(f RegisterMutatorFunc)
 
@@ -186,8 +206,9 @@
 var _ RegistrationContext = (*TestContext)(nil)
 
 type initRegistrationContext struct {
-	moduleTypes    map[string]ModuleFactory
-	singletonTypes map[string]SingletonFactory
+	moduleTypes        map[string]ModuleFactory
+	singletonTypes     map[string]SingletonFactory
+	moduleTypesForDocs map[string]reflect.Value
 }
 
 func (ctx *initRegistrationContext) RegisterModuleType(name string, factory ModuleFactory) {
@@ -196,6 +217,17 @@
 	}
 	ctx.moduleTypes[name] = factory
 	RegisterModuleType(name, factory)
+	RegisterModuleTypeForDocs(name, reflect.ValueOf(factory))
+}
+
+func (ctx *initRegistrationContext) RegisterSingletonModuleType(name string, factory SingletonModuleFactory) {
+	s, m := SingletonModuleFactoryAdaptor(name, factory)
+	ctx.RegisterSingletonType(name, s)
+	ctx.RegisterModuleType(name, m)
+	// Overwrite moduleTypesForDocs with the original factory instead of the lambda returned by
+	// SingletonModuleFactoryAdaptor so that docs can find the module type documentation on the
+	// factory method.
+	RegisterModuleTypeForDocs(name, reflect.ValueOf(factory))
 }
 
 func (ctx *initRegistrationContext) RegisterSingletonType(name string, factory SingletonFactory) {
diff --git a/android/sdk.go b/android/sdk.go
index f2cdc88..bab0ed8 100644
--- a/android/sdk.go
+++ b/android/sdk.go
@@ -177,6 +177,12 @@
 	// to the zip
 	CopyToSnapshot(src Path, dest string)
 
+	// Return the path to an empty file.
+	//
+	// This can be used by sdk member types that need to create an empty file in the snapshot, simply
+	// pass the value returned from this to the CopyToSnapshot() method.
+	EmptyFile() Path
+
 	// Unzip the supplied zip into the snapshot relative directory destDir.
 	UnzipToSnapshot(zipPath Path, destDir string)
 
diff --git a/android/singleton_module.go b/android/singleton_module.go
new file mode 100644
index 0000000..2351738
--- /dev/null
+++ b/android/singleton_module.go
@@ -0,0 +1,146 @@
+// 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"
+	"sync"
+
+	"github.com/google/blueprint"
+)
+
+// A SingletonModule is halfway between a Singleton and a Module.  It has access to visiting
+// other modules via its GenerateSingletonBuildActions method, but must be defined in an Android.bp
+// file and can also be depended on like a module.  It must be used zero or one times in an
+// Android.bp file, and it can only have a single variant.
+//
+// The SingletonModule's GenerateAndroidBuildActions method will be called before any normal or
+// singleton module that depends on it, but its GenerateSingletonBuildActions method will be called
+// after all modules, in registration order with other singletons and singleton modules.
+// GenerateAndroidBuildActions and GenerateSingletonBuildActions will not be called if the
+// SingletonModule was not instantiated in an Android.bp file.
+//
+// Since the SingletonModule rules likely depend on the modules visited during
+// GenerateSingletonBuildActions, the GenerateAndroidBuildActions is unlikely to produce any
+// rules directly.  Instead, it will probably set some providers to paths that will later have rules
+// generated to produce them in GenerateSingletonBuildActions.
+//
+// The expected use case for a SingletonModule is a module that produces files that depend on all
+// modules in the tree and will be used by other modules.  For example it could produce a text
+// file that lists all modules that meet a certain criteria, and that text file could be an input
+// to another module.  Care must be taken that the ninja rules produced by the SingletonModule
+// don't produce a cycle by referencing output files of rules of modules that depend on the
+// SingletonModule.
+//
+// A SingletonModule must embed a SingletonModuleBase struct, and its factory method must be
+// registered with RegisterSingletonModuleType from an init() function.
+//
+// A SingletonModule can also implement SingletonMakeVarsProvider to export values to Make.
+type SingletonModule interface {
+	Module
+	GenerateSingletonBuildActions(SingletonContext)
+	singletonModuleBase() *SingletonModuleBase
+}
+
+// SingletonModuleBase must be embedded into implementers of the SingletonModule interface.
+type SingletonModuleBase struct {
+	ModuleBase
+
+	lock    sync.Mutex
+	bp      string
+	variant string
+}
+
+// GenerateBuildActions wraps the ModuleBase GenerateBuildActions method, verifying it was only
+// called once to prevent multiple variants of a SingletonModule.
+func (smb *SingletonModuleBase) GenerateBuildActions(ctx blueprint.ModuleContext) {
+	smb.lock.Lock()
+	if smb.variant != "" {
+		ctx.ModuleErrorf("GenerateAndroidBuildActions already called for variant %q, SingletonModules can only  have one variant", smb.variant)
+	}
+	smb.variant = ctx.ModuleSubDir()
+	smb.lock.Unlock()
+
+	smb.ModuleBase.GenerateBuildActions(ctx)
+}
+
+// InitAndroidSingletonModule must be called from the SingletonModule's factory function to
+// initialize SingletonModuleBase.
+func InitAndroidSingletonModule(sm SingletonModule) {
+	InitAndroidModule(sm)
+}
+
+// singletonModuleBase retrieves the embedded SingletonModuleBase from a SingletonModule.
+func (smb *SingletonModuleBase) singletonModuleBase() *SingletonModuleBase { return smb }
+
+// SingletonModuleFactory is a factory method that returns a SingletonModule.
+type SingletonModuleFactory func() SingletonModule
+
+// SingletonModuleFactoryAdaptor converts a SingletonModuleFactory into a SingletonFactory and a
+// ModuleFactory.
+func SingletonModuleFactoryAdaptor(name string, factory SingletonModuleFactory) (SingletonFactory, ModuleFactory) {
+	// The sm variable acts as a static holder of the only SingletonModule instance.  Calls to the
+	// returned SingletonFactory and ModuleFactory lambdas will always return the same sm value.
+	// The SingletonFactory is only expected to be called once, but the ModuleFactory may be
+	// called multiple times if the module is replaced with a clone of itself at the end of
+	// blueprint.ResolveDependencies.
+	var sm SingletonModule
+	s := func() Singleton {
+		sm = factory()
+		return &singletonModuleSingletonAdaptor{sm}
+	}
+	m := func() Module {
+		if sm == nil {
+			panic(fmt.Errorf("Singleton %q for SingletonModule was not instantiated", name))
+		}
+
+		// Check for multiple uses of a SingletonModule in a LoadHook.  Checking directly in the
+		// factory would incorrectly flag when the factory was called again when the module is
+		// replaced with a clone of itself at the end of blueprint.ResolveDependencies.
+		AddLoadHook(sm, func(ctx LoadHookContext) {
+			smb := sm.singletonModuleBase()
+			smb.lock.Lock()
+			defer smb.lock.Unlock()
+			if smb.bp != "" {
+				ctx.ModuleErrorf("Duplicate SingletonModule %q, previously used in %s", name, smb.bp)
+			}
+			smb.bp = ctx.BlueprintsFile()
+		})
+		return sm
+	}
+	return s, m
+}
+
+// singletonModuleSingletonAdaptor makes a SingletonModule into a Singleton by translating the
+// GenerateSingletonBuildActions method to Singleton.GenerateBuildActions.
+type singletonModuleSingletonAdaptor struct {
+	sm SingletonModule
+}
+
+// GenerateBuildActions calls the SingletonModule's GenerateSingletonBuildActions method, but only
+// if the module was defined in an Android.bp file.
+func (smsa *singletonModuleSingletonAdaptor) GenerateBuildActions(ctx SingletonContext) {
+	if smsa.sm.singletonModuleBase().bp != "" {
+		smsa.sm.GenerateSingletonBuildActions(ctx)
+	}
+}
+
+func (smsa *singletonModuleSingletonAdaptor) MakeVars(ctx MakeVarsContext) {
+	if smsa.sm.singletonModuleBase().bp != "" {
+		if makeVars, ok := smsa.sm.(SingletonMakeVarsProvider); ok {
+			makeVars.MakeVars(ctx)
+		}
+	}
+}
diff --git a/android/singleton_module_test.go b/android/singleton_module_test.go
new file mode 100644
index 0000000..9232eb4
--- /dev/null
+++ b/android/singleton_module_test.go
@@ -0,0 +1,149 @@
+// Copyright 2021 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 (
+	"reflect"
+	"strings"
+	"testing"
+)
+
+type testSingletonModule struct {
+	SingletonModuleBase
+	ops []string
+}
+
+func (tsm *testSingletonModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+	tsm.ops = append(tsm.ops, "GenerateAndroidBuildActions")
+}
+
+func (tsm *testSingletonModule) GenerateSingletonBuildActions(ctx SingletonContext) {
+	tsm.ops = append(tsm.ops, "GenerateSingletonBuildActions")
+}
+
+func (tsm *testSingletonModule) MakeVars(ctx MakeVarsContext) {
+	tsm.ops = append(tsm.ops, "MakeVars")
+}
+
+func testSingletonModuleFactory() SingletonModule {
+	tsm := &testSingletonModule{}
+	InitAndroidSingletonModule(tsm)
+	return tsm
+}
+
+func runSingletonModuleTest(bp string) (*TestContext, []error) {
+	config := TestConfig(buildDir, nil, bp, nil)
+	// Enable Kati output to test SingletonModules with MakeVars.
+	config.katiEnabled = true
+	ctx := NewTestContext(config)
+	ctx.RegisterSingletonModuleType("test_singleton_module", testSingletonModuleFactory)
+	ctx.RegisterSingletonType("makevars", makeVarsSingletonFunc)
+	ctx.Register()
+
+	_, errs := ctx.ParseBlueprintsFiles("Android.bp")
+	if len(errs) > 0 {
+		return ctx, errs
+	}
+
+	_, errs = ctx.PrepareBuildActions(config)
+	return ctx, errs
+}
+
+func TestSingletonModule(t *testing.T) {
+	bp := `
+		test_singleton_module {
+			name: "test_singleton_module",
+		}
+	`
+	ctx, errs := runSingletonModuleTest(bp)
+	if len(errs) > 0 {
+		t.Fatal(errs)
+	}
+
+	ops := ctx.ModuleForTests("test_singleton_module", "").Module().(*testSingletonModule).ops
+	wantOps := []string{"GenerateAndroidBuildActions", "GenerateSingletonBuildActions", "MakeVars"}
+	if !reflect.DeepEqual(ops, wantOps) {
+		t.Errorf("Expected operations %q, got %q", wantOps, ops)
+	}
+}
+
+func TestDuplicateSingletonModule(t *testing.T) {
+	bp := `
+		test_singleton_module {
+			name: "test_singleton_module",
+		}
+
+		test_singleton_module {
+			name: "test_singleton_module2",
+		}
+	`
+	_, errs := runSingletonModuleTest(bp)
+	if len(errs) == 0 {
+		t.Fatal("expected duplicate SingletonModule error")
+	}
+	if len(errs) != 1 || !strings.Contains(errs[0].Error(), `Duplicate SingletonModule "test_singleton_module", previously used in`) {
+		t.Fatalf("expected duplicate SingletonModule error, got %q", errs)
+	}
+}
+
+func TestUnusedSingletonModule(t *testing.T) {
+	bp := ``
+	ctx, errs := runSingletonModuleTest(bp)
+	if len(errs) > 0 {
+		t.Fatal(errs)
+	}
+
+	singleton := ctx.SingletonForTests("test_singleton_module").Singleton()
+	sm := singleton.(*singletonModuleSingletonAdaptor).sm
+	ops := sm.(*testSingletonModule).ops
+	if ops != nil {
+		t.Errorf("Expected no operations, got %q", ops)
+	}
+}
+
+func testVariantSingletonModuleMutator(ctx BottomUpMutatorContext) {
+	if _, ok := ctx.Module().(*testSingletonModule); ok {
+		ctx.CreateVariations("a", "b")
+	}
+}
+
+func TestVariantSingletonModule(t *testing.T) {
+	bp := `
+		test_singleton_module {
+			name: "test_singleton_module",
+		}
+	`
+
+	config := TestConfig(buildDir, nil, bp, nil)
+	ctx := NewTestContext(config)
+	ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
+		ctx.BottomUp("test_singleton_module_mutator", testVariantSingletonModuleMutator)
+	})
+	ctx.RegisterSingletonModuleType("test_singleton_module", testSingletonModuleFactory)
+	ctx.Register()
+
+	_, errs := ctx.ParseBlueprintsFiles("Android.bp")
+
+	if len(errs) == 0 {
+		_, errs = ctx.PrepareBuildActions(config)
+	}
+
+	if len(errs) == 0 {
+		t.Fatal("expected duplicate SingletonModule error")
+	}
+	if len(errs) != 1 || !strings.Contains(errs[0].Error(), `GenerateAndroidBuildActions already called for variant`) {
+		t.Fatalf("expected duplicate SingletonModule error, got %q", errs)
+	}
+}
diff --git a/android/testing.go b/android/testing.go
index 6539063..470cfd6 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -103,6 +103,12 @@
 	ctx.Context.RegisterModuleType(name, ModuleFactoryAdaptor(factory))
 }
 
+func (ctx *TestContext) RegisterSingletonModuleType(name string, factory SingletonModuleFactory) {
+	s, m := SingletonModuleFactoryAdaptor(name, factory)
+	ctx.RegisterSingletonType(name, s)
+	ctx.RegisterModuleType(name, m)
+}
+
 func (ctx *TestContext) RegisterSingletonType(name string, factory SingletonFactory) {
 	ctx.Context.RegisterSingletonType(name, SingletonFactoryAdaptor(ctx.Context, factory))
 }
@@ -463,7 +469,14 @@
 //
 // The build and source paths should be distinguishable based on their contents.
 func NormalizePathForTesting(path Path) string {
+	if path == nil {
+		return "<nil path>"
+	}
 	p := path.String()
+	// Allow absolute paths to /dev/
+	if strings.HasPrefix(p, "/dev/") {
+		return p
+	}
 	if w, ok := path.(WritablePath); ok {
 		rel, err := filepath.Rel(w.buildDir(), p)
 		if err != nil {
diff --git a/android/variable.go b/android/variable.go
index 7532797..73be7a0 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -139,10 +139,6 @@
 			Enabled *bool
 		}
 
-		Experimental_mte struct {
-			Cflags []string `android:"arch_variant"`
-		} `android:"arch_variant"`
-
 		Native_coverage struct {
 			Src          *string  `android:"arch_variant"`
 			Srcs         []string `android:"arch_variant"`
@@ -264,7 +260,9 @@
 
 	DisableScudo *bool `json:",omitempty"`
 
-	Experimental_mte *bool `json:",omitempty"`
+	MemtagHeapExcludePaths      []string `json:",omitempty"`
+	MemtagHeapAsyncIncludePaths []string `json:",omitempty"`
+	MemtagHeapSyncIncludePaths  []string `json:",omitempty"`
 
 	VendorPath    *string `json:",omitempty"`
 	OdmPath       *string `json:",omitempty"`
@@ -311,6 +309,9 @@
 	VndkUseCoreVariant         *bool `json:",omitempty"`
 	VndkSnapshotBuildArtifacts *bool `json:",omitempty"`
 
+	DirectedVendorSnapshot bool            `json:",omitempty"`
+	VendorSnapshotModules  map[string]bool `json:",omitempty"`
+
 	BoardVendorSepolicyDirs      []string `json:",omitempty"`
 	BoardOdmSepolicyDirs         []string `json:",omitempty"`
 	BoardReqdMaskPolicy          []string `json:",omitempty"`
@@ -366,6 +367,8 @@
 	BoardKernelModuleInterfaceVersions []string `json:",omitempty"`
 
 	BoardMoveRecoveryResourcesToVendorBoot *bool `json:",omitempty"`
+
+	PrebuiltHiddenApiDir *string `json:",omitempty"`
 }
 
 func boolPtr(v bool) *bool {
diff --git a/apex/Android.bp b/apex/Android.bp
index e3a547c..77dde72 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -18,6 +18,7 @@
         "apex.go",
         "apex_singleton.go",
         "builder.go",
+        "deapexer.go",
         "key.go",
         "prebuilt.go",
         "vndk.go",
diff --git a/apex/allowed_deps.txt b/apex/allowed_deps.txt
index 69bf64f..81691f1 100644
--- a/apex/allowed_deps.txt
+++ b/apex/allowed_deps.txt
@@ -76,6 +76,7 @@
 androidx.customview_customview(minSdkVersion:14)
 androidx.documentfile_documentfile(minSdkVersion:14)
 androidx.drawerlayout_drawerlayout(minSdkVersion:14)
+androidx.dynamicanimation_dynamicanimation(minSdkVersion:14)
 androidx.fragment_fragment(minSdkVersion:14)
 androidx.fragment_fragment-ktx(minSdkVersion:14)
 androidx.interpolator_interpolator(minSdkVersion:14)
@@ -129,6 +130,7 @@
 bionic_libc_platform_headers(minSdkVersion:29)
 boringssl_self_test(minSdkVersion:29)
 bouncycastle_ike_digests(minSdkVersion:current)
+bpf_syscall_wrappers(minSdkVersion:30)
 brotli-java(minSdkVersion:current)
 captiveportal-lib(minSdkVersion:29)
 car-ui-lib(minSdkVersion:28)
@@ -384,6 +386,7 @@
 libring(minSdkVersion:29)
 libring-core(minSdkVersion:29)
 librustc_demangle.rust_sysroot(minSdkVersion:29)
+libsdk_proto(minSdkVersion:30)
 libsfplugin_ccodec_utils(minSdkVersion:29)
 libsonivoxwithoutjet(minSdkVersion:29)
 libspeexresampler(minSdkVersion:29)
@@ -466,7 +469,10 @@
 ndk_libunwind(minSdkVersion:16)
 net-utils-device-common(minSdkVersion:29)
 net-utils-framework-common(minSdkVersion:current)
+netd-client(minSdkVersion:29)
+netd_aidl_interface-java(minSdkVersion:29)
 netd_aidl_interface-unstable-java(minSdkVersion:29)
+netd_event_listener_interface-java(minSdkVersion:29)
 netd_event_listener_interface-ndk_platform(minSdkVersion:29)
 netd_event_listener_interface-unstable-ndk_platform(minSdkVersion:29)
 netlink-client(minSdkVersion:29)
@@ -482,6 +488,8 @@
 neuralnetworks_utils_hal_1_3(minSdkVersion:30)
 neuralnetworks_utils_hal_common(minSdkVersion:30)
 neuralnetworks_utils_hal_service(minSdkVersion:30)
+note_memtag_heap_async(minSdkVersion:16)
+note_memtag_heap_sync(minSdkVersion:16)
 PermissionController(minSdkVersion:28)
 permissioncontroller-statsd(minSdkVersion:current)
 philox_random(minSdkVersion:(no version))
@@ -507,6 +515,7 @@
 prebuilt_androidx.customview_customview-nodeps(minSdkVersion:(no version))
 prebuilt_androidx.documentfile_documentfile-nodeps(minSdkVersion:(no version))
 prebuilt_androidx.drawerlayout_drawerlayout-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.dynamicanimation_dynamicanimation-nodeps(minSdkVersion:(no version))
 prebuilt_androidx.fragment_fragment-ktx-nodeps(minSdkVersion:(no version))
 prebuilt_androidx.fragment_fragment-nodeps(minSdkVersion:(no version))
 prebuilt_androidx.interpolator_interpolator-nodeps(minSdkVersion:(no version))
@@ -560,6 +569,7 @@
 prebuilt_libclang_rt.builtins-arm-android(minSdkVersion:(no version))
 prebuilt_libclang_rt.builtins-i686-android(minSdkVersion:(no version))
 prebuilt_libclang_rt.builtins-x86_64-android(minSdkVersion:(no version))
+prebuilt_libunwind(minSdkVersion:(no version))
 prebuilt_test_framework-sdkextensions(minSdkVersion:(no version))
 server_configurable_flags(minSdkVersion:29)
 service-permission(minSdkVersion:current)
diff --git a/apex/apex.go b/apex/apex.go
index a18e34b..04d20f6 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -29,6 +29,7 @@
 	"android/soong/android"
 	"android/soong/bpf"
 	"android/soong/cc"
+	"android/soong/dexpreopt"
 	prebuilt_etc "android/soong/etc"
 	"android/soong/filesystem"
 	"android/soong/java"
@@ -168,6 +169,10 @@
 	// used in tests.
 	Test_only_unsigned_payload *bool
 
+	// Whenever apex should be compressed, regardless of product flag used. Should be only
+	// used in tests.
+	Test_only_force_compression *bool
+
 	IsCoverageVariant bool `blueprint:"mutated"`
 
 	// List of sanitizer names that this APEX is enabled for
@@ -716,9 +721,15 @@
 	ctx.AddFarVariationDependencies(commonVariation, bpfTag, a.properties.Bpfs...)
 	ctx.AddFarVariationDependencies(commonVariation, fsTag, a.properties.Filesystems...)
 
-	// With EMMA_INSTRUMENT_FRAMEWORK=true the ART boot image includes jacoco library.
-	if a.artApex && ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") {
-		ctx.AddFarVariationDependencies(commonVariation, javaLibTag, "jacocoagent")
+	if a.artApex {
+		// With EMMA_INSTRUMENT_FRAMEWORK=true the ART boot image includes jacoco library.
+		if ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") {
+			ctx.AddFarVariationDependencies(commonVariation, javaLibTag, "jacocoagent")
+		}
+		// The ART boot image depends on dex2oat to compile it.
+		if !java.SkipDexpreoptBootJars(ctx) {
+			dexpreopt.RegisterToolDeps(ctx)
+		}
 	}
 
 	// Dependencies for signing
@@ -1241,6 +1252,11 @@
 	return proptools.Bool(a.properties.Test_only_unsigned_payload)
 }
 
+// See the test_only_force_compression property
+func (a *apexBundle) testOnlyShouldForceCompression() bool {
+	return proptools.Bool(a.properties.Test_only_force_compression)
+}
+
 // These functions are interfacing with cc/sanitizer.go. The entire APEX (along with all of its
 // members) can be sanitized, either forcibly, or by the global configuration. For some of the
 // sanitizers, extra dependencies can be forcibly added as well.
@@ -1419,6 +1435,7 @@
 }
 
 var _ javaModule = (*java.Library)(nil)
+var _ javaModule = (*java.Import)(nil)
 var _ javaModule = (*java.SdkLibrary)(nil)
 var _ javaModule = (*java.DexImport)(nil)
 var _ javaModule = (*java.SdkLibraryImport)(nil)
@@ -1492,7 +1509,7 @@
 	return newApexFile(ctx, buildFile, buildFile.Base(), dirInApex, etc, fs)
 }
 
-// WalyPayloadDeps visits dependencies that contributes to the payload of this APEX. For each of the
+// WalkPayloadDeps visits dependencies that contributes to the payload of this APEX. For each of the
 // visited module, the `do` callback is executed. Returning true in the callback continues the visit
 // to the child modules. Returning false makes the visit to continue in the sibling or the parent
 // modules. This is used in check* functions below.
@@ -1511,6 +1528,9 @@
 		if dt, ok := depTag.(dependencyTag); ok && !dt.payload {
 			return false
 		}
+		if depTag == dexpreopt.Dex2oatDepTag {
+			return false
+		}
 
 		ai := ctx.OtherModuleProvider(child, android.ApexInfoProvider).(android.ApexInfo)
 		externalDep := !android.InList(ctx.ModuleName(), ai.InApexes)
@@ -1620,7 +1640,7 @@
 				}
 			case javaLibTag:
 				switch child.(type) {
-				case *java.Library, *java.SdkLibrary, *java.DexImport, *java.SdkLibraryImport:
+				case *java.Library, *java.SdkLibrary, *java.DexImport, *java.SdkLibraryImport, *java.Import:
 					af := apexFileForJavaModule(ctx, child.(javaModule))
 					if !af.ok() {
 						ctx.PropertyErrorf("java_libs", "%q is not configured to be compiled into dex", depName)
@@ -1832,10 +1852,10 @@
 		return
 	}
 
-	// Specific to the ART apex: dexpreopt artifacts for libcore Java libraries. Build rules are
-	// generated by the dexpreopt singleton, and here we access build artifacts via the global
-	// boot image config.
 	if a.artApex {
+		// Specific to the ART apex: dexpreopt artifacts for libcore Java libraries. Build rules are
+		// generated by the dexpreopt singleton, and here we access build artifacts via the global
+		// boot image config.
 		for arch, files := range java.DexpreoptedArtApexJars(ctx) {
 			dirInApex := filepath.Join("javalib", arch.String())
 			for _, f := range files {
@@ -1844,6 +1864,11 @@
 				filesInfo = append(filesInfo, af)
 			}
 		}
+		// Call GetGlobalSoongConfig to initialize it, which may be necessary if dexpreopt is
+		// disabled for libraries/apps, but boot images are still needed.
+		if !java.SkipDexpreoptBootJars(ctx) {
+			dexpreopt.GetGlobalSoongConfig(ctx)
+		}
 	}
 
 	// Remove duplicates in filesInfo
@@ -2845,7 +2870,7 @@
 		"libprofile-clang-extras_ndk",
 		"libprofile-extras",
 		"libprofile-extras_ndk",
-		"libunwind_llvm",
+		"libunwind",
 	}
 	return m
 }
diff --git a/apex/apex_test.go b/apex/apex_test.go
index fc74672..aa4b9c8 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -190,6 +190,7 @@
 		"testdata/baz":                               nil,
 		"AppSet.apks":                                nil,
 		"foo.rs":                                     nil,
+		"libfoo.jar":                                 nil,
 	}
 
 	cc.GatherRequiredFilesForTest(fs)
@@ -1751,10 +1752,10 @@
 
 	// ensure apex variant of c++ is linked with static unwinder
 	cm := ctx.ModuleForTests("libc++", "android_arm64_armv8-a_shared_apex29").Module().(*cc.Module)
-	ensureListContains(t, cm.Properties.AndroidMkStaticLibs, "libgcc_stripped")
+	ensureListContains(t, cm.Properties.AndroidMkStaticLibs, "libunwind")
 	// note that platform variant is not.
 	cm = ctx.ModuleForTests("libc++", "android_arm64_armv8-a_shared").Module().(*cc.Module)
-	ensureListNotContains(t, cm.Properties.AndroidMkStaticLibs, "libgcc_stripped")
+	ensureListNotContains(t, cm.Properties.AndroidMkStaticLibs, "libunwind")
 }
 
 func TestApexMinSdkVersion_ErrorIfIncompatibleStubs(t *testing.T) {
@@ -4219,6 +4220,121 @@
 	}
 }
 
+func TestPrebuiltExportDexImplementationJars(t *testing.T) {
+	transform := func(config *dexpreopt.GlobalConfig) {
+		config.BootJars = android.CreateTestConfiguredJarList([]string{"myapex:libfoo"})
+	}
+
+	checkDexJarBuildPath := func(ctx *android.TestContext, name string) {
+		// Make sure the import has been given the correct path to the dex jar.
+		p := ctx.ModuleForTests(name, "android_common_myapex").Module().(java.Dependency)
+		dexJarBuildPath := p.DexJarBuildPath()
+		if expected, actual := ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar", android.NormalizePathForTesting(dexJarBuildPath); actual != expected {
+			t.Errorf("Incorrect DexJarBuildPath value '%s', expected '%s'", actual, expected)
+		}
+	}
+
+	ensureNoSourceVariant := func(ctx *android.TestContext) {
+		// Make sure that an apex variant is not created for the source module.
+		if expected, actual := []string{"android_common"}, ctx.ModuleVariantsForTests("libfoo"); !reflect.DeepEqual(expected, actual) {
+			t.Errorf("invalid set of variants for %q: expected %q, found %q", "libfoo", expected, actual)
+		}
+	}
+
+	t.Run("prebuilt only", func(t *testing.T) {
+		bp := `
+		prebuilt_apex {
+			name: "myapex",
+			arch: {
+				arm64: {
+					src: "myapex-arm64.apex",
+				},
+				arm: {
+					src: "myapex-arm.apex",
+				},
+			},
+			exported_java_libs: ["libfoo"],
+		}
+
+		java_import {
+			name: "libfoo",
+			jars: ["libfoo.jar"],
+		}
+	`
+
+		// Make sure that dexpreopt can access dex implementation files from the prebuilt.
+		ctx := testDexpreoptWithApexes(t, bp, "", transform)
+
+		checkDexJarBuildPath(ctx, "libfoo")
+	})
+
+	t.Run("prebuilt with source preferred", func(t *testing.T) {
+
+		bp := `
+		prebuilt_apex {
+			name: "myapex",
+			arch: {
+				arm64: {
+					src: "myapex-arm64.apex",
+				},
+				arm: {
+					src: "myapex-arm.apex",
+				},
+			},
+			exported_java_libs: ["libfoo"],
+		}
+
+		java_import {
+			name: "libfoo",
+			jars: ["libfoo.jar"],
+		}
+
+		java_library {
+			name: "libfoo",
+		}
+	`
+
+		// Make sure that dexpreopt can access dex implementation files from the prebuilt.
+		ctx := testDexpreoptWithApexes(t, bp, "", transform)
+
+		checkDexJarBuildPath(ctx, "prebuilt_libfoo")
+		ensureNoSourceVariant(ctx)
+	})
+
+	t.Run("prebuilt preferred with source", func(t *testing.T) {
+		bp := `
+		prebuilt_apex {
+			name: "myapex",
+			prefer: true,
+			arch: {
+				arm64: {
+					src: "myapex-arm64.apex",
+				},
+				arm: {
+					src: "myapex-arm.apex",
+				},
+			},
+			exported_java_libs: ["libfoo"],
+		}
+
+		java_import {
+			name: "libfoo",
+			jars: ["libfoo.jar"],
+		}
+
+		java_library {
+			name: "libfoo",
+		}
+	`
+
+		// Make sure that dexpreopt can access dex implementation files from the prebuilt.
+		ctx := testDexpreoptWithApexes(t, bp, "", transform)
+
+		checkDexJarBuildPath(ctx, "prebuilt_libfoo")
+		ensureNoSourceVariant(ctx)
+	})
+}
+
 func TestApexWithTests(t *testing.T) {
 	ctx, config := testApex(t, `
 		apex_test {
@@ -4399,6 +4515,34 @@
 	`)
 }
 
+func TestApexWithJavaImport(t *testing.T) {
+	ctx, _ := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			java_libs: ["myjavaimport"],
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		java_import {
+			name: "myjavaimport",
+			apex_available: ["myapex"],
+			jars: ["my.jar"],
+			compile_dex: true,
+		}
+	`)
+
+	module := ctx.ModuleForTests("myapex", "android_common_myapex_image")
+	apexRule := module.Rule("apexRule")
+	copyCmds := apexRule.Args["copy_commands"]
+	ensureContains(t, copyCmds, "image.apex/javalib/myjavaimport.jar")
+}
+
 func TestApexWithApps(t *testing.T) {
 	ctx, _ := testApex(t, `
 		apex {
@@ -5783,7 +5927,7 @@
 	testDexpreoptWithApexes(t, bp, errmsg, transformDexpreoptConfig)
 }
 
-func testDexpreoptWithApexes(t *testing.T, bp, errmsg string, transformDexpreoptConfig func(*dexpreopt.GlobalConfig)) {
+func testDexpreoptWithApexes(t *testing.T, bp, errmsg string, transformDexpreoptConfig func(*dexpreopt.GlobalConfig)) *android.TestContext {
 	t.Helper()
 
 	bp += cc.GatherRequiredDepsForTest(android.Android)
@@ -5808,6 +5952,7 @@
 	ctx := android.NewTestArchContext(config)
 	ctx.RegisterModuleType("apex", BundleFactory)
 	ctx.RegisterModuleType("apex_key", ApexKeyFactory)
+	ctx.RegisterModuleType("prebuilt_apex", PrebuiltFactory)
 	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
 	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
 	android.RegisterPrebuiltMutators(ctx)
@@ -5837,10 +5982,11 @@
 		android.FailIfErrored(t, errs)
 	} else if len(errs) > 0 {
 		android.FailIfNoMatchingErrors(t, errmsg, errs)
-		return
 	} else {
 		t.Fatalf("missing expected error %q (0 errors are returned)", errmsg)
 	}
+
+	return ctx
 }
 
 func TestUpdatable_should_set_min_sdk_version(t *testing.T) {
@@ -5939,6 +6085,56 @@
 		}
 		testNoUpdatableJarsInBootImage(t, "", transform)
 	})
+
+}
+
+func TestDexpreoptAccessDexFilesFromPrebuiltApex(t *testing.T) {
+	transform := func(config *dexpreopt.GlobalConfig) {
+		config.BootJars = android.CreateTestConfiguredJarList([]string{"myapex:libfoo"})
+	}
+	t.Run("prebuilt no source", func(t *testing.T) {
+		testDexpreoptWithApexes(t, `
+			prebuilt_apex {
+				name: "myapex" ,
+				arch: {
+					arm64: {
+						src: "myapex-arm64.apex",
+					},
+					arm: {
+						src: "myapex-arm.apex",
+					},
+				},
+			exported_java_libs: ["libfoo"],
+		}
+
+		java_import {
+			name: "libfoo",
+			jars: ["libfoo.jar"],
+		}
+`, "", transform)
+	})
+
+	t.Run("prebuilt no source", func(t *testing.T) {
+		testDexpreoptWithApexes(t, `
+			prebuilt_apex {
+				name: "myapex" ,
+				arch: {
+					arm64: {
+						src: "myapex-arm64.apex",
+					},
+					arm: {
+						src: "myapex-arm.apex",
+					},
+				},
+			exported_java_libs: ["libfoo"],
+		}
+
+		java_import {
+			name: "libfoo",
+			jars: ["libfoo.jar"],
+		}
+`, "", transform)
+	})
 }
 
 func testApexPermittedPackagesRules(t *testing.T, errmsg, bp string, apexBootJars []string, rules []android.Rule) {
diff --git a/apex/builder.go b/apex/builder.go
index 106302b..e6bc3bd 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -763,9 +763,13 @@
 	})
 	a.outputFile = signedOutputFile
 
-	// Process APEX compression if enabled
+	// Process APEX compression if enabled or forced
+	if ctx.ModuleDir() != "system/apex/apexd/apexd_testdata" && a.testOnlyShouldForceCompression() {
+		ctx.PropertyErrorf("test_only_force_compression", "not available")
+		return
+	}
 	compressionEnabled := ctx.Config().CompressedApex() && proptools.BoolDefault(a.properties.Compressible, true)
-	if compressionEnabled && apexType == imageApex {
+	if apexType == imageApex && (compressionEnabled || a.testOnlyShouldForceCompression()) {
 		a.isCompressed = true
 		unsignedCompressedOutputFile := android.PathForModuleOut(ctx, a.Name()+".capex.unsigned")
 
@@ -782,6 +786,9 @@
 		compressRule.Build("compressRule", "Generate unsigned compressed APEX file")
 
 		signedCompressedOutputFile := android.PathForModuleOut(ctx, a.Name()+".capex")
+		if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_SIGNAPK") {
+			args["outCommaList"] = signedCompressedOutputFile.String()
+		}
 		ctx.Build(pctx, android.BuildParams{
 			Rule:        rule,
 			Description: "sign compressedApex",
diff --git a/apex/deapexer.go b/apex/deapexer.go
new file mode 100644
index 0000000..651cadf
--- /dev/null
+++ b/apex/deapexer.go
@@ -0,0 +1,139 @@
+// Copyright (C) 2021 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 apex
+
+import (
+	"android/soong/android"
+)
+
+// Contains 'deapexer' a private module type used by 'prebuilt_apex' to make dex files contained
+// within a .apex file referenced by `prebuilt_apex` available for use by their associated
+// `java_import` modules.
+//
+// An 'apex' module references `java_library` modules from which .dex files are obtained that are
+// stored in the resulting `.apex` file. The resulting `.apex` file is then made available as a
+// prebuilt by referencing it from a `prebuilt_apex`. For each such `java_library` that is used by
+// modules outside the `.apex` file a `java_import` prebuilt is made available referencing a jar
+// that contains the Java classes.
+//
+// When building a Java module type, e.g. `java_module` or `android_app` against such prebuilts the
+// `java_import` provides the classes jar  (jar containing `.class` files) against which the
+// module's `.java` files are compiled. That classes jar usually contains only stub classes. The
+// resulting classes jar is converted into a dex jar (jar containing `.dex` files). Then if
+// necessary the dex jar is further processed by `dexpreopt` to produce an optimized form of the
+// library specific to the current Android version. This process requires access to implementation
+// dex jars for each `java_import`. The `java_import` will obtain the implementation dex jar from
+// the `.apex` file in the associated `prebuilt_apex`.
+//
+// This is intentionally not registered by name as it is not intended to be used from within an
+// `Android.bp` file.
+
+// Properties that are specific to `deapexer` but which need to be provided on the `prebuilt_apex`
+// module.`
+type DeapexerProperties struct {
+	// List of java libraries that are embedded inside this prebuilt APEX bundle and for which this
+	// APEX bundle will provide dex implementation jars for use by dexpreopt and boot jars package
+	// check.
+	Exported_java_libs []string
+}
+
+type Deapexer struct {
+	android.ModuleBase
+	prebuilt android.Prebuilt
+
+	properties         DeapexerProperties
+	apexFileProperties ApexFileProperties
+
+	inputApex android.Path
+}
+
+func privateDeapexerFactory() android.Module {
+	module := &Deapexer{}
+	module.AddProperties(
+		&module.properties,
+		&module.apexFileProperties,
+	)
+	android.InitSingleSourcePrebuiltModule(module, &module.apexFileProperties, "Source")
+	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	return module
+}
+
+func (p *Deapexer) Prebuilt() *android.Prebuilt {
+	return &p.prebuilt
+}
+
+func (p *Deapexer) Name() string {
+	return p.prebuilt.Name(p.ModuleBase.Name())
+}
+
+func (p *Deapexer) DepsMutator(ctx android.BottomUpMutatorContext) {
+	if err := p.apexFileProperties.selectSource(ctx); err != nil {
+		ctx.ModuleErrorf("%s", err)
+		return
+	}
+
+	// Add dependencies from the java modules to which this exports files from the `.apex` file onto
+	// this module so that they can access the `DeapexerInfo` object that this provides.
+	for _, lib := range p.properties.Exported_java_libs {
+		dep := prebuiltApexExportedModuleName(ctx, lib)
+		ctx.AddReverseDependency(ctx.Module(), android.DeapexerTag, dep)
+	}
+}
+
+func (p *Deapexer) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	p.inputApex = p.Prebuilt().SingleSourcePath(ctx)
+
+	// Create and remember the directory into which the .apex file's contents will be unpacked.
+	deapexerOutput := android.PathForModuleOut(ctx, "deapexer")
+
+	exports := make(map[string]android.Path)
+
+	// Create mappings from name+tag to all the required exported paths.
+	for _, l := range p.properties.Exported_java_libs {
+		// Populate the exports that this makes available. The path here must match the path of the
+		// file in the APEX created by apexFileForJavaModule(...).
+		exports[l+"{.dexjar}"] = deapexerOutput.Join(ctx, "javalib", l+".jar")
+	}
+
+	// If the prebuilt_apex exports any files then create a build rule that unpacks the apex using
+	// deapexer and verifies that all the required files were created. Also, make the mapping from
+	// name+tag to path available for other modules.
+	if len(exports) > 0 {
+		// Make the information available for other modules.
+		ctx.SetProvider(android.DeapexerProvider, android.NewDeapexerInfo(exports))
+
+		// Create a sorted list of the files that this exports.
+		exportedPaths := make(android.Paths, 0, len(exports))
+		for _, p := range exports {
+			exportedPaths = append(exportedPaths, p)
+		}
+		exportedPaths = android.SortedUniquePaths(exportedPaths)
+
+		// The apex needs to export some files so create a ninja rule to unpack the apex and check that
+		// the required files are present.
+		builder := android.NewRuleBuilder(pctx, ctx)
+		command := builder.Command()
+		command.
+			Tool(android.PathForSource(ctx, "build/soong/scripts/unpack-prebuilt-apex.sh")).
+			BuiltTool("deapexer").
+			BuiltTool("debugfs").
+			Input(p.inputApex).
+			Text(deapexerOutput.String())
+		for _, p := range exportedPaths {
+			command.Output(p.(android.WritablePath))
+		}
+		builder.Build("deapexer", "deapex "+ctx.ModuleName())
+	}
+}
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index 7931e9e..c72a9eb 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -107,7 +107,7 @@
 	compatSymlinks []string
 }
 
-type PrebuiltProperties struct {
+type ApexFileProperties struct {
 	// the path to the prebuilt .apex file to import.
 	Source string `blueprint:"mutated"`
 
@@ -126,6 +126,39 @@
 			Src *string
 		}
 	}
+}
+
+func (p *ApexFileProperties) selectSource(ctx android.BottomUpMutatorContext) error {
+	// This is called before prebuilt_select and prebuilt_postdeps mutators
+	// The mutators requires that src to be set correctly for each arch so that
+	// arch variants are disabled when src is not provided for the arch.
+	if len(ctx.MultiTargets()) != 1 {
+		return fmt.Errorf("compile_multilib shouldn't be \"both\" for prebuilt_apex")
+	}
+	var src string
+	switch ctx.MultiTargets()[0].Arch.ArchType {
+	case android.Arm:
+		src = String(p.Arch.Arm.Src)
+	case android.Arm64:
+		src = String(p.Arch.Arm64.Src)
+	case android.X86:
+		src = String(p.Arch.X86.Src)
+	case android.X86_64:
+		src = String(p.Arch.X86_64.Src)
+	default:
+		return fmt.Errorf("prebuilt_apex does not support %q", ctx.MultiTargets()[0].Arch.String())
+	}
+	if src == "" {
+		src = String(p.Src)
+	}
+	p.Source = src
+
+	return nil
+}
+
+type PrebuiltProperties struct {
+	ApexFileProperties
+	DeapexerProperties
 
 	Installable *bool
 	// Optional name for the installed apex. If unspecified, name of the
@@ -166,40 +199,152 @@
 }
 
 // prebuilt_apex imports an `.apex` file into the build graph as if it was built with apex.
+//
+// If this needs to make files from within a `.apex` file available for use by other Soong modules,
+// e.g. make dex implementation jars available for java_import modules isted in exported_java_libs,
+// it does so as follows:
+//
+// 1. It creates a `deapexer` module that actually extracts the files from the `.apex` file and
+//    makes them available for use by other modules, at both Soong and ninja levels.
+//
+// 2. It adds a dependency onto those modules and creates an apex specific variant similar to what
+//    an `apex` module does. That ensures that code which looks for specific apex variant, e.g.
+//    dexpreopt, will work the same way from source and prebuilt.
+//
+// 3. The `deapexer` module adds a dependency from the modules that require the exported files onto
+//    itself so that they can retrieve the file paths to those files.
+//
 func PrebuiltFactory() android.Module {
 	module := &Prebuilt{}
 	module.AddProperties(&module.properties)
 	android.InitSingleSourcePrebuiltModule(module, &module.properties, "Source")
 	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+
+	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
+		props := struct {
+			Name *string
+		}{
+			Name: proptools.StringPtr(module.BaseModuleName() + ".deapexer"),
+		}
+		ctx.CreateModule(privateDeapexerFactory,
+			&props,
+			&module.properties.ApexFileProperties,
+			&module.properties.DeapexerProperties,
+		)
+	})
+
 	return module
 }
 
+func prebuiltApexExportedModuleName(ctx android.BottomUpMutatorContext, name string) string {
+	// The prebuilt_apex should be depending on prebuilt modules but as this runs after
+	// prebuilt_rename the prebuilt module may or may not be using the prebuilt_ prefixed named. So,
+	// check to see if the prefixed name is in use first, if it is then use that, otherwise assume
+	// the unprefixed name is the one to use. If the unprefixed one turns out to be a source module
+	// and not a renamed prebuilt module then that will be detected and reported as an error when
+	// processing the dependency in ApexInfoMutator().
+	prebuiltName := "prebuilt_" + name
+	if ctx.OtherModuleExists(prebuiltName) {
+		name = prebuiltName
+	}
+	return name
+}
+
 func (p *Prebuilt) DepsMutator(ctx android.BottomUpMutatorContext) {
-	// This is called before prebuilt_select and prebuilt_postdeps mutators
-	// The mutators requires that src to be set correctly for each arch so that
-	// arch variants are disabled when src is not provided for the arch.
-	if len(ctx.MultiTargets()) != 1 {
-		ctx.ModuleErrorf("compile_multilib shouldn't be \"both\" for prebuilt_apex")
+	if err := p.properties.selectSource(ctx); err != nil {
+		ctx.ModuleErrorf("%s", err)
 		return
 	}
-	var src string
-	switch ctx.MultiTargets()[0].Arch.ArchType {
-	case android.Arm:
-		src = String(p.properties.Arch.Arm.Src)
-	case android.Arm64:
-		src = String(p.properties.Arch.Arm64.Src)
-	case android.X86:
-		src = String(p.properties.Arch.X86.Src)
-	case android.X86_64:
-		src = String(p.properties.Arch.X86_64.Src)
-	default:
-		ctx.ModuleErrorf("prebuilt_apex does not support %q", ctx.MultiTargets()[0].Arch.String())
-		return
+
+	// Add dependencies onto the java modules that represent the java libraries that are provided by
+	// and exported from this prebuilt apex.
+	for _, lib := range p.properties.Exported_java_libs {
+		dep := prebuiltApexExportedModuleName(ctx, lib)
+		ctx.AddFarVariationDependencies(ctx.Config().AndroidCommonTarget.Variations(), javaLibTag, dep)
 	}
-	if src == "" {
-		src = String(p.properties.Src)
+}
+
+var _ ApexInfoMutator = (*Prebuilt)(nil)
+
+// ApexInfoMutator marks any modules for which this apex exports a file as requiring an apex
+// specific variant and checks that they are supported.
+//
+// The apexMutator will ensure that the ApexInfo objects passed to BuildForApex(ApexInfo) are
+// associated with the apex specific variant using the ApexInfoProvider for later retrieval.
+//
+// Unlike the source apex module type the prebuilt_apex module type cannot share compatible variants
+// across prebuilt_apex modules. That is because there is no way to determine whether two
+// prebuilt_apex modules that export files for the same module are compatible. e.g. they could have
+// been built from different source at different times or they could have been built with different
+// build options that affect the libraries.
+//
+// While it may be possible to provide sufficient information to determine whether two prebuilt_apex
+// modules were compatible it would be a lot of work and would not provide much benefit for a couple
+// of reasons:
+// * The number of prebuilt_apex modules that will be exporting files for the same module will be
+//   low as the prebuilt_apex only exports files for the direct dependencies that require it and
+//   very few modules are direct dependencies of multiple prebuilt_apex modules, e.g. there are a
+//   few com.android.art* apex files that contain the same contents and could export files for the
+//   same modules but only one of them needs to do so. Contrast that with source apex modules which
+//   need apex specific variants for every module that contributes code to the apex, whether direct
+//   or indirect.
+// * The build cost of a prebuilt_apex variant is generally low as at worst it will involve some
+//   extra copying of files. Contrast that with source apex modules that has to build each variant
+//   from source.
+func (p *Prebuilt) ApexInfoMutator(mctx android.TopDownMutatorContext) {
+
+	// Collect direct dependencies into contents.
+	contents := make(map[string]android.ApexMembership)
+
+	// Collect the list of dependencies.
+	var dependencies []android.ApexModule
+	mctx.VisitDirectDeps(func(m android.Module) {
+		tag := mctx.OtherModuleDependencyTag(m)
+		if tag == javaLibTag {
+			depName := mctx.OtherModuleName(m)
+
+			// It is an error if the other module is not a prebuilt.
+			if _, ok := m.(android.PrebuiltInterface); !ok {
+				mctx.PropertyErrorf("exported_java_libs", "%q is not a prebuilt module", depName)
+				return
+			}
+
+			// It is an error if the other module is not an ApexModule.
+			if _, ok := m.(android.ApexModule); !ok {
+				mctx.PropertyErrorf("exported_java_libs", "%q is not usable within an apex", depName)
+				return
+			}
+
+			// Strip off the prebuilt_ prefix if present before storing content to ensure consistent
+			// behavior whether there is a corresponding source module present or not.
+			depName = android.RemoveOptionalPrebuiltPrefix(depName)
+
+			// Remember that this module was added as a direct dependency.
+			contents[depName] = contents[depName].Add(true)
+
+			// Add the module to the list of dependencies that need to have an APEX variant.
+			dependencies = append(dependencies, m.(android.ApexModule))
+		}
+	})
+
+	// Create contents for the prebuilt_apex and store it away for later use.
+	apexContents := android.NewApexContents(contents)
+	mctx.SetProvider(ApexBundleInfoProvider, ApexBundleInfo{
+		Contents: apexContents,
+	})
+
+	// Create an ApexInfo for the prebuilt_apex.
+	apexInfo := android.ApexInfo{
+		ApexVariationName: mctx.ModuleName(),
+		InApexes:          []string{mctx.ModuleName()},
+		ApexContents:      []*android.ApexContents{apexContents},
+		ForPrebuiltApex:   true,
 	}
-	p.properties.Source = src
+
+	// Mark the dependencies of this module as requiring a variant for this module.
+	for _, am := range dependencies {
+		am.BuildForApex(apexInfo)
+	}
 }
 
 func (p *Prebuilt) GenerateAndroidBuildActions(ctx android.ModuleContext) {
diff --git a/bazel/Android.bp b/bazel/Android.bp
index 05eddc1..d222d98 100644
--- a/bazel/Android.bp
+++ b/bazel/Android.bp
@@ -6,6 +6,9 @@
         "constants.go",
         "properties.go",
     ],
+    testSrcs: [
+        "aquery_test.go",
+    ],
     pluginFor: [
         "soong_build",
     ],
diff --git a/bazel/aquery.go b/bazel/aquery.go
index 69d4fde..a196e8b 100644
--- a/bazel/aquery.go
+++ b/bazel/aquery.go
@@ -16,6 +16,8 @@
 
 import (
 	"encoding/json"
+	"fmt"
+	"path/filepath"
 	"strings"
 
 	"github.com/google/blueprint/proptools"
@@ -24,8 +26,14 @@
 // artifact contains relevant portions of Bazel's aquery proto, Artifact.
 // Represents a single artifact, whether it's a source file or a derived output file.
 type artifact struct {
-	Id       string
-	ExecPath string
+	Id             int
+	PathFragmentId int
+}
+
+type pathFragment struct {
+	Id       int
+	Label    string
+	ParentId int
 }
 
 // KeyValuePair represents Bazel's aquery proto, KeyValuePair.
@@ -38,9 +46,9 @@
 // Represents a data structure containing one or more files. Depsets in Bazel are an efficient
 // data structure for storing large numbers of file paths.
 type depSetOfFiles struct {
-	Id string
+	Id int
 	// TODO(cparsons): Handle non-flat depsets.
-	DirectArtifactIds []string
+	DirectArtifactIds []int
 }
 
 // action contains relevant portions of Bazel's aquery proto, Action.
@@ -48,9 +56,9 @@
 type action struct {
 	Arguments            []string
 	EnvironmentVariables []KeyValuePair
-	InputDepSetIds       []string
+	InputDepSetIds       []int
 	Mnemonic             string
-	OutputIds            []string
+	OutputIds            []int
 }
 
 // actionGraphContainer contains relevant portions of Bazel's aquery proto, ActionGraphContainer.
@@ -59,6 +67,7 @@
 	Artifacts     []artifact
 	Actions       []action
 	DepSetOfFiles []depSetOfFiles
+	PathFragments []pathFragment
 }
 
 // BuildStatement contains information to register a build statement corresponding (one to one)
@@ -74,17 +83,29 @@
 // AqueryBuildStatements returns an array of BuildStatements which should be registered (and output
 // to a ninja file) to correspond one-to-one with the given action graph json proto (from a bazel
 // aquery invocation).
-func AqueryBuildStatements(aqueryJsonProto []byte) []BuildStatement {
+func AqueryBuildStatements(aqueryJsonProto []byte) ([]BuildStatement, error) {
 	buildStatements := []BuildStatement{}
 
 	var aqueryResult actionGraphContainer
-	json.Unmarshal(aqueryJsonProto, &aqueryResult)
+	err := json.Unmarshal(aqueryJsonProto, &aqueryResult)
 
-	artifactIdToPath := map[string]string{}
-	for _, artifact := range aqueryResult.Artifacts {
-		artifactIdToPath[artifact.Id] = artifact.ExecPath
+	if err != nil {
+		return nil, err
 	}
-	depsetIdToArtifactIds := map[string][]string{}
+
+	pathFragments := map[int]pathFragment{}
+	for _, pathFragment := range aqueryResult.PathFragments {
+		pathFragments[pathFragment.Id] = pathFragment
+	}
+	artifactIdToPath := map[int]string{}
+	for _, artifact := range aqueryResult.Artifacts {
+		artifactPath, err := expandPathFragment(artifact.PathFragmentId, pathFragments)
+		if err != nil {
+			return nil, err
+		}
+		artifactIdToPath[artifact.Id] = artifactPath
+	}
+	depsetIdToArtifactIds := map[int][]int{}
 	for _, depset := range aqueryResult.DepSetOfFiles {
 		depsetIdToArtifactIds[depset.Id] = depset.DirectArtifactIds
 	}
@@ -92,15 +113,24 @@
 	for _, actionEntry := range aqueryResult.Actions {
 		outputPaths := []string{}
 		for _, outputId := range actionEntry.OutputIds {
-			// TODO(cparsons): Validate the id is present.
-			outputPaths = append(outputPaths, artifactIdToPath[outputId])
+			outputPath, exists := artifactIdToPath[outputId]
+			if !exists {
+				return nil, fmt.Errorf("undefined outputId %d", outputId)
+			}
+			outputPaths = append(outputPaths, outputPath)
 		}
 		inputPaths := []string{}
 		for _, inputDepSetId := range actionEntry.InputDepSetIds {
-			// TODO(cparsons): Validate the id is present.
-			for _, inputId := range depsetIdToArtifactIds[inputDepSetId] {
-				// TODO(cparsons): Validate the id is present.
-				inputPaths = append(inputPaths, artifactIdToPath[inputId])
+			inputArtifacts, exists := depsetIdToArtifactIds[inputDepSetId]
+			if !exists {
+				return nil, fmt.Errorf("undefined input depsetId %d", inputDepSetId)
+			}
+			for _, inputId := range inputArtifacts {
+				inputPath, exists := artifactIdToPath[inputId]
+				if !exists {
+					return nil, fmt.Errorf("undefined input artifactId %d", inputId)
+				}
+				inputPaths = append(inputPaths, inputPath)
 			}
 		}
 		buildStatement := BuildStatement{
@@ -112,5 +142,20 @@
 		buildStatements = append(buildStatements, buildStatement)
 	}
 
-	return buildStatements
+	return buildStatements, nil
+}
+
+func expandPathFragment(id int, pathFragmentsMap map[int]pathFragment) (string, error) {
+	labels := []string{}
+	currId := id
+	// Only positive IDs are valid for path fragments. An ID of zero indicates a terminal node.
+	for currId > 0 {
+		currFragment, ok := pathFragmentsMap[currId]
+		if !ok {
+			return "", fmt.Errorf("undefined path fragment id %d", currId)
+		}
+		labels = append([]string{currFragment.Label}, labels...)
+		currId = currFragment.ParentId
+	}
+	return filepath.Join(labels...), nil
 }
diff --git a/bazel/aquery_test.go b/bazel/aquery_test.go
new file mode 100644
index 0000000..1bd6e67
--- /dev/null
+++ b/bazel/aquery_test.go
@@ -0,0 +1,450 @@
+// 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 bazel
+
+import (
+	"fmt"
+	"reflect"
+	"testing"
+)
+
+func TestAqueryMultiArchGenrule(t *testing.T) {
+	// This input string is retrieved from a real build of bionic-related genrules.
+	const inputString = `
+{
+  "artifacts": [{
+    "id": 1,
+    "pathFragmentId": 1
+  }, {
+    "id": 2,
+    "pathFragmentId": 6
+  }, {
+    "id": 3,
+    "pathFragmentId": 8
+  }, {
+    "id": 4,
+    "pathFragmentId": 12
+  }, {
+    "id": 5,
+    "pathFragmentId": 19
+  }, {
+    "id": 6,
+    "pathFragmentId": 20
+  }, {
+    "id": 7,
+    "pathFragmentId": 21
+  }],
+  "actions": [{
+    "targetId": 1,
+    "actionKey": "ab53f6ecbdc2ee8cb8812613b63205464f1f5083f6dca87081a0a398c0f1ecf7",
+    "mnemonic": "Genrule",
+    "configurationId": 1,
+    "arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py arm ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-arm.S"],
+    "environmentVariables": [{
+      "key": "PATH",
+      "value": "/bin:/usr/bin:/usr/local/bin"
+    }],
+    "inputDepSetIds": [1],
+    "outputIds": [4],
+    "primaryOutputId": 4
+  }, {
+    "targetId": 2,
+    "actionKey": "9f4309ce165dac458498cb92811c18b0b7919782cc37b82a42d2141b8cc90826",
+    "mnemonic": "Genrule",
+    "configurationId": 1,
+    "arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py x86 ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-x86.S"],
+    "environmentVariables": [{
+      "key": "PATH",
+      "value": "/bin:/usr/bin:/usr/local/bin"
+    }],
+    "inputDepSetIds": [2],
+    "outputIds": [5],
+    "primaryOutputId": 5
+  }, {
+    "targetId": 3,
+    "actionKey": "50d6c586103ebeed3a218195540bcc30d329464eae36377eb82f8ce7c36ac342",
+    "mnemonic": "Genrule",
+    "configurationId": 1,
+    "arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py x86_64 ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-x86_64.S"],
+    "environmentVariables": [{
+      "key": "PATH",
+      "value": "/bin:/usr/bin:/usr/local/bin"
+    }],
+    "inputDepSetIds": [3],
+    "outputIds": [6],
+    "primaryOutputId": 6
+  }, {
+    "targetId": 4,
+    "actionKey": "f30cbe442f5216f4223cf16a39112cad4ec56f31f49290d85cff587e48647ffa",
+    "mnemonic": "Genrule",
+    "configurationId": 1,
+    "arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py arm64 ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-arm64.S"],
+    "environmentVariables": [{
+      "key": "PATH",
+      "value": "/bin:/usr/bin:/usr/local/bin"
+    }],
+    "inputDepSetIds": [4],
+    "outputIds": [7],
+    "primaryOutputId": 7
+  }],
+  "targets": [{
+    "id": 1,
+    "label": "@sourceroot//bionic/libc:syscalls-arm",
+    "ruleClassId": 1
+  }, {
+    "id": 2,
+    "label": "@sourceroot//bionic/libc:syscalls-x86",
+    "ruleClassId": 1
+  }, {
+    "id": 3,
+    "label": "@sourceroot//bionic/libc:syscalls-x86_64",
+    "ruleClassId": 1
+  }, {
+    "id": 4,
+    "label": "@sourceroot//bionic/libc:syscalls-arm64",
+    "ruleClassId": 1
+  }],
+  "depSetOfFiles": [{
+    "id": 1,
+    "directArtifactIds": [1, 2, 3]
+  }, {
+    "id": 2,
+    "directArtifactIds": [1, 2, 3]
+  }, {
+    "id": 3,
+    "directArtifactIds": [1, 2, 3]
+  }, {
+    "id": 4,
+    "directArtifactIds": [1, 2, 3]
+  }],
+  "configuration": [{
+    "id": 1,
+    "mnemonic": "k8-fastbuild",
+    "platformName": "k8",
+    "checksum": "485c362832c178e367d972177f68e69e0981e51e67ef1c160944473db53fe046"
+  }],
+  "ruleClasses": [{
+    "id": 1,
+    "name": "genrule"
+  }],
+  "pathFragments": [{
+    "id": 5,
+    "label": ".."
+  }, {
+    "id": 4,
+    "label": "sourceroot",
+    "parentId": 5
+  }, {
+    "id": 3,
+    "label": "bionic",
+    "parentId": 4
+  }, {
+    "id": 2,
+    "label": "libc",
+    "parentId": 3
+  }, {
+    "id": 1,
+    "label": "SYSCALLS.TXT",
+    "parentId": 2
+  }, {
+    "id": 7,
+    "label": "tools",
+    "parentId": 2
+  }, {
+    "id": 6,
+    "label": "gensyscalls.py",
+    "parentId": 7
+  }, {
+    "id": 11,
+    "label": "bazel_tools",
+    "parentId": 5
+  }, {
+    "id": 10,
+    "label": "tools",
+    "parentId": 11
+  }, {
+    "id": 9,
+    "label": "genrule",
+    "parentId": 10
+  }, {
+    "id": 8,
+    "label": "genrule-setup.sh",
+    "parentId": 9
+  }, {
+    "id": 18,
+    "label": "bazel-out"
+  }, {
+    "id": 17,
+    "label": "sourceroot",
+    "parentId": 18
+  }, {
+    "id": 16,
+    "label": "k8-fastbuild",
+    "parentId": 17
+  }, {
+    "id": 15,
+    "label": "bin",
+    "parentId": 16
+  }, {
+    "id": 14,
+    "label": "bionic",
+    "parentId": 15
+  }, {
+    "id": 13,
+    "label": "libc",
+    "parentId": 14
+  }, {
+    "id": 12,
+    "label": "syscalls-arm.S",
+    "parentId": 13
+  }, {
+    "id": 19,
+    "label": "syscalls-x86.S",
+    "parentId": 13
+  }, {
+    "id": 20,
+    "label": "syscalls-x86_64.S",
+    "parentId": 13
+  }, {
+    "id": 21,
+    "label": "syscalls-arm64.S",
+    "parentId": 13
+  }]
+}`
+	actualbuildStatements, _ := AqueryBuildStatements([]byte(inputString))
+	expectedBuildStatements := []BuildStatement{}
+	for _, arch := range []string{"arm", "arm64", "x86", "x86_64"} {
+		expectedBuildStatements = append(expectedBuildStatements,
+			BuildStatement{
+				Command: fmt.Sprintf(
+					"/bin/bash -c 'source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py %s ../sourceroot/bionic/libc/SYSCALLS.TXT > bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-%s.S'",
+					arch, arch),
+				OutputPaths: []string{
+					fmt.Sprintf("bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-%s.S", arch),
+				},
+				InputPaths: []string{
+					"../sourceroot/bionic/libc/SYSCALLS.TXT",
+					"../sourceroot/bionic/libc/tools/gensyscalls.py",
+					"../bazel_tools/tools/genrule/genrule-setup.sh",
+				},
+				Env: []KeyValuePair{
+					KeyValuePair{Key: "PATH", Value: "/bin:/usr/bin:/usr/local/bin"},
+				},
+				Mnemonic: "Genrule",
+			})
+	}
+	assertBuildStatements(t, expectedBuildStatements, actualbuildStatements)
+}
+
+func TestInvalidOutputId(t *testing.T) {
+	const inputString = `
+{
+  "artifacts": [{
+    "id": 1,
+    "pathFragmentId": 1
+  }, {
+    "id": 2,
+    "pathFragmentId": 2
+  }],
+  "actions": [{
+    "targetId": 1,
+    "actionKey": "x",
+    "mnemonic": "x",
+    "arguments": ["touch", "foo"],
+    "inputDepSetIds": [1],
+    "outputIds": [3],
+    "primaryOutputId": 3
+  }],
+  "depSetOfFiles": [{
+    "id": 1,
+    "directArtifactIds": [1, 2]
+  }],
+  "pathFragments": [{
+    "id": 1,
+    "label": "one"
+  }, {
+    "id": 2,
+    "label": "two"
+  }]
+}`
+
+	_, err := AqueryBuildStatements([]byte(inputString))
+	assertError(t, err, "undefined outputId 3")
+}
+
+func TestInvalidInputDepsetId(t *testing.T) {
+	const inputString = `
+{
+  "artifacts": [{
+    "id": 1,
+    "pathFragmentId": 1
+  }, {
+    "id": 2,
+    "pathFragmentId": 2
+  }],
+  "actions": [{
+    "targetId": 1,
+    "actionKey": "x",
+    "mnemonic": "x",
+    "arguments": ["touch", "foo"],
+    "inputDepSetIds": [2],
+    "outputIds": [1],
+    "primaryOutputId": 1
+  }],
+  "depSetOfFiles": [{
+    "id": 1,
+    "directArtifactIds": [1, 2]
+  }],
+  "pathFragments": [{
+    "id": 1,
+    "label": "one"
+  }, {
+    "id": 2,
+    "label": "two"
+  }]
+}`
+
+	_, err := AqueryBuildStatements([]byte(inputString))
+	assertError(t, err, "undefined input depsetId 2")
+}
+
+func TestInvalidInputArtifactId(t *testing.T) {
+	const inputString = `
+{
+  "artifacts": [{
+    "id": 1,
+    "pathFragmentId": 1
+  }, {
+    "id": 2,
+    "pathFragmentId": 2
+  }],
+  "actions": [{
+    "targetId": 1,
+    "actionKey": "x",
+    "mnemonic": "x",
+    "arguments": ["touch", "foo"],
+    "inputDepSetIds": [1],
+    "outputIds": [1],
+    "primaryOutputId": 1
+  }],
+  "depSetOfFiles": [{
+    "id": 1,
+    "directArtifactIds": [1, 3]
+  }],
+  "pathFragments": [{
+    "id": 1,
+    "label": "one"
+  }, {
+    "id": 2,
+    "label": "two"
+  }]
+}`
+
+	_, err := AqueryBuildStatements([]byte(inputString))
+	assertError(t, err, "undefined input artifactId 3")
+}
+
+func TestInvalidPathFragmentId(t *testing.T) {
+	const inputString = `
+{
+  "artifacts": [{
+    "id": 1,
+    "pathFragmentId": 1
+  }, {
+    "id": 2,
+    "pathFragmentId": 2
+  }],
+  "actions": [{
+    "targetId": 1,
+    "actionKey": "x",
+    "mnemonic": "x",
+    "arguments": ["touch", "foo"],
+    "inputDepSetIds": [1],
+    "outputIds": [1],
+    "primaryOutputId": 1
+  }],
+  "depSetOfFiles": [{
+    "id": 1,
+    "directArtifactIds": [1, 2]
+  }],
+  "pathFragments": [{
+    "id": 1,
+    "label": "one"
+  }, {
+    "id": 2,
+    "label": "two",
+		"parentId": 3
+  }]
+}`
+
+	_, err := AqueryBuildStatements([]byte(inputString))
+	assertError(t, err, "undefined path fragment id 3")
+}
+
+func assertError(t *testing.T, err error, expected string) {
+	if err == nil || err.Error() != expected {
+		t.Errorf("expected error '%s', but got: %s", expected, err)
+	}
+}
+
+// Asserts that the given actual build statements match the given expected build statements.
+// Build statement equivalence is determined using buildStatementEquals.
+func assertBuildStatements(t *testing.T, expected []BuildStatement, actual []BuildStatement) {
+	if len(expected) != len(actual) {
+		t.Errorf("expected %d build statements, but got %d,\n expected: %s,\n actual: %s",
+			len(expected), len(actual), expected, actual)
+		return
+	}
+ACTUAL_LOOP:
+	for _, actualStatement := range actual {
+		for _, expectedStatement := range expected {
+			if buildStatementEquals(actualStatement, expectedStatement) {
+				continue ACTUAL_LOOP
+			}
+		}
+		t.Errorf("unexpected build statement %s.\n expected: %s",
+			actualStatement, expected)
+		return
+	}
+}
+
+func buildStatementEquals(first BuildStatement, second BuildStatement) bool {
+	if first.Mnemonic != second.Mnemonic {
+		return false
+	}
+	if first.Command != second.Command {
+		return false
+	}
+	// Ordering is significant for environment variables.
+	if !reflect.DeepEqual(first.Env, second.Env) {
+		return false
+	}
+	// Ordering is irrelevant for input and output paths, so compare sets.
+	if !reflect.DeepEqual(stringSet(first.InputPaths), stringSet(second.InputPaths)) {
+		return false
+	}
+	if !reflect.DeepEqual(stringSet(first.OutputPaths), stringSet(second.OutputPaths)) {
+		return false
+	}
+	return true
+}
+
+func stringSet(stringSlice []string) map[string]struct{} {
+	stringMap := make(map[string]struct{})
+	for _, s := range stringSlice {
+		stringMap[s] = struct{}{}
+	}
+	return stringMap
+}
diff --git a/bp2build/Android.bp b/bp2build/Android.bp
new file mode 100644
index 0000000..49587f4
--- /dev/null
+++ b/bp2build/Android.bp
@@ -0,0 +1,23 @@
+bootstrap_go_package {
+    name: "soong-bp2build",
+    pkgPath: "android/soong/bp2build",
+    srcs: [
+        "androidbp_to_build_templates.go",
+        "bp2build.go",
+        "build_conversion.go",
+        "bzl_conversion.go",
+        "conversion.go",
+    ],
+    deps: [
+        "soong-android",
+    ],
+    testSrcs: [
+        "build_conversion_test.go",
+        "bzl_conversion_test.go",
+        "conversion_test.go",
+        "testing.go",
+    ],
+    pluginFor: [
+        "soong_build",
+    ],
+}
diff --git a/cmd/soong_build/queryview_templates.go b/bp2build/androidbp_to_build_templates.go
similarity index 98%
rename from cmd/soong_build/queryview_templates.go
rename to bp2build/androidbp_to_build_templates.go
index 359c0d8..75c3ccb 100644
--- a/cmd/soong_build/queryview_templates.go
+++ b/bp2build/androidbp_to_build_templates.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package main
+package bp2build
 
 const (
 	// The default `load` preamble for every generated BUILD file.
diff --git a/bp2build/bp2build.go b/bp2build/bp2build.go
new file mode 100644
index 0000000..49729e0
--- /dev/null
+++ b/bp2build/bp2build.go
@@ -0,0 +1,59 @@
+// 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 bp2build
+
+import (
+	"android/soong/android"
+	"fmt"
+	"os"
+)
+
+// The Bazel bp2build code generator is responsible for writing .bzl files that are equivalent to
+// Android.bp files that are capable of being built with Bazel.
+func Codegen(ctx CodegenContext) {
+	outputDir := android.PathForOutput(ctx, "bp2build")
+	android.RemoveAllOutputDir(outputDir)
+
+	ruleShims := CreateRuleShims(android.ModuleTypeFactories())
+
+	buildToTargets := GenerateSoongModuleTargets(ctx.Context())
+
+	filesToWrite := CreateBazelFiles(ruleShims, buildToTargets)
+	for _, f := range filesToWrite {
+		if err := writeFile(outputDir, ctx, f); err != nil {
+			fmt.Errorf("Failed to write %q (dir %q) due to %q", f.Basename, f.Dir, err)
+		}
+	}
+}
+
+func writeFile(outputDir android.OutputPath, ctx android.PathContext, f BazelFile) error {
+	return writeReadOnlyFile(ctx, getOutputPath(outputDir, ctx, f.Dir), f.Basename, f.Contents)
+}
+
+func getOutputPath(outputDir android.OutputPath, ctx android.PathContext, dir string) android.OutputPath {
+	return outputDir.Join(ctx, dir)
+}
+
+// The auto-conversion directory should be read-only, sufficient for bazel query. The files
+// are not intended to be edited by end users.
+func writeReadOnlyFile(ctx android.PathContext, dir android.OutputPath, baseName, content string) error {
+	android.CreateOutputDirIfNonexistent(dir, os.ModePerm)
+	pathToFile := dir.Join(ctx, baseName)
+
+	// 0444 is read-only
+	err := android.WriteFileToOutputDir(pathToFile, []byte(content), 0444)
+
+	return err
+}
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
new file mode 100644
index 0000000..bece8f6
--- /dev/null
+++ b/bp2build/build_conversion.go
@@ -0,0 +1,323 @@
+// 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 bp2build
+
+import (
+	"android/soong/android"
+	"fmt"
+	"reflect"
+	"strings"
+
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+)
+
+type BazelAttributes struct {
+	Attrs map[string]string
+}
+
+type BazelTarget struct {
+	name    string
+	content string
+}
+
+type bpToBuildContext interface {
+	ModuleName(module blueprint.Module) string
+	ModuleDir(module blueprint.Module) string
+	ModuleSubDir(module blueprint.Module) string
+	ModuleType(module blueprint.Module) string
+
+	VisitAllModules(visit func(blueprint.Module))
+	VisitDirectDeps(module blueprint.Module, visit func(blueprint.Module))
+}
+
+type CodegenContext struct {
+	config  android.Config
+	context android.Context
+}
+
+func (ctx CodegenContext) AddNinjaFileDeps(...string) {}
+func (ctx CodegenContext) Config() android.Config     { return ctx.config }
+func (ctx CodegenContext) Context() android.Context   { return ctx.context }
+
+// NewCodegenContext creates a wrapper context that conforms to PathContext for
+// writing BUILD files in the output directory.
+func NewCodegenContext(config android.Config, context android.Context) CodegenContext {
+	return CodegenContext{
+		context: context,
+		config:  config,
+	}
+}
+
+// props is an unsorted map. This function ensures that
+// the generated attributes are sorted to ensure determinism.
+func propsToAttributes(props map[string]string) string {
+	var attributes string
+	for _, propName := range android.SortedStringKeys(props) {
+		if shouldGenerateAttribute(propName) {
+			attributes += fmt.Sprintf("    %s = %s,\n", propName, props[propName])
+		}
+	}
+	return attributes
+}
+
+func GenerateSoongModuleTargets(ctx bpToBuildContext) map[string][]BazelTarget {
+	buildFileToTargets := make(map[string][]BazelTarget)
+	ctx.VisitAllModules(func(m blueprint.Module) {
+		dir := ctx.ModuleDir(m)
+		t := generateSoongModuleTarget(ctx, m)
+		buildFileToTargets[ctx.ModuleDir(m)] = append(buildFileToTargets[dir], t)
+	})
+	return buildFileToTargets
+}
+
+// Convert a module and its deps and props into a Bazel macro/rule
+// representation in the BUILD file.
+func generateSoongModuleTarget(ctx bpToBuildContext, m blueprint.Module) BazelTarget {
+	props := getBuildProperties(ctx, m)
+
+	// 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{}
+	if aModule, ok := m.(android.Module); ok {
+		ctx.VisitDirectDeps(aModule, func(depModule blueprint.Module) {
+			depLabels[qualifiedTargetLabel(ctx, depModule)] = true
+		})
+	}
+	attributes := propsToAttributes(props.Attrs)
+
+	depLabelList := "[\n"
+	for depLabel, _ := range depLabels {
+		depLabelList += fmt.Sprintf("        %q,\n", depLabel)
+	}
+	depLabelList += "    ]"
+
+	targetName := targetNameWithVariant(ctx, m)
+	return BazelTarget{
+		name: targetName,
+		content: fmt.Sprintf(
+			soongModuleTarget,
+			targetName,
+			ctx.ModuleName(m),
+			canonicalizeModuleType(ctx.ModuleType(m)),
+			ctx.ModuleSubDir(m),
+			depLabelList,
+			attributes),
+	}
+}
+
+func getBuildProperties(ctx bpToBuildContext, m blueprint.Module) BazelAttributes {
+	var allProps map[string]string
+	// TODO: this omits properties for blueprint modules (blueprint_go_binary,
+	// bootstrap_go_binary, bootstrap_go_package), which will have to be handled separately.
+	if aModule, ok := m.(android.Module); ok {
+		allProps = ExtractModuleProperties(aModule)
+	}
+
+	return BazelAttributes{
+		Attrs: allProps,
+	}
+}
+
+// Generically extract module properties and types into a map, keyed by the module property name.
+func ExtractModuleProperties(aModule android.Module) map[string]string {
+	ret := map[string]string{}
+
+	// Iterate over this android.Module's property structs.
+	for _, properties := range aModule.GetProperties() {
+		propertiesValue := reflect.ValueOf(properties)
+		// Check that propertiesValue is a pointer to the Properties struct, like
+		// *cc.BaseLinkerProperties or *java.CompilerProperties.
+		//
+		// propertiesValue can also be type-asserted to the structs to
+		// manipulate internal props, if needed.
+		if isStructPtr(propertiesValue.Type()) {
+			structValue := propertiesValue.Elem()
+			for k, v := range extractStructProperties(structValue, 0) {
+				ret[k] = v
+			}
+		} else {
+			panic(fmt.Errorf(
+				"properties must be a pointer to a struct, got %T",
+				propertiesValue.Interface()))
+		}
+	}
+
+	return ret
+}
+
+func isStructPtr(t reflect.Type) bool {
+	return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct
+}
+
+// prettyPrint a property value into the equivalent Starlark representation
+// recursively.
+func prettyPrint(propertyValue reflect.Value, indent int) (string, error) {
+	if isZero(propertyValue) {
+		// A property value being set or unset actually matters -- Soong does set default
+		// values for unset properties, like system_shared_libs = ["libc", "libm", "libdl"] at
+		// https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/linker.go;l=281-287;drc=f70926eef0b9b57faf04c17a1062ce50d209e480
+		//
+		// In Bazel-parlance, we would use "attr.<type>(default = <default value>)" to set the default
+		// value of unset attributes.
+		return "", nil
+	}
+
+	var ret string
+	switch propertyValue.Kind() {
+	case reflect.String:
+		ret = fmt.Sprintf("\"%v\"", escapeString(propertyValue.String()))
+	case reflect.Bool:
+		ret = strings.Title(fmt.Sprintf("%v", propertyValue.Interface()))
+	case reflect.Int, reflect.Uint, reflect.Int64:
+		ret = fmt.Sprintf("%v", propertyValue.Interface())
+	case reflect.Ptr:
+		return prettyPrint(propertyValue.Elem(), indent)
+	case reflect.Slice:
+		ret = "[\n"
+		for i := 0; i < propertyValue.Len(); i++ {
+			indexedValue, err := prettyPrint(propertyValue.Index(i), indent+1)
+			if err != nil {
+				return "", err
+			}
+
+			if indexedValue != "" {
+				ret += makeIndent(indent + 1)
+				ret += indexedValue
+				ret += ",\n"
+			}
+		}
+		ret += makeIndent(indent)
+		ret += "]"
+	case reflect.Struct:
+		ret = "{\n"
+		// Sort and print the struct props by the key.
+		structProps := extractStructProperties(propertyValue, indent)
+		for _, k := range android.SortedStringKeys(structProps) {
+			ret += makeIndent(indent + 1)
+			ret += fmt.Sprintf("%q: %s,\n", k, structProps[k])
+		}
+		ret += makeIndent(indent)
+		ret += "}"
+	case reflect.Interface:
+		// TODO(b/164227191): implement pretty print for interfaces.
+		// Interfaces are used for for arch, multilib and target properties.
+		return "", nil
+	default:
+		return "", fmt.Errorf(
+			"unexpected kind for property struct field: %s", propertyValue.Kind())
+	}
+	return ret, nil
+}
+
+// Converts a reflected property struct value into a map of property names and property values,
+// which each property value correctly pretty-printed and indented at the right nest level,
+// since property structs can be nested. In Starlark, nested structs are represented as nested
+// dicts: https://docs.bazel.build/skylark/lib/dict.html
+func extractStructProperties(structValue reflect.Value, indent int) map[string]string {
+	if structValue.Kind() != reflect.Struct {
+		panic(fmt.Errorf("Expected a reflect.Struct type, but got %s", structValue.Kind()))
+	}
+
+	ret := map[string]string{}
+	structType := structValue.Type()
+	for i := 0; i < structValue.NumField(); i++ {
+		field := structType.Field(i)
+		if shouldSkipStructField(field) {
+			continue
+		}
+
+		fieldValue := structValue.Field(i)
+		if isZero(fieldValue) {
+			// Ignore zero-valued fields
+			continue
+		}
+
+		propertyName := proptools.PropertyNameForField(field.Name)
+		prettyPrintedValue, err := prettyPrint(fieldValue, indent+1)
+		if err != nil {
+			panic(
+				fmt.Errorf(
+					"Error while parsing property: %q. %s",
+					propertyName,
+					err))
+		}
+		if prettyPrintedValue != "" {
+			ret[propertyName] = prettyPrintedValue
+		}
+	}
+
+	return ret
+}
+
+func isZero(value reflect.Value) bool {
+	switch value.Kind() {
+	case reflect.Func, reflect.Map, reflect.Slice:
+		return value.IsNil()
+	case reflect.Array:
+		valueIsZero := true
+		for i := 0; i < value.Len(); i++ {
+			valueIsZero = valueIsZero && isZero(value.Index(i))
+		}
+		return valueIsZero
+	case reflect.Struct:
+		valueIsZero := true
+		for i := 0; i < value.NumField(); i++ {
+			if value.Field(i).CanSet() {
+				valueIsZero = valueIsZero && isZero(value.Field(i))
+			}
+		}
+		return valueIsZero
+	case reflect.Ptr:
+		if !value.IsNil() {
+			return isZero(reflect.Indirect(value))
+		} else {
+			return true
+		}
+	default:
+		zeroValue := reflect.Zero(value.Type())
+		result := value.Interface() == zeroValue.Interface()
+		return result
+	}
+}
+
+func escapeString(s string) string {
+	s = strings.ReplaceAll(s, "\\", "\\\\")
+	return strings.ReplaceAll(s, "\"", "\\\"")
+}
+
+func makeIndent(indent int) string {
+	if indent < 0 {
+		panic(fmt.Errorf("indent column cannot be less than 0, but got %d", indent))
+	}
+	return strings.Repeat("    ", indent)
+}
+
+func targetNameWithVariant(c bpToBuildContext, logicModule blueprint.Module) string {
+	name := ""
+	if c.ModuleSubDir(logicModule) != "" {
+		// TODO(b/162720883): Figure out a way to drop the "--" variant suffixes.
+		name = c.ModuleName(logicModule) + "--" + c.ModuleSubDir(logicModule)
+	} else {
+		name = c.ModuleName(logicModule)
+	}
+
+	return strings.Replace(name, "//", "", 1)
+}
+
+func qualifiedTargetLabel(c bpToBuildContext, logicModule blueprint.Module) string {
+	return fmt.Sprintf("//%s:%s", c.ModuleDir(logicModule), targetNameWithVariant(c, logicModule))
+}
diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go
new file mode 100644
index 0000000..4e31aa7
--- /dev/null
+++ b/bp2build/build_conversion_test.go
@@ -0,0 +1,217 @@
+// 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 bp2build
+
+import (
+	"android/soong/android"
+	"testing"
+)
+
+func TestGenerateSoongModuleTargets(t *testing.T) {
+	testCases := []struct {
+		bp                  string
+		expectedBazelTarget string
+	}{
+		{
+			bp: `custom {
+	name: "foo",
+}
+		`,
+			expectedBazelTarget: `soong_module(
+    name = "foo",
+    module_name = "foo",
+    module_type = "custom",
+    module_variant = "",
+    module_deps = [
+    ],
+)`,
+		},
+		{
+			bp: `custom {
+	name: "foo",
+	ramdisk: true,
+}
+		`,
+			expectedBazelTarget: `soong_module(
+    name = "foo",
+    module_name = "foo",
+    module_type = "custom",
+    module_variant = "",
+    module_deps = [
+    ],
+    ramdisk = True,
+)`,
+		},
+		{
+			bp: `custom {
+	name: "foo",
+	owner: "a_string_with\"quotes\"_and_\\backslashes\\\\",
+}
+		`,
+			expectedBazelTarget: `soong_module(
+    name = "foo",
+    module_name = "foo",
+    module_type = "custom",
+    module_variant = "",
+    module_deps = [
+    ],
+    owner = "a_string_with\"quotes\"_and_\\backslashes\\\\",
+)`,
+		},
+		{
+			bp: `custom {
+	name: "foo",
+	required: ["bar"],
+}
+		`,
+			expectedBazelTarget: `soong_module(
+    name = "foo",
+    module_name = "foo",
+    module_type = "custom",
+    module_variant = "",
+    module_deps = [
+    ],
+    required = [
+        "bar",
+    ],
+)`,
+		},
+		{
+			bp: `custom {
+	name: "foo",
+	target_required: ["qux", "bazqux"],
+}
+		`,
+			expectedBazelTarget: `soong_module(
+    name = "foo",
+    module_name = "foo",
+    module_type = "custom",
+    module_variant = "",
+    module_deps = [
+    ],
+    target_required = [
+        "qux",
+        "bazqux",
+    ],
+)`,
+		},
+		{
+			bp: `custom {
+	name: "foo",
+	dist: {
+		targets: ["goal_foo"],
+		tag: ".foo",
+	},
+	dists: [
+		{
+			targets: ["goal_bar"],
+			tag: ".bar",
+		},
+	],
+}
+		`,
+			expectedBazelTarget: `soong_module(
+    name = "foo",
+    module_name = "foo",
+    module_type = "custom",
+    module_variant = "",
+    module_deps = [
+    ],
+    dist = {
+        "tag": ".foo",
+        "targets": [
+            "goal_foo",
+        ],
+    },
+    dists = [
+        {
+            "tag": ".bar",
+            "targets": [
+                "goal_bar",
+            ],
+        },
+    ],
+)`,
+		},
+		{
+			bp: `custom {
+	name: "foo",
+	required: ["bar"],
+	target_required: ["qux", "bazqux"],
+	ramdisk: true,
+	owner: "custom_owner",
+	dists: [
+		{
+			tag: ".tag",
+			targets: ["my_goal"],
+		},
+	],
+}
+		`,
+			expectedBazelTarget: `soong_module(
+    name = "foo",
+    module_name = "foo",
+    module_type = "custom",
+    module_variant = "",
+    module_deps = [
+    ],
+    dists = [
+        {
+            "tag": ".tag",
+            "targets": [
+                "my_goal",
+            ],
+        },
+    ],
+    owner = "custom_owner",
+    ramdisk = True,
+    required = [
+        "bar",
+    ],
+    target_required = [
+        "qux",
+        "bazqux",
+    ],
+)`,
+		},
+	}
+
+	dir := "."
+	for _, testCase := range testCases {
+		config := android.TestConfig(buildDir, nil, testCase.bp, nil)
+		ctx := android.NewTestContext(config)
+		ctx.RegisterModuleType("custom", customModuleFactory)
+		ctx.Register()
+
+		_, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
+		android.FailIfErrored(t, errs)
+		_, errs = ctx.PrepareBuildActions(config)
+		android.FailIfErrored(t, errs)
+
+		bazelTargets := GenerateSoongModuleTargets(ctx.Context.Context)[dir]
+		if g, w := len(bazelTargets), 1; g != w {
+			t.Fatalf("Expected %d bazel target, got %d", w, g)
+		}
+
+		actualBazelTarget := bazelTargets[0]
+		if actualBazelTarget.content != testCase.expectedBazelTarget {
+			t.Errorf(
+				"Expected generated Bazel target to be '%s', got '%s'",
+				testCase.expectedBazelTarget,
+				actualBazelTarget,
+			)
+		}
+	}
+}
diff --git a/bp2build/bzl_conversion.go b/bp2build/bzl_conversion.go
new file mode 100644
index 0000000..04c4542
--- /dev/null
+++ b/bp2build/bzl_conversion.go
@@ -0,0 +1,230 @@
+// 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 bp2build
+
+import (
+	"android/soong/android"
+	"fmt"
+	"reflect"
+	"runtime"
+	"sort"
+	"strings"
+
+	"github.com/google/blueprint/proptools"
+)
+
+var (
+	// An allowlist of prop types that are surfaced from module props to rule
+	// attributes. (nested) dictionaries are notably absent here, because while
+	// Soong supports multi value typed and nested dictionaries, Bazel's rule
+	// attr() API supports only single-level string_dicts.
+	allowedPropTypes = map[string]bool{
+		"int":         true, // e.g. 42
+		"bool":        true, // e.g. True
+		"string_list": true, // e.g. ["a", "b"]
+		"string":      true, // e.g. "a"
+	}
+)
+
+type rule struct {
+	name  string
+	attrs string
+}
+
+type RuleShim struct {
+	// The rule class shims contained in a bzl file. e.g. ["cc_object", "cc_library", ..]
+	rules []string
+
+	// The generated string content of the bzl file.
+	content string
+}
+
+// Create <module>.bzl containing Bazel rule shims for every module type available in Soong and
+// user-specified Go plugins.
+//
+// This function reuses documentation generation APIs to ensure parity between modules-as-docs
+// and modules-as-code, including the names and types of morule properties.
+func CreateRuleShims(moduleTypeFactories map[string]android.ModuleFactory) map[string]RuleShim {
+	ruleShims := map[string]RuleShim{}
+	for pkg, rules := range generateRules(moduleTypeFactories) {
+		shim := RuleShim{
+			rules: make([]string, 0, len(rules)),
+		}
+		shim.content = "load(\"//build/bazel/queryview_rules:providers.bzl\", \"SoongModuleInfo\")\n"
+
+		bzlFileName := strings.ReplaceAll(pkg, "android/soong/", "")
+		bzlFileName = strings.ReplaceAll(bzlFileName, ".", "_")
+		bzlFileName = strings.ReplaceAll(bzlFileName, "/", "_")
+
+		for _, r := range rules {
+			shim.content += fmt.Sprintf(moduleRuleShim, r.name, r.attrs)
+			shim.rules = append(shim.rules, r.name)
+		}
+		sort.Strings(shim.rules)
+		ruleShims[bzlFileName] = shim
+	}
+	return ruleShims
+}
+
+// Generate the content of soong_module.bzl with the rule shim load statements
+// and mapping of module_type to rule shim map for every module type in Soong.
+func generateSoongModuleBzl(bzlLoads map[string]RuleShim) string {
+	var loadStmts string
+	var moduleRuleMap string
+	for _, bzlFileName := range android.SortedStringKeys(bzlLoads) {
+		loadStmt := "load(\"//build/bazel/queryview_rules:"
+		loadStmt += bzlFileName
+		loadStmt += ".bzl\""
+		ruleShim := bzlLoads[bzlFileName]
+		for _, rule := range ruleShim.rules {
+			loadStmt += fmt.Sprintf(", %q", rule)
+			moduleRuleMap += "    \"" + rule + "\": " + rule + ",\n"
+		}
+		loadStmt += ")\n"
+		loadStmts += loadStmt
+	}
+
+	return fmt.Sprintf(soongModuleBzl, loadStmts, moduleRuleMap)
+}
+
+func generateRules(moduleTypeFactories map[string]android.ModuleFactory) map[string][]rule {
+	// TODO: add shims for bootstrap/blueprint go modules types
+
+	rules := make(map[string][]rule)
+	// TODO: allow registration of a bzl rule when registring a factory
+	for _, moduleType := range android.SortedStringKeys(moduleTypeFactories) {
+		factory := moduleTypeFactories[moduleType]
+		factoryName := runtime.FuncForPC(reflect.ValueOf(factory).Pointer()).Name()
+		pkg := strings.Split(factoryName, ".")[0]
+		attrs := `{
+        "module_name": attr.string(mandatory = True),
+        "module_variant": attr.string(),
+        "module_deps": attr.label_list(providers = [SoongModuleInfo]),
+`
+		attrs += getAttributes(factory)
+		attrs += "    },"
+
+		r := rule{
+			name:  canonicalizeModuleType(moduleType),
+			attrs: attrs,
+		}
+
+		rules[pkg] = append(rules[pkg], r)
+	}
+	return rules
+}
+
+type property struct {
+	name             string
+	starlarkAttrType string
+	properties       []property
+}
+
+const (
+	attributeIndent = "        "
+)
+
+func (p *property) attributeString() string {
+	if !shouldGenerateAttribute(p.name) {
+		return ""
+	}
+
+	if _, ok := allowedPropTypes[p.starlarkAttrType]; !ok {
+		// a struct -- let's just comment out sub-props
+		s := fmt.Sprintf(attributeIndent+"# %s start\n", p.name)
+		for _, nestedP := range p.properties {
+			s += "# " + nestedP.attributeString()
+		}
+		s += fmt.Sprintf(attributeIndent+"# %s end\n", p.name)
+		return s
+	}
+	return fmt.Sprintf(attributeIndent+"%q: attr.%s(),\n", p.name, p.starlarkAttrType)
+}
+
+func extractPropertyDescriptionsFromStruct(structType reflect.Type) []property {
+	properties := make([]property, 0)
+	for i := 0; i < structType.NumField(); i++ {
+		field := structType.Field(i)
+		if shouldSkipStructField(field) {
+			continue
+		}
+
+		properties = append(properties, extractPropertyDescriptions(field.Name, field.Type)...)
+	}
+	return properties
+}
+
+func extractPropertyDescriptions(name string, t reflect.Type) []property {
+	name = proptools.PropertyNameForField(name)
+
+	// TODO: handle android:paths tags, they should be changed to label types
+
+	starlarkAttrType := fmt.Sprintf("%s", t.Name())
+	props := make([]property, 0)
+
+	switch t.Kind() {
+	case reflect.Bool, reflect.String:
+		// do nothing
+	case reflect.Uint, reflect.Int, reflect.Int64:
+		starlarkAttrType = "int"
+	case reflect.Slice:
+		if t.Elem().Kind() != reflect.String {
+			// TODO: handle lists of non-strings (currently only list of Dist)
+			return []property{}
+		}
+		starlarkAttrType = "string_list"
+	case reflect.Struct:
+		props = extractPropertyDescriptionsFromStruct(t)
+	case reflect.Ptr:
+		return extractPropertyDescriptions(name, t.Elem())
+	case reflect.Interface:
+		// Interfaces are used for for arch, multilib and target properties, which are handled at runtime.
+		// These will need to be handled in a bazel-specific version of the arch mutator.
+		return []property{}
+	}
+
+	prop := property{
+		name:             name,
+		starlarkAttrType: starlarkAttrType,
+		properties:       props,
+	}
+
+	return []property{prop}
+}
+
+func getPropertyDescriptions(props []interface{}) []property {
+	// there may be duplicate properties, e.g. from defaults libraries
+	propertiesByName := make(map[string]property)
+	for _, p := range props {
+		for _, prop := range extractPropertyDescriptionsFromStruct(reflect.ValueOf(p).Elem().Type()) {
+			propertiesByName[prop.name] = prop
+		}
+	}
+
+	properties := make([]property, 0, len(propertiesByName))
+	for _, key := range android.SortedStringKeys(propertiesByName) {
+		properties = append(properties, propertiesByName[key])
+	}
+
+	return properties
+}
+
+func getAttributes(factory android.ModuleFactory) string {
+	attrs := ""
+	for _, p := range getPropertyDescriptions(factory().GetProperties()) {
+		attrs += p.attributeString()
+	}
+	return attrs
+}
diff --git a/bp2build/bzl_conversion_test.go b/bp2build/bzl_conversion_test.go
new file mode 100644
index 0000000..8bea3f6
--- /dev/null
+++ b/bp2build/bzl_conversion_test.go
@@ -0,0 +1,208 @@
+// 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 bp2build
+
+import (
+	"android/soong/android"
+	"io/ioutil"
+	"os"
+	"strings"
+	"testing"
+)
+
+var buildDir string
+
+func setUp() {
+	var err error
+	buildDir, err = ioutil.TempDir("", "bazel_queryview_test")
+	if err != nil {
+		panic(err)
+	}
+}
+
+func tearDown() {
+	os.RemoveAll(buildDir)
+}
+
+func TestMain(m *testing.M) {
+	run := func() int {
+		setUp()
+		defer tearDown()
+
+		return m.Run()
+	}
+
+	os.Exit(run())
+}
+
+func TestGenerateModuleRuleShims(t *testing.T) {
+	moduleTypeFactories := map[string]android.ModuleFactory{
+		"custom":          customModuleFactoryBase,
+		"custom_test":     customTestModuleFactoryBase,
+		"custom_defaults": customDefaultsModuleFactoryBasic,
+	}
+	ruleShims := CreateRuleShims(moduleTypeFactories)
+
+	if len(ruleShims) != 1 {
+		t.Errorf("Expected to generate 1 rule shim, but got %d", len(ruleShims))
+	}
+
+	ruleShim := ruleShims["bp2build"]
+	expectedRules := []string{
+		"custom",
+		"custom_defaults",
+		"custom_test_",
+	}
+
+	if len(ruleShim.rules) != len(expectedRules) {
+		t.Errorf("Expected %d rules, but got %d", len(expectedRules), len(ruleShim.rules))
+	}
+
+	for i, rule := range ruleShim.rules {
+		if rule != expectedRules[i] {
+			t.Errorf("Expected rule shim to contain %s, but got %s", expectedRules[i], rule)
+		}
+	}
+	expectedBzl := `load("//build/bazel/queryview_rules:providers.bzl", "SoongModuleInfo")
+
+def _custom_impl(ctx):
+    return [SoongModuleInfo()]
+
+custom = rule(
+    implementation = _custom_impl,
+    attrs = {
+        "module_name": attr.string(mandatory = True),
+        "module_variant": attr.string(),
+        "module_deps": attr.label_list(providers = [SoongModuleInfo]),
+        "bool_prop": attr.bool(),
+        "bool_ptr_prop": attr.bool(),
+        "int64_ptr_prop": attr.int(),
+        # nested_props start
+#         "nested_prop": attr.string(),
+        # nested_props end
+        # nested_props_ptr start
+#         "nested_prop": attr.string(),
+        # nested_props_ptr end
+        "string_list_prop": attr.string_list(),
+        "string_prop": attr.string(),
+        "string_ptr_prop": attr.string(),
+    },
+)
+
+def _custom_defaults_impl(ctx):
+    return [SoongModuleInfo()]
+
+custom_defaults = rule(
+    implementation = _custom_defaults_impl,
+    attrs = {
+        "module_name": attr.string(mandatory = True),
+        "module_variant": attr.string(),
+        "module_deps": attr.label_list(providers = [SoongModuleInfo]),
+        "bool_prop": attr.bool(),
+        "bool_ptr_prop": attr.bool(),
+        "int64_ptr_prop": attr.int(),
+        # nested_props start
+#         "nested_prop": attr.string(),
+        # nested_props end
+        # nested_props_ptr start
+#         "nested_prop": attr.string(),
+        # nested_props_ptr end
+        "string_list_prop": attr.string_list(),
+        "string_prop": attr.string(),
+        "string_ptr_prop": attr.string(),
+    },
+)
+
+def _custom_test__impl(ctx):
+    return [SoongModuleInfo()]
+
+custom_test_ = rule(
+    implementation = _custom_test__impl,
+    attrs = {
+        "module_name": attr.string(mandatory = True),
+        "module_variant": attr.string(),
+        "module_deps": attr.label_list(providers = [SoongModuleInfo]),
+        "bool_prop": attr.bool(),
+        "bool_ptr_prop": attr.bool(),
+        "int64_ptr_prop": attr.int(),
+        # nested_props start
+#         "nested_prop": attr.string(),
+        # nested_props end
+        # nested_props_ptr start
+#         "nested_prop": attr.string(),
+        # nested_props_ptr end
+        "string_list_prop": attr.string_list(),
+        "string_prop": attr.string(),
+        "string_ptr_prop": attr.string(),
+        # test_prop start
+#         "test_string_prop": attr.string(),
+        # test_prop end
+    },
+)
+`
+
+	if ruleShim.content != expectedBzl {
+		t.Errorf(
+			"Expected the generated rule shim bzl to be:\n%s\nbut got:\n%s",
+			expectedBzl,
+			ruleShim.content)
+	}
+}
+
+func TestGenerateSoongModuleBzl(t *testing.T) {
+	ruleShims := map[string]RuleShim{
+		"file1": RuleShim{
+			rules:   []string{"a", "b"},
+			content: "irrelevant",
+		},
+		"file2": RuleShim{
+			rules:   []string{"c", "d"},
+			content: "irrelevant",
+		},
+	}
+	files := CreateBazelFiles(ruleShims, make(map[string][]BazelTarget))
+
+	var actualSoongModuleBzl BazelFile
+	for _, f := range files {
+		if f.Basename == "soong_module.bzl" {
+			actualSoongModuleBzl = f
+		}
+	}
+
+	expectedLoad := `load("//build/bazel/queryview_rules:file1.bzl", "a", "b")
+load("//build/bazel/queryview_rules:file2.bzl", "c", "d")
+`
+	expectedRuleMap := `soong_module_rule_map = {
+    "a": a,
+    "b": b,
+    "c": c,
+    "d": d,
+}`
+	if !strings.Contains(actualSoongModuleBzl.Contents, expectedLoad) {
+		t.Errorf(
+			"Generated soong_module.bzl:\n\n%s\n\n"+
+				"Could not find the load statement in the generated soong_module.bzl:\n%s",
+			actualSoongModuleBzl.Contents,
+			expectedLoad)
+	}
+
+	if !strings.Contains(actualSoongModuleBzl.Contents, expectedRuleMap) {
+		t.Errorf(
+			"Generated soong_module.bzl:\n\n%s\n\n"+
+				"Could not find the module -> rule map in the generated soong_module.bzl:\n%s",
+			actualSoongModuleBzl.Contents,
+			expectedRuleMap)
+	}
+}
diff --git a/bp2build/conversion.go b/bp2build/conversion.go
new file mode 100644
index 0000000..cdfb38b
--- /dev/null
+++ b/bp2build/conversion.go
@@ -0,0 +1,118 @@
+package bp2build
+
+import (
+	"android/soong/android"
+	"reflect"
+	"sort"
+	"strings"
+
+	"github.com/google/blueprint/proptools"
+)
+
+type BazelFile struct {
+	Dir      string
+	Basename string
+	Contents string
+}
+
+func CreateBazelFiles(
+	ruleShims map[string]RuleShim,
+	buildToTargets map[string][]BazelTarget) []BazelFile {
+	files := make([]BazelFile, 0, len(ruleShims)+len(buildToTargets)+numAdditionalFiles)
+
+	// Write top level files: WORKSPACE and BUILD. These files are empty.
+	files = append(files, newFile("", "WORKSPACE", ""))
+	// Used to denote that the top level directory is a package.
+	files = append(files, newFile("", "BUILD", ""))
+
+	files = append(files, newFile(bazelRulesSubDir, "BUILD", ""))
+	files = append(files, newFile(bazelRulesSubDir, "providers.bzl", providersBzl))
+
+	for bzlFileName, ruleShim := range ruleShims {
+		files = append(files, newFile(bazelRulesSubDir, bzlFileName+".bzl", ruleShim.content))
+	}
+	files = append(files, newFile(bazelRulesSubDir, "soong_module.bzl", generateSoongModuleBzl(ruleShims)))
+
+	files = append(files, createBuildFiles(buildToTargets)...)
+
+	return files
+}
+
+func createBuildFiles(buildToTargets map[string][]BazelTarget) []BazelFile {
+	files := make([]BazelFile, 0, len(buildToTargets))
+	for _, dir := range android.SortedStringKeys(buildToTargets) {
+		content := soongModuleLoad
+		targets := buildToTargets[dir]
+		sort.Slice(targets, func(i, j int) bool { return targets[i].name < targets[j].name })
+		for _, t := range targets {
+			content += "\n\n"
+			content += t.content
+		}
+		files = append(files, newFile(dir, "BUILD.bazel", content))
+	}
+	return files
+}
+
+func newFile(dir, basename, content string) BazelFile {
+	return BazelFile{
+		Dir:      dir,
+		Basename: basename,
+		Contents: content,
+	}
+}
+
+const (
+	bazelRulesSubDir = "build/bazel/queryview_rules"
+
+	// additional files:
+	//  * workspace file
+	//  * base BUILD file
+	//  * rules BUILD file
+	//  * rules providers.bzl file
+	//  * rules soong_module.bzl file
+	numAdditionalFiles = 5
+)
+
+var (
+	// Certain module property names are blocklisted/ignored here, for the reasons commented.
+	ignoredPropNames = map[string]bool{
+		"name":       true, // redundant, since this is explicitly generated for every target
+		"from":       true, // reserved keyword
+		"in":         true, // reserved keyword
+		"arch":       true, // interface prop type is not supported yet.
+		"multilib":   true, // interface prop type is not supported yet.
+		"target":     true, // interface prop type is not supported yet.
+		"visibility": true, // Bazel has native visibility semantics. Handle later.
+		"features":   true, // There is already a built-in attribute 'features' which cannot be overridden.
+	}
+)
+
+func shouldGenerateAttribute(prop string) bool {
+	return !ignoredPropNames[prop]
+}
+
+func shouldSkipStructField(field reflect.StructField) bool {
+	if field.PkgPath != "" {
+		// Skip unexported fields. Some properties are
+		// internal to Soong only, and these fields do not have PkgPath.
+		return true
+	}
+	// fields with tag `blueprint:"mutated"` are exported to enable modification in mutators, etc
+	// but cannot be set in a .bp file
+	if proptools.HasTag(field, "blueprint", "mutated") {
+		return true
+	}
+	return false
+}
+
+// FIXME(b/168089390): In Bazel, rules ending with "_test" needs to be marked as
+// testonly = True, forcing other rules that depend on _test rules to also be
+// marked as testonly = True. This semantic constraint is not present in Soong.
+// To work around, rename "*_test" rules to "*_test_".
+func canonicalizeModuleType(moduleName string) string {
+	if strings.HasSuffix(moduleName, "_test") {
+		return moduleName + "_"
+	}
+
+	return moduleName
+}
diff --git a/bp2build/conversion_test.go b/bp2build/conversion_test.go
new file mode 100644
index 0000000..a38fa6a
--- /dev/null
+++ b/bp2build/conversion_test.go
@@ -0,0 +1,73 @@
+// 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 bp2build
+
+import (
+	"sort"
+	"testing"
+)
+
+func TestCreateBazelFiles_AddsTopLevelFiles(t *testing.T) {
+	files := CreateBazelFiles(map[string]RuleShim{}, map[string][]BazelTarget{})
+	expectedFilePaths := []struct {
+		dir      string
+		basename string
+	}{
+		{
+			dir:      "",
+			basename: "BUILD",
+		},
+		{
+			dir:      "",
+			basename: "WORKSPACE",
+		},
+		{
+			dir:      bazelRulesSubDir,
+			basename: "BUILD",
+		},
+		{
+			dir:      bazelRulesSubDir,
+			basename: "providers.bzl",
+		},
+		{
+			dir:      bazelRulesSubDir,
+			basename: "soong_module.bzl",
+		},
+	}
+
+	if g, w := len(files), len(expectedFilePaths); g != w {
+		t.Errorf("Expected %d files, got %d", w, g)
+	}
+
+	sort.Slice(files, func(i, j int) bool {
+		if dir1, dir2 := files[i].Dir, files[j].Dir; dir1 == dir2 {
+			return files[i].Basename < files[j].Basename
+		} else {
+			return dir1 < dir2
+		}
+	})
+
+	for i := range files {
+		if g, w := files[i], expectedFilePaths[i]; g.Dir != w.dir || g.Basename != w.basename {
+			t.Errorf("Did not find expected file %s/%s", g.Dir, g.Basename)
+		} else if g.Basename == "BUILD" || g.Basename == "WORKSPACE" {
+			if g.Contents != "" {
+				t.Errorf("Expected %s to have no content.", g)
+			}
+		} else if g.Contents == "" {
+			t.Errorf("Contents of %s unexpected empty.", g)
+		}
+	}
+}
diff --git a/bp2build/testing.go b/bp2build/testing.go
new file mode 100644
index 0000000..2da32c6
--- /dev/null
+++ b/bp2build/testing.go
@@ -0,0 +1,102 @@
+package bp2build
+
+import (
+	"android/soong/android"
+)
+
+type nestedProps struct {
+	Nested_prop string
+}
+
+type customProps struct {
+	Bool_prop     bool
+	Bool_ptr_prop *bool
+	// Ensure that properties tagged `blueprint:mutated` are omitted
+	Int_prop         int `blueprint:"mutated"`
+	Int64_ptr_prop   *int64
+	String_prop      string
+	String_ptr_prop  *string
+	String_list_prop []string
+
+	Nested_props     nestedProps
+	Nested_props_ptr *nestedProps
+}
+
+type customModule struct {
+	android.ModuleBase
+
+	props customProps
+}
+
+// OutputFiles is needed because some instances of this module use dist with a
+// tag property which requires the module implements OutputFileProducer.
+func (m *customModule) OutputFiles(tag string) (android.Paths, error) {
+	return android.PathsForTesting("path" + tag), nil
+}
+
+func (m *customModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	// nothing for now.
+}
+
+func customModuleFactoryBase() android.Module {
+	module := &customModule{}
+	module.AddProperties(&module.props)
+	return module
+}
+
+func customModuleFactory() android.Module {
+	m := customModuleFactoryBase()
+	android.InitAndroidModule(m)
+	return m
+}
+
+type testProps struct {
+	Test_prop struct {
+		Test_string_prop string
+	}
+}
+
+type customTestModule struct {
+	android.ModuleBase
+
+	props      customProps
+	test_props testProps
+}
+
+func (m *customTestModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	// nothing for now.
+}
+
+func customTestModuleFactoryBase() android.Module {
+	m := &customTestModule{}
+	m.AddProperties(&m.props)
+	m.AddProperties(&m.test_props)
+	return m
+}
+
+func customTestModuleFactory() android.Module {
+	m := customTestModuleFactoryBase()
+	android.InitAndroidModule(m)
+	return m
+}
+
+type customDefaultsModule struct {
+	android.ModuleBase
+	android.DefaultsModuleBase
+}
+
+func customDefaultsModuleFactoryBase() android.DefaultsModule {
+	module := &customDefaultsModule{}
+	module.AddProperties(&customProps{})
+	return module
+}
+
+func customDefaultsModuleFactoryBasic() android.Module {
+	return customDefaultsModuleFactoryBase()
+}
+
+func customDefaultsModuleFactory() android.Module {
+	m := customDefaultsModuleFactoryBase()
+	android.InitDefaultsModule(m)
+	return m
+}
diff --git a/cc/cc.go b/cc/cc.go
index d719006..c4a1c00 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -57,14 +57,14 @@
 	})
 
 	ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.TopDown("asan_deps", sanitizerDepsMutator(asan))
-		ctx.BottomUp("asan", sanitizerMutator(asan)).Parallel()
+		ctx.TopDown("asan_deps", sanitizerDepsMutator(Asan))
+		ctx.BottomUp("asan", sanitizerMutator(Asan)).Parallel()
 
 		ctx.TopDown("hwasan_deps", sanitizerDepsMutator(hwasan))
 		ctx.BottomUp("hwasan", sanitizerMutator(hwasan)).Parallel()
 
-		ctx.TopDown("fuzzer_deps", sanitizerDepsMutator(fuzzer))
-		ctx.BottomUp("fuzzer", sanitizerMutator(fuzzer)).Parallel()
+		ctx.TopDown("fuzzer_deps", sanitizerDepsMutator(Fuzzer))
+		ctx.BottomUp("fuzzer", sanitizerMutator(Fuzzer)).Parallel()
 
 		// cfi mutator shouldn't run before sanitizers that return true for
 		// incompatibleWithCfi()
@@ -369,10 +369,7 @@
 	// If set to false, this module becomes inaccessible from /vendor modules.
 	//
 	// The modules with vndk: {enabled: true} must define 'vendor_available'
-	// to either 'true' or 'false'. In this case, 'vendor_available: false' has
-	// a different meaning than that of non-VNDK modules.
-	// 'vendor_available: false' for a VNDK module means 'VNDK-private' that
-	// can only be depended on by VNDK libraries, not by non-VNDK vendor modules.
+	// to 'true'.
 	//
 	// Nothing happens if BOARD_VNDK_VERSION isn't set in the BoardConfig.mk
 	Vendor_available *bool
@@ -394,13 +391,6 @@
 	// vndk: {enabled: true} don't have to define 'product_available'. The VNDK
 	// library without 'product_available' may not be depended on by any other
 	// modules that has product variants including the product available VNDKs.
-	// However, for the modules with vndk: {enabled: true},
-	// 'product_available: false' creates the product variant that is available
-	// only for the other product available VNDK modules but not by non-VNDK
-	// product modules.
-	// In the case of the modules with vndk: {enabled: true}, if
-	// 'product_available' is defined, it must have the same value with the
-	// 'vendor_available'.
 	//
 	// Nothing happens if BOARD_VNDK_VERSION isn't set in the BoardConfig.mk
 	// and PRODUCT_PRODUCT_VNDK_VERSION isn't set.
@@ -418,9 +408,22 @@
 	// IsLLNDK is set to true for the vendor variant of a cc_library module that has LLNDK stubs.
 	IsLLNDK bool `blueprint:"mutated"`
 
-	// IsLLNDKPrivate is set to true for the vendor variant of a cc_library module that has LLNDK
-	// stubs and also sets llndk.private: true.
-	IsLLNDKPrivate bool `blueprint:"mutated"`
+	// IsVNDKUsingCoreVariant is true for VNDK modules if the global VndkUseCoreVariant option is
+	// set and the module is not listed in VndkMustUseVendorVariantList.
+	IsVNDKUsingCoreVariant bool `blueprint:"mutated"`
+
+	// IsVNDKCore is set if a VNDK module does not set the vndk.support_system_process property.
+	IsVNDKCore bool `blueprint:"mutated"`
+
+	// IsVNDKSP is set if a VNDK module sets the vndk.support_system_process property.
+	IsVNDKSP bool `blueprint:"mutated"`
+
+	// IsVNDKPrivate is set if a VNDK module sets the vndk.private property or an LLNDK
+	// module sets the llndk.private property.
+	IsVNDKPrivate bool `blueprint:"mutated"`
+
+	// IsVNDKProduct is set if a VNDK module sets the product_available property.
+	IsVNDKProduct bool `blueprint:"mutated"`
 }
 
 // ModuleContextIntf is an interface (on a module context helper) consisting of functions related
@@ -430,6 +433,7 @@
 type ModuleContextIntf interface {
 	static() bool
 	staticBinary() bool
+	testBinary() bool
 	header() bool
 	binary() bool
 	object() bool
@@ -785,6 +789,14 @@
 	hideApexVariantFromMake bool
 }
 
+func (c *Module) SetPreventInstall() {
+	c.Properties.PreventInstall = true
+}
+
+func (c *Module) SetHideFromMake() {
+	c.Properties.HideFromMake = true
+}
+
 func (c *Module) Toc() android.OptionalPath {
 	if c.linker != nil {
 		if library, ok := c.linker.(libraryInterface); ok {
@@ -1026,7 +1038,7 @@
 
 // Returns true for dependency roots (binaries)
 // TODO(ccross): also handle dlopenable libraries
-func (c *Module) isDependencyRoot() bool {
+func (c *Module) IsDependencyRoot() bool {
 	if root, ok := c.linker.(interface {
 		isDependencyRoot() bool
 	}); ok {
@@ -1072,7 +1084,7 @@
 
 // IsLlndkPublic returns true only for LLNDK (public) libs.
 func (c *Module) IsLlndkPublic() bool {
-	return c.VendorProperties.IsLLNDK && !c.VendorProperties.IsLLNDKPrivate
+	return c.VendorProperties.IsLLNDK && !c.VendorProperties.IsVNDKPrivate
 }
 
 // isImplementationForLLNDKPublic returns true for any variant of a cc_library that has LLNDK stubs
@@ -1264,8 +1276,12 @@
 	return ctx.mod.staticBinary()
 }
 
+func (ctx *moduleContextImpl) testBinary() bool {
+	return ctx.mod.testBinary()
+}
+
 func (ctx *moduleContextImpl) header() bool {
-	return ctx.mod.header()
+	return ctx.mod.Header()
 }
 
 func (ctx *moduleContextImpl) binary() bool {
@@ -1422,6 +1438,10 @@
 	return nil
 }
 
+func (c *Module) IsPrebuilt() bool {
+	return c.Prebuilt() != nil
+}
+
 func (c *Module) Name() string {
 	name := c.ModuleBase.Name()
 	if p, ok := c.linker.(interface {
@@ -2317,10 +2337,18 @@
 			return false
 		}
 
+		// These dependencies are not excercised at runtime. Tracking these will give us
+		// false negative, so skip.
 		depTag := ctx.OtherModuleDependencyTag(child)
 		if IsHeaderDepTag(depTag) {
 			return false
 		}
+		if depTag == staticVariantTag {
+			return false
+		}
+		if depTag == stubImplDepTag {
+			return false
+		}
 
 		// Even if target lib has no vendor variant, keep checking dependency
 		// graph in case it depends on vendor_available or product_available
@@ -2329,22 +2357,24 @@
 			return true
 		}
 
-		if to.isVndkSp() || to.IsLlndk() || Bool(to.VendorProperties.Double_loadable) {
+		// The happy path. Keep tracking dependencies until we hit a non double-loadable
+		// one.
+		if Bool(to.VendorProperties.Double_loadable) {
+			return true
+		}
+
+		if to.isVndkSp() || to.IsLlndk() {
 			return false
 		}
 
-		var stringPath []string
-		for _, m := range ctx.GetWalkPath() {
-			stringPath = append(stringPath, m.Name())
-		}
 		ctx.ModuleErrorf("links a library %q which is not LL-NDK, "+
 			"VNDK-SP, or explicitly marked as 'double_loadable:true'. "+
-			"(dependency: %s)", ctx.OtherModuleName(to), strings.Join(stringPath, " -> "))
+			"Dependency list: %s", ctx.OtherModuleName(to), ctx.GetPathString(false))
 		return false
 	}
 	if module, ok := ctx.Module().(*Module); ok {
 		if lib, ok := module.linker.(*libraryDecorator); ok && lib.shared() {
-			if lib.hasLLNDKStubs() || Bool(module.VendorProperties.Double_loadable) {
+			if lib.hasLLNDKStubs() {
 				ctx.WalkDeps(check)
 			}
 		}
@@ -2848,7 +2878,7 @@
 				return baseName + ".vendor"
 			}
 
-			if c.inVendor() && vendorSuffixModules[baseName] {
+			if c.InVendor() && vendorSuffixModules[baseName] {
 				return baseName + ".vendor"
 			} else if c.InRecovery() && recoverySuffixModules[baseName] {
 				return baseName + ".recovery"
@@ -2960,7 +2990,17 @@
 	return false
 }
 
-func (c *Module) header() bool {
+func (c *Module) testBinary() bool {
+	if test, ok := c.linker.(interface {
+		testBinary() bool
+	}); ok {
+		return test.testBinary()
+	}
+	return false
+}
+
+// Header returns true if the module is a header-only variant. (See cc/library.go header()).
+func (c *Module) Header() bool {
 	if h, ok := c.linker.(interface {
 		header() bool
 	}); ok {
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 3502d5f..eef13da 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -316,16 +316,8 @@
 
 func checkVndkLibrariesOutput(t *testing.T, ctx *android.TestContext, module string, expected []string) {
 	t.Helper()
-	vndkLibraries := ctx.ModuleForTests(module, "")
-
-	var output string
-	if module != "vndkcorevariant.libraries.txt" {
-		output = insertVndkVersion(module, "VER")
-	} else {
-		output = module
-	}
-
-	checkWriteFileOutput(t, vndkLibraries.Output(output), expected)
+	got := ctx.ModuleForTests(module, "").Module().(*vndkLibrariesTxt).fileNames
+	assertArrayString(t, got, expected)
 }
 
 func TestVndk(t *testing.T) {
@@ -738,12 +730,29 @@
 			},
 			nocrt: true,
 		}
+
+		cc_library {
+			name: "libllndk",
+			llndk_stubs: "libllndk.llndk",
+		}
+
+		llndk_library {
+			name: "libllndk.llndk",
+			symbol_file: "",
+			export_llndk_headers: ["libllndk_headers"],
+		}
+
+		llndk_headers {
+			name: "libllndk_headers",
+			export_include_dirs: ["include"],
+		}
 	`)
 
 	checkVndkOutput(t, ctx, "vndk/vndk.libraries.txt", []string{
 		"LLNDK: libc.so",
 		"LLNDK: libdl.so",
 		"LLNDK: libft2.so",
+		"LLNDK: libllndk.so",
 		"LLNDK: libm.so",
 		"VNDK-SP: libc++.so",
 		"VNDK-core: libvndk-private.so",
@@ -1257,6 +1266,95 @@
 	}
 }
 
+func TestVendorSnapshotDirected(t *testing.T) {
+	bp := `
+	cc_library_shared {
+		name: "libvendor",
+		vendor: true,
+		nocrt: true,
+	}
+
+	cc_library_shared {
+		name: "libvendor_available",
+		vendor_available: true,
+		nocrt: true,
+	}
+
+	genrule {
+		name: "libfoo_gen",
+		cmd: "",
+		out: ["libfoo.so"],
+	}
+
+	cc_prebuilt_library_shared {
+		name: "libfoo",
+		vendor: true,
+		prefer: true,
+		srcs: [":libfoo_gen"],
+	}
+
+	cc_library_shared {
+		name: "libfoo",
+		vendor: true,
+		nocrt: true,
+	}
+`
+	config := TestConfig(buildDir, android.Android, nil, bp, nil)
+	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
+	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+	config.TestProductVariables.DirectedVendorSnapshot = true
+	config.TestProductVariables.VendorSnapshotModules = make(map[string]bool)
+	config.TestProductVariables.VendorSnapshotModules["libvendor"] = true
+	config.TestProductVariables.VendorSnapshotModules["libfoo"] = true
+	ctx := testCcWithConfig(t, config)
+
+	// Check Vendor snapshot output.
+
+	snapshotDir := "vendor-snapshot"
+	snapshotVariantPath := filepath.Join(buildDir, snapshotDir, "arm64")
+	snapshotSingleton := ctx.SingletonForTests("vendor-snapshot")
+
+	var includeJsonFiles []string
+	var excludeJsonFiles []string
+
+	for _, arch := range [][]string{
+		[]string{"arm64", "armv8-a"},
+		[]string{"arm", "armv7-a-neon"},
+	} {
+		archType := arch[0]
+		archVariant := arch[1]
+		archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
+
+		sharedVariant := fmt.Sprintf("android_vendor.VER_%s_%s_shared", archType, archVariant)
+		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
+
+		// Included modules
+		checkSnapshot(t, ctx, snapshotSingleton, "libvendor", "libvendor.so", sharedDir, sharedVariant)
+		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libvendor.so.json"))
+		// Check that snapshot captures "prefer: true" prebuilt
+		checkSnapshot(t, ctx, snapshotSingleton, "prebuilt_libfoo", "libfoo.so", sharedDir, sharedVariant)
+		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libfoo.so.json"))
+
+		// Excluded modules
+		checkSnapshotExclude(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.so", sharedDir, sharedVariant)
+		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libvendor_available.so.json"))
+	}
+
+	// Verify that each json file for an included module has a rule.
+	for _, jsonFile := range includeJsonFiles {
+		if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
+			t.Errorf("include json file %q not found", jsonFile)
+		}
+	}
+
+	// Verify that each json file for an excluded module has no rule.
+	for _, jsonFile := range excludeJsonFiles {
+		if snapshotSingleton.MaybeOutput(jsonFile).Rule != nil {
+			t.Errorf("exclude json file %q found", jsonFile)
+		}
+	}
+}
+
 func TestVendorSnapshotUse(t *testing.T) {
 	frameworkBp := `
 	cc_library {
@@ -2013,64 +2111,6 @@
 		}
 	`)
 
-	// Check whether an error is emitted when a double_loadable lib depends on a non-double_loadable vendor_available lib.
-	testCcError(t, "module \".*\" variant \".*\": link.* \".*\" which is not LL-NDK, VNDK-SP, .*double_loadable", `
-		cc_library {
-			name: "libdoubleloadable",
-			vendor_available: true,
-			double_loadable: true,
-			shared_libs: ["libnondoubleloadable"],
-		}
-
-		cc_library {
-			name: "libnondoubleloadable",
-			vendor_available: true,
-		}
-	`)
-
-	// Check whether an error is emitted when a double_loadable lib depends on a non-double_loadable VNDK lib.
-	testCcError(t, "module \".*\" variant \".*\": link.* \".*\" which is not LL-NDK, VNDK-SP, .*double_loadable", `
-		cc_library {
-			name: "libdoubleloadable",
-			vendor_available: true,
-			double_loadable: true,
-			shared_libs: ["libnondoubleloadable"],
-		}
-
-		cc_library {
-			name: "libnondoubleloadable",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-			},
-		}
-	`)
-
-	// Check whether an error is emitted when a double_loadable VNDK depends on a non-double_loadable VNDK private lib.
-	testCcError(t, "module \".*\" variant \".*\": link.* \".*\" which is not LL-NDK, VNDK-SP, .*double_loadable", `
-		cc_library {
-			name: "libdoubleloadable",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-			},
-			double_loadable: true,
-			shared_libs: ["libnondoubleloadable"],
-		}
-
-		cc_library {
-			name: "libnondoubleloadable",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-				private: true,
-			},
-		}
-	`)
-
 	// Check whether an error is emitted when a LLNDK depends on a non-double_loadable indirectly.
 	testCcError(t, "module \".*\" variant \".*\": link.* \".*\" which is not LL-NDK, VNDK-SP, .*double_loadable", `
 		cc_library {
@@ -2095,6 +2135,29 @@
 			vendor_available: true,
 		}
 	`)
+
+	// The error is not from 'client' but from 'libllndk'
+	testCcError(t, "module \"libllndk\".* links a library \"libnondoubleloadable\".*double_loadable", `
+		cc_library {
+			name: "client",
+			vendor_available: true,
+			double_loadable: true,
+			shared_libs: ["libllndk"],
+		}
+		cc_library {
+			name: "libllndk",
+			shared_libs: ["libnondoubleloadable"],
+			llndk_stubs: "libllndk.llndk",
+		}
+		llndk_library {
+			name: "libllndk.llndk",
+			symbol_file: "",
+		}
+		cc_library {
+			name: "libnondoubleloadable",
+			vendor_available: true,
+		}
+	`)
 }
 
 func TestCheckVndkMembershipBeforeDoubleLoadable(t *testing.T) {
@@ -3154,7 +3217,25 @@
 			name: "libllndkprivate.llndk",
 			private: true,
 			symbol_file: "",
-		}`
+		}
+
+		llndk_libraries_txt {
+			name: "llndk.libraries.txt",
+		}
+		vndkcore_libraries_txt {
+			name: "vndkcore.libraries.txt",
+		}
+		vndksp_libraries_txt {
+			name: "vndksp.libraries.txt",
+		}
+		vndkprivate_libraries_txt {
+			name: "vndkprivate.libraries.txt",
+		}
+		vndkcorevariant_libraries_txt {
+			name: "vndkcorevariant.libraries.txt",
+			insert_vndk_version: false,
+		}
+	`
 
 	config := TestConfig(buildDir, android.Android, nil, bp, nil)
 	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
@@ -3162,14 +3243,14 @@
 	// native:vndk
 	ctx := testCcWithConfig(t, config)
 
-	assertMapKeys(t, vndkCoreLibraries(config),
-		[]string{"libvndk", "libvndkprivate"})
-	assertMapKeys(t, vndkSpLibraries(config),
-		[]string{"libc++", "libvndksp"})
-	assertMapKeys(t, llndkLibraries(config),
-		[]string{"libc", "libdl", "libft2", "libllndk", "libllndkprivate", "libm"})
-	assertMapKeys(t, vndkPrivateLibraries(config),
-		[]string{"libft2", "libllndkprivate", "libvndkprivate"})
+	checkVndkLibrariesOutput(t, ctx, "vndkcore.libraries.txt",
+		[]string{"libvndk.so", "libvndkprivate.so"})
+	checkVndkLibrariesOutput(t, ctx, "vndksp.libraries.txt",
+		[]string{"libc++.so", "libvndksp.so"})
+	checkVndkLibrariesOutput(t, ctx, "llndk.libraries.txt",
+		[]string{"libc.so", "libdl.so", "libft2.so", "libllndk.so", "libllndkprivate.so", "libm.so"})
+	checkVndkLibrariesOutput(t, ctx, "vndkprivate.libraries.txt",
+		[]string{"libft2.so", "libllndkprivate.so", "libvndkprivate.so"})
 
 	vendorVariant27 := "android_vendor.27_arm64_armv8-a_shared"
 
@@ -4461,3 +4542,138 @@
 		t.Errorf("aidl command %q does not contain %q", aidlCommand, expectedAidlFlag)
 	}
 }
+
+func checkHasImplicitDep(t *testing.T, m android.TestingModule, name string) {
+	implicits := m.Rule("ld").Implicits
+	for _, lib := range implicits {
+		if strings.Contains(lib.Rel(), name) {
+			return
+		}
+	}
+
+	t.Errorf("%q is not found in implicit deps of module %q", name, m.Module().(*Module).Name())
+}
+
+func checkDoesNotHaveImplicitDep(t *testing.T, m android.TestingModule, name string) {
+	implicits := m.Rule("ld").Implicits
+	for _, lib := range implicits {
+		if strings.Contains(lib.Rel(), name) {
+			t.Errorf("%q is found in implicit deps of module %q", name, m.Module().(*Module).Name())
+		}
+	}
+}
+
+func TestSanitizeMemtagHeap(t *testing.T) {
+	rootBp := `
+		cc_library_static {
+			name: "libstatic",
+			sanitize: { memtag_heap: true },
+		}
+
+		cc_library_shared {
+			name: "libshared",
+			sanitize: { memtag_heap: true },
+		}
+
+		cc_library {
+			name: "libboth",
+			sanitize: { memtag_heap: true },
+		}
+
+		cc_binary {
+			name: "binary",
+			shared_libs: [ "libshared" ],
+			static_libs: [ "libstatic" ],
+		}
+
+		cc_binary {
+			name: "binary_true",
+			sanitize: { memtag_heap: true },
+		}
+
+		cc_binary {
+			name: "binary_true_sync",
+			sanitize: { memtag_heap: true, diag: { memtag_heap: true }, },
+		}
+
+		cc_binary {
+			name: "binary_false",
+			sanitize: { memtag_heap: false },
+		}
+
+		cc_test {
+			name: "test",
+			gtest: false,
+		}
+
+		cc_test {
+			name: "test_true",
+			gtest: false,
+			sanitize: { memtag_heap: true },
+		}
+
+		cc_test {
+			name: "test_false",
+			gtest: false,
+			sanitize: { memtag_heap: false },
+		}
+
+		cc_test {
+			name: "test_true_async",
+			gtest: false,
+			sanitize: { memtag_heap: true, diag: { memtag_heap: false }  },
+		}
+
+		`
+
+	subdirAsyncBp := `
+		cc_binary {
+			name: "binary_async",
+		}
+		`
+
+	subdirSyncBp := `
+		cc_binary {
+			name: "binary_sync",
+		}
+		`
+
+	mockFS := map[string][]byte{
+		"subdir_async/Android.bp": []byte(subdirAsyncBp),
+		"subdir_sync/Android.bp":  []byte(subdirSyncBp),
+	}
+
+	config := TestConfig(buildDir, android.Android, nil, rootBp, mockFS)
+	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
+	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+	config.TestProductVariables.MemtagHeapAsyncIncludePaths = []string{"subdir_async"}
+	config.TestProductVariables.MemtagHeapSyncIncludePaths = []string{"subdir_sync"}
+	ctx := CreateTestContext(config)
+	ctx.Register()
+
+	_, errs := ctx.ParseFileList(".", []string{"Android.bp", "subdir_sync/Android.bp", "subdir_async/Android.bp"})
+	android.FailIfErrored(t, errs)
+	_, errs = ctx.PrepareBuildActions(config)
+	android.FailIfErrored(t, errs)
+
+	variant := "android_arm64_armv8-a"
+	note_async := "note_memtag_heap_async"
+	note_sync := "note_memtag_heap_sync"
+	note_any := "note_memtag_"
+
+	checkDoesNotHaveImplicitDep(t, ctx.ModuleForTests("libshared", "android_arm64_armv8-a_shared"), note_any)
+	checkDoesNotHaveImplicitDep(t, ctx.ModuleForTests("libboth", "android_arm64_armv8-a_shared"), note_any)
+
+	checkDoesNotHaveImplicitDep(t, ctx.ModuleForTests("binary", variant), note_any)
+	checkHasImplicitDep(t, ctx.ModuleForTests("binary_true", variant), note_async)
+	checkHasImplicitDep(t, ctx.ModuleForTests("binary_true_sync", variant), note_sync)
+	checkDoesNotHaveImplicitDep(t, ctx.ModuleForTests("binary_false", variant), note_any)
+
+	checkHasImplicitDep(t, ctx.ModuleForTests("test", variant), note_sync)
+	checkHasImplicitDep(t, ctx.ModuleForTests("test_true", variant), note_async)
+	checkDoesNotHaveImplicitDep(t, ctx.ModuleForTests("test_false", variant), note_any)
+	checkHasImplicitDep(t, ctx.ModuleForTests("test_true_async", variant), note_async)
+
+	checkHasImplicitDep(t, ctx.ModuleForTests("binary_async", variant), note_async)
+	checkHasImplicitDep(t, ctx.ModuleForTests("binary_sync", variant), note_sync)
+}
diff --git a/cc/config/global.go b/cc/config/global.go
index fa8e0fb..c34f936 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -90,9 +90,12 @@
 		"-Wl,--warn-shared-textrel",
 		"-Wl,--fatal-warnings",
 		"-Wl,--no-undefined-version",
+		// TODO: Eventually we should link against a libunwind.a with hidden symbols, and then these
+		// --exclude-libs arguments can be removed.
 		"-Wl,--exclude-libs,libgcc.a",
 		"-Wl,--exclude-libs,libgcc_stripped.a",
 		"-Wl,--exclude-libs,libunwind_llvm.a",
+		"-Wl,--exclude-libs,libunwind.a",
 	}
 
 	deviceGlobalLldflags = append(ClangFilterUnknownLldflags(deviceGlobalLdflags),
@@ -215,10 +218,6 @@
 			"frameworks/native/opengl/include",
 			"frameworks/av/include",
 		})
-	// This is used by non-NDK modules to get jni.h. export_include_dirs doesn't help
-	// with this, since there is no associated library.
-	pctx.PrefixedExistentPathsForSourcesVariable("CommonNativehelperInclude", "-I",
-		[]string{"libnativehelper/include_jni"})
 
 	pctx.SourcePathVariable("ClangDefaultBase", ClangDefaultBase)
 	pctx.VariableFunc("ClangBase", func(ctx android.PackageVarContext) string {
diff --git a/cc/config/tidy.go b/cc/config/tidy.go
index 4ac9e58..d5d01b4 100644
--- a/cc/config/tidy.go
+++ b/cc/config/tidy.go
@@ -20,25 +20,54 @@
 )
 
 func init() {
-	// Most Android source files are not clang-tidy clean yet.
-	// Global tidy checks include only google*, performance*,
-	// and misc-macro-parentheses, but not google-readability*
-	// or google-runtime-references.
+	// Many clang-tidy checks like altera-*, llvm-*, modernize-*
+	// are not designed for Android source code or creating too
+	// many (false-positive) warnings. The global default tidy checks
+	// should include only tested groups and exclude known noisy checks.
+	// See https://clang.llvm.org/extra/clang-tidy/checks/list.html
 	pctx.VariableFunc("TidyDefaultGlobalChecks", func(ctx android.PackageVarContext) string {
 		if override := ctx.Config().Getenv("DEFAULT_GLOBAL_TIDY_CHECKS"); override != "" {
 			return override
 		}
-		return strings.Join([]string{
+		checks := strings.Join([]string{
 			"-*",
-			"bugprone*",
+			"abseil-*",
+			"android-*",
+			"bugprone-*",
+			"cert-*",
 			"clang-diagnostic-unused-command-line-argument",
-			"google*",
-			"misc-macro-parentheses",
-			"performance*",
+			"google-*",
+			"misc-*",
+			"performance-*",
+			"portability-*",
 			"-bugprone-narrowing-conversions",
 			"-google-readability*",
 			"-google-runtime-references",
+			"-misc-no-recursion",
+			"-misc-non-private-member-variables-in-classes",
+			"-misc-unused-parameters",
+			// the following groups are excluded by -*
+			// -altera-*
+			// -cppcoreguidelines-*
+			// -darwin-*
+			// -fuchsia-*
+			// -hicpp-*
+			// -llvm-*
+			// -llvmlibc-*
+			// -modernize-*
+			// -mpi-*
+			// -objc-*
+			// -readability-*
+			// -zircon-*
 		}, ",")
+		// clang-analyzer-* checks are too slow to be in the default for WITH_TIDY=1.
+		// nightly builds add CLANG_ANALYZER_CHECKS=1 to run those checks.
+		// Some test code have clang-diagnostic-padded warnings that cannot be
+		// suppressed, but only by disabling clang-analyzer-optin.performance.*.
+		if ctx.Config().IsEnvTrue("CLANG_ANALYZER_CHECKS") {
+			checks += ",clang-analyzer-*,-clang-analyzer-optin.performance.*"
+		}
+		return checks
 	})
 
 	// There are too many clang-tidy warnings in external and vendor projects.
diff --git a/cc/config/vndk.go b/cc/config/vndk.go
index 4bcad4b..286bb53 100644
--- a/cc/config/vndk.go
+++ b/cc/config/vndk.go
@@ -18,14 +18,22 @@
 // For these libraries, the vendor variants must be installed even if the device
 // has VndkUseCoreVariant set.
 var VndkMustUseVendorVariantList = []string{
+	"android.hardware.authsecret-unstable-ndk_platform",
 	"android.hardware.automotive.occupant_awareness-ndk_platform",
+	"android.hardware.health.storage-ndk_platform",
+	"android.hardware.health.storage-unstable-ndk_platform",
 	"android.hardware.light-ndk_platform",
 	"android.hardware.identity-ndk_platform",
 	"android.hardware.nfc@1.2",
 	"android.hardware.memtrack-unstable-ndk_platform",
+	"android.hardware.oemlock-unstable-ndk_platform",
 	"android.hardware.power-ndk_platform",
 	"android.hardware.rebootescrow-ndk_platform",
 	"android.hardware.security.keymint-unstable-ndk_platform",
+	"android.hardware.security.secureclock-ndk_platform",
+	"android.hardware.security.secureclock-unstable-ndk_platform",
+	"android.hardware.security.sharedsecret-ndk_platform",
+	"android.hardware.security.sharedsecret-unstable-ndk_platform",
 	"android.hardware.vibrator-ndk_platform",
 	"android.system.keystore2-unstable-ndk_platform",
 	"libbinder",
diff --git a/cc/fuzz.go b/cc/fuzz.go
index 6b17c48..d7da5ab 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -41,6 +41,12 @@
 	// Specify who should be acknowledged for CVEs in the Android Security
 	// Bulletin.
 	Acknowledgement []string `json:"acknowledgement,omitempty"`
+	// Additional options to be passed to libfuzzer when run in Haiku.
+	Libfuzzer_options []string `json:"libfuzzer_options,omitempty"`
+	// Additional options to be passed to HWASAN when running on-device in Haiku.
+	Hwasan_options []string `json:"hwasan_options,omitempty"`
+	// Additional options to be passed to HWASAN when running on host in Haiku.
+	Asan_options []string `json:"asan_options,omitempty"`
 }
 
 func (f *FuzzConfig) String() string {
@@ -315,7 +321,7 @@
 	module, binary := NewBinary(hod)
 
 	binary.baseInstaller = NewFuzzInstaller()
-	module.sanitize.SetSanitizer(fuzzer, true)
+	module.sanitize.SetSanitizer(Fuzzer, true)
 
 	fuzz := &fuzzBinary{
 		binaryDecorator: binary,
diff --git a/cc/image.go b/cc/image.go
index 12bd65b..f89194f 100644
--- a/cc/image.go
+++ b/cc/image.go
@@ -26,36 +26,18 @@
 
 var _ android.ImageInterface = (*Module)(nil)
 
-type imageVariantType string
+type ImageVariantType string
 
 const (
-	coreImageVariant          imageVariantType = "core"
-	vendorImageVariant        imageVariantType = "vendor"
-	productImageVariant       imageVariantType = "product"
-	ramdiskImageVariant       imageVariantType = "ramdisk"
-	vendorRamdiskImageVariant imageVariantType = "vendor_ramdisk"
-	recoveryImageVariant      imageVariantType = "recovery"
-	hostImageVariant          imageVariantType = "host"
+	coreImageVariant          ImageVariantType = "core"
+	vendorImageVariant        ImageVariantType = "vendor"
+	productImageVariant       ImageVariantType = "product"
+	ramdiskImageVariant       ImageVariantType = "ramdisk"
+	vendorRamdiskImageVariant ImageVariantType = "vendor_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.InVendorRamdisk() {
-		return vendorRamdiskImageVariant
-	} else if c.InRecovery() {
-		return recoveryImageVariant
-	} else {
-		return coreImageVariant
-	}
-}
-
 const (
 	// VendorVariationPrefix is the variant prefix used for /vendor code that compiles
 	// against the VNDK.
@@ -75,7 +57,7 @@
 func (ctx *moduleContext) SocSpecific() bool {
 	// Additionally check if this module is inVendor() that means it is a "vendor" variant of a
 	// module. As well as SoC specific modules, vendor variants must be installed to /vendor.
-	return ctx.ModuleContext.SocSpecific() || ctx.mod.inVendor()
+	return ctx.ModuleContext.SocSpecific() || ctx.mod.InVendor()
 }
 
 func (ctx *moduleContextImpl) inProduct() bool {
@@ -83,7 +65,7 @@
 }
 
 func (ctx *moduleContextImpl) inVendor() bool {
-	return ctx.mod.inVendor()
+	return ctx.mod.InVendor()
 }
 
 func (ctx *moduleContextImpl) inRamdisk() bool {
@@ -119,7 +101,7 @@
 }
 
 // Returns true if the module is "vendor" variant. Usually these modules are installed in /vendor
-func (c *Module) inVendor() bool {
+func (c *Module) InVendor() bool {
 	return c.Properties.ImageVariationPrefix == VendorVariationPrefix
 }
 
@@ -282,31 +264,34 @@
 		productVndkVersion = platformVndkVersion
 	}
 
-	if boardVndkVersion == "" {
+	_, isLLNDKLibrary := m.linker.(*llndkStubDecorator)
+	_, isLLNDKHeaders := m.linker.(*llndkHeadersDecorator)
+	lib := moduleLibraryInterface(m)
+	hasLLNDKStubs := lib != nil && lib.hasLLNDKStubs()
+
+	if isLLNDKLibrary || isLLNDKHeaders || hasLLNDKStubs {
+		// This is an LLNDK library.  The implementation of the library will be on /system,
+		// and vendor and product variants will be created with LLNDK stubs.
+		// The LLNDK libraries need vendor variants even if there is no VNDK.
+		// The obsolete llndk_library and llndk_headers modules also need the vendor variants
+		// so the cc_library LLNDK stubs can depend on them.
+		if hasLLNDKStubs {
+			coreVariantNeeded = true
+		}
+		if platformVndkVersion != "" {
+			vendorVariants = append(vendorVariants, platformVndkVersion)
+			productVariants = append(productVariants, platformVndkVersion)
+		}
+		if boardVndkVersion != "" {
+			vendorVariants = append(vendorVariants, boardVndkVersion)
+		}
+		if productVndkVersion != "" {
+			productVariants = append(productVariants, productVndkVersion)
+		}
+	} else 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.
@@ -364,18 +349,6 @@
 		} else {
 			vendorVariants = append(vendorVariants, platformVndkVersion)
 		}
-	} else if lib := moduleLibraryInterface(m); lib != nil && lib.hasLLNDKStubs() {
-		// This is an LLNDK library.  The implementation of the library will be on /system,
-		// and vendor and product variants will be created with LLNDK stubs.
-		coreVariantNeeded = true
-		vendorVariants = append(vendorVariants,
-			platformVndkVersion,
-			boardVndkVersion,
-		)
-		productVariants = append(productVariants,
-			platformVndkVersion,
-			productVndkVersion,
-		)
 	} else {
 		// This is either in /system (or similar: /data), or is a
 		// modules built with the NDK. Modules built with the NDK
diff --git a/cc/library.go b/cc/library.go
index bc6ff69..af9aaca 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -1318,17 +1318,13 @@
 	if library.baseCompiler.hasSrcExt(".sysprop") {
 		dir := android.PathForModuleGen(ctx, "sysprop", "include")
 		if library.Properties.Sysprop.Platform != nil {
-			isClientProduct := ctx.ProductSpecific() && !ctx.useVndk()
-			isClientVendor := ctx.useVndk()
 			isOwnerPlatform := Bool(library.Properties.Sysprop.Platform)
 
 			// If the owner is different from the user, expose public header. That is,
 			// 1) if the user is product (as owner can only be platform / vendor)
-			// 2) if one is platform and the other is vendor
-			// Exceptions are ramdisk and recovery. They are not enforced at all. So
-			// they always use internal header.
-			if !ctx.inRamdisk() && !ctx.inVendorRamdisk() && !ctx.inRecovery() &&
-				(isClientProduct || (isOwnerPlatform == isClientVendor)) {
+			// 2) if the owner is platform and the client is vendor
+			// We don't care Platform -> Vendor dependency as it's already forbidden.
+			if ctx.Device() && (ctx.ProductSpecific() || (isOwnerPlatform && ctx.inVendor())) {
 				dir = android.PathForModuleGen(ctx, "sysprop/public", "include")
 			}
 		}
diff --git a/cc/linkable.go b/cc/linkable.go
index 489063f..ab5a552 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -6,6 +6,59 @@
 	"github.com/google/blueprint"
 )
 
+// PlatformSanitizeable is an interface for sanitizing platform modules.
+type PlatformSanitizeable interface {
+	LinkableInterface
+
+	// SanitizePropDefined returns whether the Sanitizer properties struct for this module is defined.
+	SanitizePropDefined() bool
+
+	// IsDependencyRoot returns whether a module is of a type which cannot be a linkage dependency
+	// of another module. For example, cc_binary and rust_binary represent dependency roots as other
+	// modules cannot have linkage dependencies against these types.
+	IsDependencyRoot() bool
+
+	// IsSanitizerEnabled returns whether a sanitizer is enabled.
+	IsSanitizerEnabled(t SanitizerType) bool
+
+	// IsSanitizerExplicitlyDisabled returns whether a sanitizer has been explicitly disabled (set to false) rather
+	// than left undefined.
+	IsSanitizerExplicitlyDisabled(t SanitizerType) bool
+
+	// SanitizeDep returns the value of the SanitizeDep flag, which is set if a module is a dependency of a
+	// sanitized module.
+	SanitizeDep() bool
+
+	// SetSanitizer enables or disables the specified sanitizer type if it's supported, otherwise this should panic.
+	SetSanitizer(t SanitizerType, b bool)
+
+	// SetSanitizerDep returns true if the module is statically linked.
+	SetSanitizeDep(b bool)
+
+	// StaticallyLinked returns true if the module is statically linked.
+	StaticallyLinked() bool
+
+	// SetInSanitizerDir sets the module installation to the sanitizer directory.
+	SetInSanitizerDir()
+
+	// SanitizeNever returns true if this module should never be sanitized.
+	SanitizeNever() bool
+
+	// SanitizerSupported returns true if a sanitizer type is supported by this modules compiler.
+	SanitizerSupported(t SanitizerType) bool
+
+	// SanitizableDepTagChecker returns a SantizableDependencyTagChecker function type.
+	SanitizableDepTagChecker() SantizableDependencyTagChecker
+}
+
+// SantizableDependencyTagChecker functions check whether or not a dependency
+// tag can be sanitized. These functions should return true if the tag can be
+// sanitized, otherwise they should return false. These functions should also
+// handle all possible dependency tags in the dependency tree. For example,
+// Rust modules can depend on both Rust and CC libraries, so the Rust module
+// implementation should handle tags from both.
+type SantizableDependencyTagChecker func(tag blueprint.DependencyTag) bool
+
 // LinkableInterface is an interface for a type of module that is linkable in a C++ library.
 type LinkableInterface interface {
 	android.Module
@@ -27,6 +80,8 @@
 	SetShared()
 	Static() bool
 	Shared() bool
+	Header() bool
+	IsPrebuilt() bool
 	Toc() android.OptionalPath
 
 	Host() bool
@@ -40,6 +95,8 @@
 	InRecovery() bool
 	OnlyInRecovery() bool
 
+	InVendor() bool
+
 	UseSdk() bool
 	UseVndk() bool
 	MustUseVendorVariant() bool
@@ -56,6 +113,11 @@
 	IsSdkVariant() bool
 
 	SplitPerApiLevel() bool
+
+	// SetPreventInstall sets the PreventInstall property to 'true' for this module.
+	SetPreventInstall()
+	// SetHideFromMake sets the HideFromMake property to 'true' for this module.
+	SetHideFromMake()
 }
 
 var (
@@ -67,6 +129,26 @@
 	CoverageDepTag = dependencyTag{name: "coverage"}
 )
 
+// GetImageVariantType returns the ImageVariantType string value for the given module
+// (these are defined in cc/image.go).
+func GetImageVariantType(c LinkableInterface) 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.InVendorRamdisk() {
+		return vendorRamdiskImageVariant
+	} else if c.InRecovery() {
+		return recoveryImageVariant
+	} else {
+		return coreImageVariant
+	}
+}
+
 // SharedDepTag returns the dependency tag for any C++ shared libraries.
 func SharedDepTag() blueprint.DependencyTag {
 	return libraryDependencyTag{Kind: sharedLibraryDependency}
diff --git a/cc/llndk_library.go b/cc/llndk_library.go
index bd48501..a46b31c 100644
--- a/cc/llndk_library.go
+++ b/cc/llndk_library.go
@@ -161,6 +161,13 @@
 	*libraryDecorator
 }
 
+func (llndk *llndkHeadersDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps {
+	deps.HeaderLibs = append(deps.HeaderLibs, llndk.Properties.Llndk.Export_llndk_headers...)
+	deps.ReexportHeaderLibHeaders = append(deps.ReexportHeaderLibHeaders,
+		llndk.Properties.Llndk.Export_llndk_headers...)
+	return deps
+}
+
 // llndk_headers contains a set of c/c++ llndk headers files which are imported
 // by other soongs cc modules.
 func llndkHeadersFactory() android.Module {
@@ -178,11 +185,6 @@
 	module.installer = nil
 	module.library = decorator
 
-	module.AddProperties(
-		&module.Properties,
-		&library.MutatedProperties,
-		&library.flagExporter.Properties)
-
 	module.Init()
 
 	return module
diff --git a/cc/makevars.go b/cc/makevars.go
index 8301c6b..48d5636 100644
--- a/cc/makevars.go
+++ b/cc/makevars.go
@@ -154,16 +154,6 @@
 	ctx.Strict("SOONG_STRIP_PATH", "${stripPath}")
 	ctx.Strict("XZ", "${xzCmd}")
 
-	nativeHelperIncludeFlags, err := ctx.Eval("${config.CommonNativehelperInclude}")
-	if err != nil {
-		panic(err)
-	}
-	nativeHelperIncludes, nativeHelperSystemIncludes := splitSystemIncludes(ctx, nativeHelperIncludeFlags)
-	if len(nativeHelperSystemIncludes) > 0 {
-		panic("native helper may not have any system includes")
-	}
-	ctx.Strict("JNI_H_INCLUDE", strings.Join(nativeHelperIncludes, " "))
-
 	includeFlags, err := ctx.Eval("${config.CommonGlobalIncludes}")
 	if err != nil {
 		panic(err)
diff --git a/cc/sanitize.go b/cc/sanitize.go
index bb92a88..af17490 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -71,7 +71,7 @@
 		"export_memory_stats=0", "max_malloc_fill_size=0"}
 )
 
-type sanitizerType int
+type SanitizerType int
 
 func boolPtr(v bool) *bool {
 	if v {
@@ -82,19 +82,20 @@
 }
 
 const (
-	asan sanitizerType = iota + 1
+	Asan SanitizerType = iota + 1
 	hwasan
 	tsan
 	intOverflow
 	cfi
 	scs
-	fuzzer
+	Fuzzer
+	memtag_heap
 )
 
 // Name of the sanitizer variation for this sanitizer type
-func (t sanitizerType) variationName() string {
+func (t SanitizerType) variationName() string {
 	switch t {
-	case asan:
+	case Asan:
 		return "asan"
 	case hwasan:
 		return "hwasan"
@@ -106,20 +107,24 @@
 		return "cfi"
 	case scs:
 		return "scs"
-	case fuzzer:
+	case memtag_heap:
+		return "memtag_heap"
+	case Fuzzer:
 		return "fuzzer"
 	default:
-		panic(fmt.Errorf("unknown sanitizerType %d", t))
+		panic(fmt.Errorf("unknown SanitizerType %d", t))
 	}
 }
 
 // This is the sanitizer names in SANITIZE_[TARGET|HOST]
-func (t sanitizerType) name() string {
+func (t SanitizerType) name() string {
 	switch t {
-	case asan:
+	case Asan:
 		return "address"
 	case hwasan:
 		return "hwaddress"
+	case memtag_heap:
+		return "memtag_heap"
 	case tsan:
 		return "thread"
 	case intOverflow:
@@ -128,15 +133,37 @@
 		return "cfi"
 	case scs:
 		return "shadow-call-stack"
-	case fuzzer:
+	case Fuzzer:
 		return "fuzzer"
 	default:
-		panic(fmt.Errorf("unknown sanitizerType %d", t))
+		panic(fmt.Errorf("unknown SanitizerType %d", t))
 	}
 }
 
-func (t sanitizerType) incompatibleWithCfi() bool {
-	return t == asan || t == fuzzer || t == hwasan
+func (*Module) SanitizerSupported(t SanitizerType) bool {
+	switch t {
+	case Asan:
+		return true
+	case hwasan:
+		return true
+	case tsan:
+		return true
+	case intOverflow:
+		return true
+	case cfi:
+		return true
+	case scs:
+		return true
+	case Fuzzer:
+		return true
+	default:
+		return false
+	}
+}
+
+// incompatibleWithCfi returns true if a sanitizer is incompatible with CFI.
+func (t SanitizerType) incompatibleWithCfi() bool {
+	return t == Asan || t == Fuzzer || t == hwasan
 }
 
 type SanitizeUserProps struct {
@@ -157,6 +184,7 @@
 	Integer_overflow *bool    `android:"arch_variant"`
 	Scudo            *bool    `android:"arch_variant"`
 	Scs              *bool    `android:"arch_variant"`
+	Memtag_heap      *bool    `android:"arch_variant"`
 
 	// A modifier for ASAN and HWASAN for write only instrumentation
 	Writeonly *bool `android:"arch_variant"`
@@ -168,6 +196,7 @@
 		Undefined        *bool    `android:"arch_variant"`
 		Cfi              *bool    `android:"arch_variant"`
 		Integer_overflow *bool    `android:"arch_variant"`
+		Memtag_heap      *bool    `android:"arch_variant"`
 		Misc_undefined   []string `android:"arch_variant"`
 		No_recover       []string `android:"arch_variant"`
 	} `android:"arch_variant"`
@@ -308,6 +337,11 @@
 			}
 			s.Writeonly = boolPtr(true)
 		}
+		if found, globalSanitizers = removeFromList("memtag_heap", globalSanitizers); found && s.Memtag_heap == nil {
+			if !ctx.Config().MemtagHeapDisabledForPath(ctx.ModuleDir()) {
+				s.Memtag_heap = boolPtr(true)
+			}
+		}
 
 		if len(globalSanitizers) > 0 {
 			ctx.ModuleErrorf("unknown global sanitizer option %s", globalSanitizers[0])
@@ -329,6 +363,25 @@
 		}
 	}
 
+	// cc_test targets default to SYNC MemTag.
+	if ctx.testBinary() && s.Memtag_heap == nil {
+		if !ctx.Config().MemtagHeapDisabledForPath(ctx.ModuleDir()) {
+			s.Memtag_heap = boolPtr(true)
+			s.Diag.Memtag_heap = boolPtr(true)
+		}
+	}
+
+	// Enable Memtag for all components in the include paths (for Aarch64 only)
+	if s.Memtag_heap == nil && ctx.Arch().ArchType == android.Arm64 {
+		if ctx.Config().MemtagHeapSyncEnabledForPath(ctx.ModuleDir()) {
+			s.Memtag_heap = boolPtr(true)
+			s.Diag.Memtag_heap = boolPtr(true)
+		} else if ctx.Config().MemtagHeapAsyncEnabledForPath(ctx.ModuleDir()) {
+			s.Memtag_heap = boolPtr(true)
+			s.Diag.Memtag_heap = boolPtr(false)
+		}
+	}
+
 	// Enable CFI for all components in the include paths (for Aarch64 only)
 	if s.Cfi == nil && ctx.Config().CFIEnabledForPath(ctx.ModuleDir()) && ctx.Arch().ArchType == android.Arm64 {
 		s.Cfi = boolPtr(true)
@@ -359,6 +412,11 @@
 		s.Scs = nil
 	}
 
+	// memtag_heap is only implemented on AArch64.
+	if ctx.Arch().ArchType != android.Arm64 {
+		s.Memtag_heap = nil
+	}
+
 	// Also disable CFI if ASAN is enabled.
 	if Bool(s.Address) || Bool(s.Hwaddress) {
 		s.Cfi = boolPtr(false)
@@ -413,7 +471,7 @@
 
 	if ctx.Os() != android.Windows && (Bool(s.All_undefined) || Bool(s.Undefined) || Bool(s.Address) || Bool(s.Thread) ||
 		Bool(s.Fuzzer) || Bool(s.Safestack) || Bool(s.Cfi) || Bool(s.Integer_overflow) || len(s.Misc_undefined) > 0 ||
-		Bool(s.Scudo) || Bool(s.Hwaddress) || Bool(s.Scs)) {
+		Bool(s.Scudo) || Bool(s.Hwaddress) || Bool(s.Scs) || Bool(s.Memtag_heap)) {
 		sanitize.Properties.SanitizerEnabled = true
 	}
 
@@ -680,9 +738,10 @@
 	return sanitize.Properties.InSanitizerDir
 }
 
-func (sanitize *sanitize) getSanitizerBoolPtr(t sanitizerType) *bool {
+// getSanitizerBoolPtr returns the SanitizerTypes associated bool pointer from SanitizeProperties.
+func (sanitize *sanitize) getSanitizerBoolPtr(t SanitizerType) *bool {
 	switch t {
-	case asan:
+	case Asan:
 		return sanitize.Properties.Sanitize.Address
 	case hwasan:
 		return sanitize.Properties.Sanitize.Hwaddress
@@ -694,32 +753,37 @@
 		return sanitize.Properties.Sanitize.Cfi
 	case scs:
 		return sanitize.Properties.Sanitize.Scs
-	case fuzzer:
+	case memtag_heap:
+		return sanitize.Properties.Sanitize.Memtag_heap
+	case Fuzzer:
 		return sanitize.Properties.Sanitize.Fuzzer
 	default:
-		panic(fmt.Errorf("unknown sanitizerType %d", t))
+		panic(fmt.Errorf("unknown SanitizerType %d", t))
 	}
 }
 
+// isUnsanitizedVariant returns true if no sanitizers are enabled.
 func (sanitize *sanitize) isUnsanitizedVariant() bool {
-	return !sanitize.isSanitizerEnabled(asan) &&
+	return !sanitize.isSanitizerEnabled(Asan) &&
 		!sanitize.isSanitizerEnabled(hwasan) &&
 		!sanitize.isSanitizerEnabled(tsan) &&
 		!sanitize.isSanitizerEnabled(cfi) &&
 		!sanitize.isSanitizerEnabled(scs) &&
-		!sanitize.isSanitizerEnabled(fuzzer)
+		!sanitize.isSanitizerEnabled(memtag_heap) &&
+		!sanitize.isSanitizerEnabled(Fuzzer)
 }
 
+// isVariantOnProductionDevice returns true if variant is for production devices (no non-production sanitizers enabled).
 func (sanitize *sanitize) isVariantOnProductionDevice() bool {
-	return !sanitize.isSanitizerEnabled(asan) &&
+	return !sanitize.isSanitizerEnabled(Asan) &&
 		!sanitize.isSanitizerEnabled(hwasan) &&
 		!sanitize.isSanitizerEnabled(tsan) &&
-		!sanitize.isSanitizerEnabled(fuzzer)
+		!sanitize.isSanitizerEnabled(Fuzzer)
 }
 
-func (sanitize *sanitize) SetSanitizer(t sanitizerType, b bool) {
+func (sanitize *sanitize) SetSanitizer(t SanitizerType, b bool) {
 	switch t {
-	case asan:
+	case Asan:
 		sanitize.Properties.Sanitize.Address = boolPtr(b)
 	case hwasan:
 		sanitize.Properties.Sanitize.Hwaddress = boolPtr(b)
@@ -731,10 +795,12 @@
 		sanitize.Properties.Sanitize.Cfi = boolPtr(b)
 	case scs:
 		sanitize.Properties.Sanitize.Scs = boolPtr(b)
-	case fuzzer:
+	case memtag_heap:
+		sanitize.Properties.Sanitize.Memtag_heap = boolPtr(b)
+	case Fuzzer:
 		sanitize.Properties.Sanitize.Fuzzer = boolPtr(b)
 	default:
-		panic(fmt.Errorf("unknown sanitizerType %d", t))
+		panic(fmt.Errorf("unknown SanitizerType %d", t))
 	}
 	if b {
 		sanitize.Properties.SanitizerEnabled = true
@@ -743,7 +809,7 @@
 
 // Check if the sanitizer is explicitly disabled (as opposed to nil by
 // virtue of not being set).
-func (sanitize *sanitize) isSanitizerExplicitlyDisabled(t sanitizerType) bool {
+func (sanitize *sanitize) isSanitizerExplicitlyDisabled(t SanitizerType) bool {
 	if sanitize == nil {
 		return false
 	}
@@ -757,7 +823,7 @@
 // indirectly (via a mutator) sets the bool ptr to true, and you can't
 // distinguish between the cases. It isn't needed though - both cases can be
 // treated identically.
-func (sanitize *sanitize) isSanitizerEnabled(t sanitizerType) bool {
+func (sanitize *sanitize) isSanitizerEnabled(t SanitizerType) bool {
 	if sanitize == nil {
 		return false
 	}
@@ -766,7 +832,8 @@
 	return sanitizerVal != nil && *sanitizerVal == true
 }
 
-func isSanitizableDependencyTag(tag blueprint.DependencyTag) bool {
+// IsSanitizableDependencyTag returns true if the dependency tag is sanitizable.
+func IsSanitizableDependencyTag(tag blueprint.DependencyTag) bool {
 	switch t := tag.(type) {
 	case dependencyTag:
 		return t == reuseObjTag || t == objDepTag
@@ -777,6 +844,10 @@
 	}
 }
 
+func (m *Module) SanitizableDepTagChecker() SantizableDependencyTagChecker {
+	return IsSanitizableDependencyTag
+}
+
 // 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.
@@ -785,51 +856,58 @@
 		return false
 	}
 
-	c := mctx.Module().(*Module)
+	c := mctx.Module().(PlatformSanitizeable)
 
-	if !c.inVendor() {
+	if !c.InVendor() {
 		return false
 	}
 
-	if !c.static() {
+	if !c.StaticallyLinked() {
 		return false
 	}
 
-	if c.Prebuilt() != nil {
+	if c.IsPrebuilt() {
 		return false
 	}
 
-	return c.sanitize != nil &&
-		!Bool(c.sanitize.Properties.Sanitize.Never) &&
-		!c.sanitize.isSanitizerExplicitlyDisabled(cfi)
+	if !c.SanitizerSupported(cfi) {
+		return false
+	}
+
+	return c.SanitizePropDefined() &&
+		!c.SanitizeNever() &&
+		!c.IsSanitizerExplicitlyDisabled(cfi)
 }
 
 // Propagate sanitizer requirements down from binaries
-func sanitizerDepsMutator(t sanitizerType) func(android.TopDownMutatorContext) {
+func sanitizerDepsMutator(t SanitizerType) func(android.TopDownMutatorContext) {
 	return func(mctx android.TopDownMutatorContext) {
-		if c, ok := mctx.Module().(*Module); ok {
-			enabled := c.sanitize.isSanitizerEnabled(t)
+		if c, ok := mctx.Module().(PlatformSanitizeable); ok {
+			enabled := c.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
+				c.SetSanitizeDep(true)
 			}
 			if enabled {
+				isSanitizableDependencyTag := c.SanitizableDepTagChecker()
 				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 d, ok := child.(PlatformSanitizeable); ok && d.SanitizePropDefined() &&
+						!d.SanitizeNever() &&
+						!d.IsSanitizerExplicitlyDisabled(t) {
 						if t == cfi || t == hwasan || t == scs {
-							if d.static() {
-								d.sanitize.Properties.SanitizeDep = true
+							if d.StaticallyLinked() && d.SanitizerSupported(t) {
+								// Rust does not support some of these sanitizers, so we need to check if it's
+								// supported before setting this true.
+								d.SetSanitizeDep(true)
 							}
 						} else {
-							d.sanitize.Properties.SanitizeDep = true
+							d.SetSanitizeDep(true)
 						}
 					}
 					return true
@@ -847,9 +925,19 @@
 	}
 }
 
+func (c *Module) SanitizeNever() bool {
+	return Bool(c.sanitize.Properties.Sanitize.Never)
+}
+
+func (c *Module) IsSanitizerExplicitlyDisabled(t SanitizerType) bool {
+	return c.sanitize.isSanitizerExplicitlyDisabled(t)
+}
+
 // Propagate the ubsan minimal runtime dependency when there are integer overflow sanitized static dependencies.
 func sanitizerRuntimeDepsMutator(mctx android.TopDownMutatorContext) {
+	// Change this to PlatformSanitizable when/if non-cc modules support ubsan sanitizers.
 	if c, ok := mctx.Module().(*Module); ok && c.sanitize != nil {
+		isSanitizableDependencyTag := c.SanitizableDepTagChecker()
 		mctx.WalkDeps(func(child, parent android.Module) bool {
 			if !isSanitizableDependencyTag(mctx.OtherModuleDependencyTag(child)) {
 				return false
@@ -985,6 +1073,20 @@
 			sanitizers = append(sanitizers, "shadow-call-stack")
 		}
 
+		if Bool(c.sanitize.Properties.Sanitize.Memtag_heap) && c.binary() {
+			noteDep := "note_memtag_heap_async"
+			if Bool(c.sanitize.Properties.Sanitize.Diag.Memtag_heap) {
+				noteDep = "note_memtag_heap_sync"
+			}
+			depTag := libraryDependencyTag{Kind: staticLibraryDependency, wholeStatic: true}
+			variations := append(mctx.Target().Variations(),
+				blueprint.Variation{Mutator: "link", Variation: "static"})
+			if c.Device() {
+				variations = append(variations, c.ImageVariation())
+			}
+			mctx.AddFarVariationDependencies(variations, depTag, noteDep)
+		}
+
 		if Bool(c.sanitize.Properties.Sanitize.Fuzzer) {
 			sanitizers = append(sanitizers, "fuzzer-no-link")
 		}
@@ -1057,7 +1159,7 @@
 					variations = append(variations, c.ImageVariation())
 				}
 				mctx.AddFarVariationDependencies(variations, depTag, deps...)
-			} else if !c.static() && !c.header() {
+			} else if !c.static() && !c.Header() {
 				// If we're using snapshots and in vendor, redirect to snapshot whenever possible
 				if c.VndkVersion() == mctx.DeviceConfig().VndkVersion() {
 					snapshots := vendorSnapshotSharedLibs(mctx.Config())
@@ -1098,16 +1200,52 @@
 	AddSanitizerDependencies(ctx android.BottomUpMutatorContext, sanitizerName string)
 }
 
+func (c *Module) SanitizePropDefined() bool {
+	return c.sanitize != nil
+}
+
+func (c *Module) IsSanitizerEnabled(t SanitizerType) bool {
+	return c.sanitize.isSanitizerEnabled(t)
+}
+
+func (c *Module) SanitizeDep() bool {
+	return c.sanitize.Properties.SanitizeDep
+}
+
+func (c *Module) StaticallyLinked() bool {
+	return c.static()
+}
+
+func (c *Module) SetInSanitizerDir() {
+	if c.sanitize != nil {
+		c.sanitize.Properties.InSanitizerDir = true
+	}
+}
+
+func (c *Module) SetSanitizer(t SanitizerType, b bool) {
+	if c.sanitize != nil {
+		c.sanitize.SetSanitizer(t, b)
+	}
+}
+
+func (c *Module) SetSanitizeDep(b bool) {
+	if c.sanitize != nil {
+		c.sanitize.Properties.SanitizeDep = b
+	}
+}
+
+var _ PlatformSanitizeable = (*Module)(nil)
+
 // Create sanitized variants for modules that need them
-func sanitizerMutator(t sanitizerType) func(android.BottomUpMutatorContext) {
+func sanitizerMutator(t SanitizerType) func(android.BottomUpMutatorContext) {
 	return func(mctx android.BottomUpMutatorContext) {
-		if c, ok := mctx.Module().(*Module); ok && c.sanitize != nil {
-			if c.isDependencyRoot() && c.sanitize.isSanitizerEnabled(t) {
+		if c, ok := mctx.Module().(PlatformSanitizeable); ok && c.SanitizePropDefined() {
+			if c.IsDependencyRoot() && c.IsSanitizerEnabled(t) {
 				modules := mctx.CreateVariations(t.variationName())
-				modules[0].(*Module).sanitize.SetSanitizer(t, true)
-			} else if c.sanitize.isSanitizerEnabled(t) || c.sanitize.Properties.SanitizeDep {
-				isSanitizerEnabled := c.sanitize.isSanitizerEnabled(t)
-				if c.static() || c.header() || t == asan || t == fuzzer {
+				modules[0].(PlatformSanitizeable).SetSanitizer(t, true)
+			} else if c.IsSanitizerEnabled(t) || c.SanitizeDep() {
+				isSanitizerEnabled := c.IsSanitizerEnabled(t)
+				if c.StaticallyLinked() || c.Header() || t == Asan || t == Fuzzer {
 					// Static and header libs are split into non-sanitized and sanitized variants.
 					// Shared libs are not split. However, for asan and fuzzer, we split even for shared
 					// libs because a library sanitized for asan/fuzzer can't be linked from a library
@@ -1121,17 +1259,20 @@
 					// module. By setting it to the name of the sanitized variation, the dangling dependency
 					// is redirected to the sanitized variant of the dependent module.
 					defaultVariation := t.variationName()
+					// Not all PlatformSanitizeable modules support the CFI sanitizer
+					cfiSupported := mctx.Module().(PlatformSanitizeable).SanitizerSupported(cfi)
 					mctx.SetDefaultDependencyVariation(&defaultVariation)
-					modules := mctx.CreateVariations("", t.variationName())
-					modules[0].(*Module).sanitize.SetSanitizer(t, false)
-					modules[1].(*Module).sanitize.SetSanitizer(t, true)
-					modules[0].(*Module).sanitize.Properties.SanitizeDep = false
-					modules[1].(*Module).sanitize.Properties.SanitizeDep = false
 
-					if mctx.Device() && t.incompatibleWithCfi() {
+					modules := mctx.CreateVariations("", t.variationName())
+					modules[0].(PlatformSanitizeable).SetSanitizer(t, false)
+					modules[1].(PlatformSanitizeable).SetSanitizer(t, true)
+					modules[0].(PlatformSanitizeable).SetSanitizeDep(false)
+					modules[1].(PlatformSanitizeable).SetSanitizeDep(false)
+
+					if mctx.Device() && t.incompatibleWithCfi() && cfiSupported {
 						// TODO: Make sure that cfi mutator runs "after" any of the sanitizers that
 						// are incompatible with cfi
-						modules[1].(*Module).sanitize.SetSanitizer(cfi, false)
+						modules[1].(PlatformSanitizeable).SetSanitizer(cfi, false)
 					}
 
 					// For cfi/scs/hwasan, we can export both sanitized and un-sanitized variants
@@ -1139,46 +1280,48 @@
 					// For other types of sanitizers, suppress the variation that is disabled.
 					if t != cfi && t != scs && t != hwasan {
 						if isSanitizerEnabled {
-							modules[0].(*Module).Properties.PreventInstall = true
-							modules[0].(*Module).Properties.HideFromMake = true
+							modules[0].(PlatformSanitizeable).SetPreventInstall()
+							modules[0].(PlatformSanitizeable).SetHideFromMake()
 						} else {
-							modules[1].(*Module).Properties.PreventInstall = true
-							modules[1].(*Module).Properties.HideFromMake = true
+							modules[1].(PlatformSanitizeable).SetPreventInstall()
+							modules[1].(PlatformSanitizeable).SetHideFromMake()
 						}
 					}
 
 					// Export the static lib name to make
-					if c.static() && c.ExportedToMake() {
+					if c.StaticallyLinked() && c.ExportedToMake() {
 						if t == cfi {
-							cfiStaticLibs(mctx.Config()).add(c, c.Name())
+							cfiStaticLibs(mctx.Config()).add(c, c.Module().Name())
 						} else if t == hwasan {
-							hwasanStaticLibs(mctx.Config()).add(c, c.Name())
+							hwasanStaticLibs(mctx.Config()).add(c, c.Module().Name())
 						}
 					}
 				} else {
 					// Shared libs are not split. Only the sanitized variant is created.
 					modules := mctx.CreateVariations(t.variationName())
-					modules[0].(*Module).sanitize.SetSanitizer(t, true)
-					modules[0].(*Module).sanitize.Properties.SanitizeDep = false
+					modules[0].(PlatformSanitizeable).SetSanitizer(t, true)
+					modules[0].(PlatformSanitizeable).SetSanitizeDep(false)
 
 					// locate the asan libraries under /data/asan
-					if mctx.Device() && t == asan && isSanitizerEnabled {
-						modules[0].(*Module).sanitize.Properties.InSanitizerDir = true
+					if mctx.Device() && t == Asan && isSanitizerEnabled {
+						modules[0].(PlatformSanitizeable).SetInSanitizerDir()
 					}
 
 					if mctx.Device() && t.incompatibleWithCfi() {
 						// TODO: Make sure that cfi mutator runs "after" any of the sanitizers that
 						// are incompatible with cfi
-						modules[0].(*Module).sanitize.SetSanitizer(cfi, false)
+						modules[0].(PlatformSanitizeable).SetSanitizer(cfi, false)
 					}
 				}
 			}
-			c.sanitize.Properties.SanitizeDep = false
+			c.SetSanitizeDep(false)
 		} else if sanitizeable, ok := mctx.Module().(Sanitizeable); ok && sanitizeable.IsSanitizerEnabled(mctx, t.name()) {
 			// APEX modules fall here
 			sanitizeable.AddSanitizerDependencies(mctx, t.name())
 			mctx.CreateVariations(t.variationName())
 		} else if c, ok := mctx.Module().(*Module); ok {
+			//TODO: When Rust modules have vendor support, enable this path for PlatformSanitizeable
+
 			// Check if it's a snapshot module supporting sanitizer
 			if s, ok := c.linker.(snapshotSanitizer); ok && s.isSanitizerEnabled(t) {
 				// Set default variation as above.
@@ -1203,23 +1346,23 @@
 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
+	libsMap       map[ImageVariantType]map[string][]string
 	libsMapLock   sync.Mutex
-	sanitizerType sanitizerType
+	sanitizerType SanitizerType
 }
 
-func newSanitizerStaticLibsMap(t sanitizerType) *sanitizerStaticLibsMap {
+func newSanitizerStaticLibsMap(t SanitizerType) *sanitizerStaticLibsMap {
 	return &sanitizerStaticLibsMap{
 		sanitizerType: t,
-		libsMap:       make(map[imageVariantType]map[string][]string),
+		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()
+func (s *sanitizerStaticLibsMap) add(c LinkableInterface, name string) {
+	image := GetImageVariantType(c)
+	arch := c.Module().Target().Arch.ArchType.String()
 
 	s.libsMapLock.Lock()
 	defer s.libsMapLock.Unlock()
@@ -1238,7 +1381,7 @@
 // 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)]
+		archMap := s.libsMap[ImageVariantType(image)]
 		for _, arch := range android.SortedStringKeys(archMap) {
 			libs := archMap[arch]
 			sort.Strings(libs)
diff --git a/cc/snapshot_prebuilt.go b/cc/snapshot_prebuilt.go
index e3f3a4d..d9c46ea 100644
--- a/cc/snapshot_prebuilt.go
+++ b/cc/snapshot_prebuilt.go
@@ -82,6 +82,11 @@
 
 	// Whether to skip the source mutator for a given module.
 	skipSourceMutator(ctx android.BottomUpMutatorContext) bool
+
+	// Whether to exclude a given module from the directed snapshot or not.
+	// If the makefile variable DIRECTED_{IMAGE}_SNAPSHOT is true, directed snapshot is turned on,
+	// and only modules listed in {IMAGE}_SNAPSHOT_MODULES will be captured.
+	excludeFromDirectedSnapshot(cfg android.DeviceConfig, name string) bool
 }
 
 type vendorSnapshotImage struct{}
@@ -104,7 +109,7 @@
 }
 
 func (vendorSnapshotImage) inImage(m *Module) func() bool {
-	return m.inVendor
+	return m.InVendor
 }
 
 func (vendorSnapshotImage) available(m *Module) *bool {
@@ -193,6 +198,16 @@
 	return false
 }
 
+// returns true iff a given module SHOULD BE EXCLUDED, false if included
+func (vendorSnapshotImage) excludeFromDirectedSnapshot(cfg android.DeviceConfig, name string) bool {
+	// If we're using full snapshot, not directed snapshot, capture every module
+	if !cfg.DirectedVendorSnapshot() {
+		return false
+	}
+	// Else, checks if name is in VENDOR_SNAPSHOT_MODULES.
+	return !cfg.VendorSnapshotModules()[name]
+}
+
 func (recoverySnapshotImage) init() {
 	android.RegisterSingletonType("recovery-snapshot", RecoverySnapshotSingleton)
 	android.RegisterModuleType("recovery_snapshot_shared", RecoverySnapshotSharedFactory)
@@ -275,6 +290,11 @@
 	return !ok || !module.InRecovery()
 }
 
+func (recoverySnapshotImage) excludeFromDirectedSnapshot(cfg android.DeviceConfig, name string) bool {
+	// directed recovery snapshot is not implemented yet
+	return false
+}
+
 var vendorSnapshotImageSingleton vendorSnapshotImage
 var recoverySnapshotImageSingleton recoverySnapshotImage
 
@@ -507,8 +527,8 @@
 }
 
 type snapshotSanitizer interface {
-	isSanitizerEnabled(t sanitizerType) bool
-	setSanitizerVariation(t sanitizerType, enabled bool)
+	isSanitizerEnabled(t SanitizerType) bool
+	setSanitizerVariation(t SanitizerType, enabled bool)
 }
 
 type snapshotLibraryDecorator struct {
@@ -546,7 +566,7 @@
 func (p *snapshotLibraryDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps, objs Objects) android.Path {
 	m := ctx.Module().(*Module)
 
-	if m.inVendor() && vendorSuffixModules(ctx.Config())[m.BaseModuleName()] {
+	if m.InVendor() && vendorSuffixModules(ctx.Config())[m.BaseModuleName()] {
 		p.androidMkSuffix = vendorSuffix
 	} else if m.InRecovery() && recoverySuffixModules(ctx.Config())[m.BaseModuleName()] {
 		p.androidMkSuffix = recoverySuffix
@@ -613,7 +633,7 @@
 	return false
 }
 
-func (p *snapshotLibraryDecorator) isSanitizerEnabled(t sanitizerType) bool {
+func (p *snapshotLibraryDecorator) isSanitizerEnabled(t SanitizerType) bool {
 	switch t {
 	case cfi:
 		return p.sanitizerProperties.Cfi.Src != nil
@@ -622,7 +642,7 @@
 	}
 }
 
-func (p *snapshotLibraryDecorator) setSanitizerVariation(t sanitizerType, enabled bool) {
+func (p *snapshotLibraryDecorator) setSanitizerVariation(t SanitizerType, enabled bool) {
 	if !enabled {
 		return
 	}
@@ -769,7 +789,7 @@
 	binName := in.Base()
 
 	m := ctx.Module().(*Module)
-	if m.inVendor() && vendorSuffixModules(ctx.Config())[m.BaseModuleName()] {
+	if m.InVendor() && vendorSuffixModules(ctx.Config())[m.BaseModuleName()] {
 		p.androidMkSuffix = vendorSuffix
 	} else if m.InRecovery() && recoverySuffixModules(ctx.Config())[m.BaseModuleName()] {
 		p.androidMkSuffix = recoverySuffix
@@ -868,7 +888,7 @@
 
 	m := ctx.Module().(*Module)
 
-	if m.inVendor() && vendorSuffixModules(ctx.Config())[m.BaseModuleName()] {
+	if m.InVendor() && vendorSuffixModules(ctx.Config())[m.BaseModuleName()] {
 		p.androidMkSuffix = vendorSuffix
 	} else if m.InRecovery() && recoverySuffixModules(ctx.Config())[m.BaseModuleName()] {
 		p.androidMkSuffix = recoverySuffix
diff --git a/cc/snapshot_utils.go b/cc/snapshot_utils.go
index 3e6444b..c50ef45 100644
--- a/cc/snapshot_utils.go
+++ b/cc/snapshot_utils.go
@@ -80,7 +80,7 @@
 	}
 
 	for _, image := range []snapshotImage{vendorSnapshotImageSingleton, recoverySnapshotImageSingleton} {
-		if isSnapshotAware(m, image.isProprietaryPath(ctx.ModuleDir()), apexInfo, image) {
+		if isSnapshotAware(ctx.DeviceConfig(), m, image.isProprietaryPath(ctx.ModuleDir()), apexInfo, image) {
 			return true
 		}
 	}
diff --git a/cc/stl.go b/cc/stl.go
index 406fa3a..75fab17 100644
--- a/cc/stl.go
+++ b/cc/stl.go
@@ -140,11 +140,7 @@
 }
 
 func staticUnwinder(ctx android.BaseModuleContext) string {
-	if ctx.Arch().ArchType == android.Arm {
-		return "libunwind_llvm"
-	} else {
-		return "libgcc_stripped"
-	}
+	return "libunwind"
 }
 
 func (stl *stl) deps(ctx BaseModuleContext, deps Deps) Deps {
@@ -192,6 +188,7 @@
 		if needsLibAndroidSupport(ctx) {
 			deps.StaticLibs = append(deps.StaticLibs, "ndk_libandroid_support")
 		}
+		// TODO: Switch the NDK over to the LLVM unwinder for non-arm32 architectures.
 		if ctx.Arch().ArchType == android.Arm {
 			deps.StaticLibs = append(deps.StaticLibs, "ndk_libunwind")
 		} else {
@@ -231,10 +228,6 @@
 					// Use Win32 threads in libc++.
 					"-D_LIBCPP_HAS_THREAD_API_WIN32")
 			}
-		} else {
-			if ctx.Arch().ArchType == android.Arm {
-				flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--exclude-libs,libunwind_llvm.a")
-			}
 		}
 	case "libstdc++":
 		// Nothing
diff --git a/cc/test.go b/cc/test.go
index 4ff5bf6..f715a8d 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -236,6 +236,10 @@
 	return BoolDefault(test.Properties.Gtest, true)
 }
 
+func (test *testDecorator) testBinary() bool {
+	return true
+}
+
 func (test *testDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
 	if !test.gtest() {
 		return flags
diff --git a/cc/testing.go b/cc/testing.go
index 8d92ea2..dc9a59d 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -110,6 +110,16 @@
 		}
 
 		toolchain_library {
+			name: "libunwind",
+			defaults: ["linux_bionic_supported"],
+			vendor_available: true,
+			product_available: true,
+			recovery_available: true,
+			native_bridge_supported: true,
+			src: "",
+		}
+
+		toolchain_library {
 			name: "libclang_rt.fuzzer-arm-android",
 			vendor_available: true,
 			product_available: true,
@@ -445,6 +455,14 @@
 			stl: "none",
 			system_shared_libs: [],
 		}
+
+		cc_library_static {
+			name: "note_memtag_heap_async",
+		}
+
+		cc_library_static {
+			name: "note_memtag_heap_sync",
+		}
 	`
 
 	supportLinuxBionic := false
diff --git a/cc/tidy.go b/cc/tidy.go
index 17471e6..972ad7b 100644
--- a/cc/tidy.go
+++ b/cc/tidy.go
@@ -15,6 +15,7 @@
 package cc
 
 import (
+	"regexp"
 	"strings"
 
 	"github.com/google/blueprint/proptools"
@@ -130,7 +131,31 @@
 	tidyChecks = tidyChecks + ",-bugprone-branch-clone"
 	flags.TidyFlags = append(flags.TidyFlags, tidyChecks)
 
-	if len(tidy.Properties.Tidy_checks_as_errors) > 0 {
+	if ctx.Config().IsEnvTrue("WITH_TIDY") {
+		// WITH_TIDY=1 enables clang-tidy globally. There could be many unexpected
+		// warnings from new checks and many local tidy_checks_as_errors and
+		// -warnings-as-errors can break a global build.
+		// So allow all clang-tidy warnings.
+		inserted := false
+		for i, s := range flags.TidyFlags {
+			if strings.Contains(s, "-warnings-as-errors=") {
+				// clang-tidy accepts only one -warnings-as-errors
+				// replace the old one
+				re := regexp.MustCompile(`'?-?-warnings-as-errors=[^ ]* *`)
+				newFlag := re.ReplaceAllString(s, "")
+				if newFlag == "" {
+					flags.TidyFlags[i] = "-warnings-as-errors=-*"
+				} else {
+					flags.TidyFlags[i] = newFlag + " -warnings-as-errors=-*"
+				}
+				inserted = true
+				break
+			}
+		}
+		if !inserted {
+			flags.TidyFlags = append(flags.TidyFlags, "-warnings-as-errors=-*")
+		}
+	} else if len(tidy.Properties.Tidy_checks_as_errors) > 0 {
 		tidyChecksAsErrors := "-warnings-as-errors=" + strings.Join(esc(tidy.Properties.Tidy_checks_as_errors), ",")
 		flags.TidyFlags = append(flags.TidyFlags, tidyChecksAsErrors)
 	}
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index 622ebec..6bd095f 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -198,7 +198,7 @@
 }
 
 // Determines if the module is a candidate for snapshot.
-func isSnapshotAware(m *Module, inProprietaryPath bool, apexInfo android.ApexInfo, image snapshotImage) bool {
+func isSnapshotAware(cfg android.DeviceConfig, m *Module, inProprietaryPath bool, apexInfo android.ApexInfo, image snapshotImage) bool {
 	if !m.Enabled() || m.Properties.HideFromMake {
 		return false
 	}
@@ -241,13 +241,17 @@
 	if _, ok := m.linker.(*llndkHeadersDecorator); ok {
 		return false
 	}
+	// If we are using directed snapshot AND we have to exclude this module, skip this
+	if image.excludeFromDirectedSnapshot(cfg, m.BaseModuleName()) {
+		return false
+	}
 
 	// Libraries
 	if l, ok := m.linker.(snapshotLibraryInterface); ok {
 		if m.sanitize != nil {
 			// scs and hwasan export both sanitized and unsanitized variants for static and header
 			// Always use unsanitized variants of them.
-			for _, t := range []sanitizerType{scs, hwasan} {
+			for _, t := range []SanitizerType{scs, hwasan} {
 				if !l.shared() && m.sanitize.isSanitizerEnabled(t) {
 					return false
 				}
@@ -535,7 +539,7 @@
 			}
 		}
 
-		if !isSnapshotAware(m, inProprietaryPath, apexInfo, c.image) {
+		if !isSnapshotAware(ctx.DeviceConfig(), m, inProprietaryPath, apexInfo, c.image) {
 			return
 		}
 
diff --git a/cc/vndk.go b/cc/vndk.go
index c1264f7..ff38db5 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -21,7 +21,6 @@
 	"path/filepath"
 	"sort"
 	"strings"
-	"sync"
 
 	"android/soong/android"
 	"android/soong/cc/config"
@@ -231,52 +230,59 @@
 	return nil
 }
 
+type moduleListerFunc func(ctx android.SingletonContext) (moduleNames, fileNames []string)
+
 var (
-	vndkCoreLibrariesKey             = android.NewOnceKey("vndkCoreLibraries")
-	vndkSpLibrariesKey               = android.NewOnceKey("vndkSpLibraries")
-	llndkLibrariesKey                = android.NewOnceKey("llndkLibraries")
-	vndkPrivateLibrariesKey          = android.NewOnceKey("vndkPrivateLibraries")
-	vndkProductLibrariesKey          = android.NewOnceKey("vndkProductLibraries")
-	vndkUsingCoreVariantLibrariesKey = android.NewOnceKey("vndkUsingCoreVariantLibraries")
-	vndkMustUseVendorVariantListKey  = android.NewOnceKey("vndkMustUseVendorVariantListKey")
-	vndkLibrariesLock                sync.Mutex
+	llndkLibraries                = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsLLNDK && !isVestigialLLNDKModule(m) })
+	llndkLibrariesWithoutHWASAN   = vndkModuleListRemover(llndkLibraries, "libclang_rt.hwasan-")
+	vndkSPLibraries               = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsVNDKSP })
+	vndkCoreLibraries             = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsVNDKCore })
+	vndkPrivateLibraries          = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsVNDKPrivate && !isVestigialLLNDKModule(m) })
+	vndkProductLibraries          = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsVNDKProduct })
+	vndkUsingCoreVariantLibraries = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsVNDKUsingCoreVariant })
 )
 
-func vndkCoreLibraries(config android.Config) map[string]string {
-	return config.Once(vndkCoreLibrariesKey, func() interface{} {
-		return make(map[string]string)
-	}).(map[string]string)
+// vndkModuleLister takes a predicate that operates on a Module and returns a moduleListerFunc
+// that produces a list of module names and output file names for which the predicate returns true.
+func vndkModuleLister(predicate func(*Module) bool) moduleListerFunc {
+	return func(ctx android.SingletonContext) (moduleNames, fileNames []string) {
+		ctx.VisitAllModules(func(m android.Module) {
+			if c, ok := m.(*Module); ok && predicate(c) {
+				filename, err := getVndkFileName(c)
+				if err != nil {
+					ctx.ModuleErrorf(m, "%s", err)
+				}
+				moduleNames = append(moduleNames, ctx.ModuleName(m))
+				fileNames = append(fileNames, filename)
+			}
+		})
+		moduleNames = android.SortedUniqueStrings(moduleNames)
+		fileNames = android.SortedUniqueStrings(fileNames)
+		return
+	}
 }
 
-func vndkSpLibraries(config android.Config) map[string]string {
-	return config.Once(vndkSpLibrariesKey, func() interface{} {
-		return make(map[string]string)
-	}).(map[string]string)
+// vndkModuleListRemover takes a moduleListerFunc and a prefix and returns a moduleListerFunc
+// that returns the same lists as the input moduleListerFunc, but with  modules with the
+// given prefix removed.
+func vndkModuleListRemover(lister moduleListerFunc, prefix string) moduleListerFunc {
+	return func(ctx android.SingletonContext) (moduleNames, fileNames []string) {
+		moduleNames, fileNames = lister(ctx)
+		filter := func(in []string) []string {
+			out := make([]string, 0, len(in))
+			for _, lib := range in {
+				if strings.HasPrefix(lib, prefix) {
+					continue
+				}
+				out = append(out, lib)
+			}
+			return out
+		}
+		return filter(moduleNames), filter(fileNames)
+	}
 }
 
-func llndkLibraries(config android.Config) map[string]string {
-	return config.Once(llndkLibrariesKey, func() interface{} {
-		return make(map[string]string)
-	}).(map[string]string)
-}
-
-func vndkPrivateLibraries(config android.Config) map[string]string {
-	return config.Once(vndkPrivateLibrariesKey, func() interface{} {
-		return make(map[string]string)
-	}).(map[string]string)
-}
-
-func vndkProductLibraries(config android.Config) map[string]string {
-	return config.Once(vndkProductLibrariesKey, func() interface{} {
-		return make(map[string]string)
-	}).(map[string]string)
-}
-
-func vndkUsingCoreVariantLibraries(config android.Config) map[string]string {
-	return config.Once(vndkUsingCoreVariantLibrariesKey, func() interface{} {
-		return make(map[string]string)
-	}).(map[string]string)
-}
+var vndkMustUseVendorVariantListKey = android.NewOnceKey("vndkMustUseVendorVariantListKey")
 
 func vndkMustUseVendorVariantList(cfg android.Config) []string {
 	return cfg.Once(vndkMustUseVendorVariantListKey, func() interface{} {
@@ -294,17 +300,10 @@
 
 func processLlndkLibrary(mctx android.BottomUpMutatorContext, m *Module) {
 	lib := m.linker.(*llndkStubDecorator)
-	name := m.ImplementationModuleName(mctx)
-	filename := name + ".so"
 
-	vndkLibrariesLock.Lock()
-	defer vndkLibrariesLock.Unlock()
-
-	llndkLibraries(mctx.Config())[name] = filename
 	m.VendorProperties.IsLLNDK = true
 	if Bool(lib.Properties.Private) {
-		vndkPrivateLibraries(mctx.Config())[name] = filename
-		m.VendorProperties.IsLLNDKPrivate = true
+		m.VendorProperties.IsVNDKPrivate = true
 	}
 }
 
@@ -316,10 +315,6 @@
 	}
 
 	name := m.BaseModuleName()
-	filename, err := getVndkFileName(m)
-	if err != nil {
-		panic(err)
-	}
 
 	if lib := m.library; lib != nil && lib.hasStubsVariants() && name != "libz" {
 		// b/155456180 libz is the ONLY exception here. We don't want to make
@@ -331,26 +326,23 @@
 		mctx.PropertyErrorf("vndk.enabled", "This library provides stubs. Shouldn't be VNDK. Consider making it as LLNDK")
 	}
 
-	vndkLibrariesLock.Lock()
-	defer vndkLibrariesLock.Unlock()
-
 	if inList(name, vndkMustUseVendorVariantList(mctx.Config())) {
 		m.Properties.MustUseVendorVariant = true
 	}
 	if mctx.DeviceConfig().VndkUseCoreVariant() && !m.Properties.MustUseVendorVariant {
-		vndkUsingCoreVariantLibraries(mctx.Config())[name] = filename
+		m.VendorProperties.IsVNDKUsingCoreVariant = true
 	}
 
 	if m.vndkdep.isVndkSp() {
-		vndkSpLibraries(mctx.Config())[name] = filename
+		m.VendorProperties.IsVNDKSP = true
 	} else {
-		vndkCoreLibraries(mctx.Config())[name] = filename
+		m.VendorProperties.IsVNDKCore = true
 	}
 	if m.IsVndkPrivate() {
-		vndkPrivateLibraries(mctx.Config())[name] = filename
+		m.VendorProperties.IsVNDKPrivate = true
 	}
 	if Bool(m.VendorProperties.Product_available) {
-		vndkProductLibraries(mctx.Config())[name] = filename
+		m.VendorProperties.IsVNDKProduct = true
 	}
 }
 
@@ -394,7 +386,7 @@
 
 		useCoreVariant := m.VndkVersion() == mctx.DeviceConfig().PlatformVndkVersion() &&
 			mctx.DeviceConfig().VndkUseCoreVariant() && !m.MustUseVendorVariant()
-		return lib.shared() && m.inVendor() && m.IsVndk() && !m.IsVndkExt() && !useCoreVariant
+		return lib.shared() && m.InVendor() && m.IsVndk() && !m.IsVndkExt() && !useCoreVariant
 	}
 	return false
 }
@@ -424,7 +416,7 @@
 			flagExporter.Properties = llndkLib.(*llndkStubDecorator).flagExporter.Properties
 
 			m.VendorProperties.IsLLNDK = llndk.VendorProperties.IsLLNDK
-			m.VendorProperties.IsLLNDKPrivate = llndk.VendorProperties.IsLLNDKPrivate
+			m.VendorProperties.IsVNDKPrivate = llndk.VendorProperties.IsVNDKPrivate
 		}
 	}
 
@@ -454,21 +446,29 @@
 }
 
 func RegisterVndkLibraryTxtTypes(ctx android.RegistrationContext) {
-	ctx.RegisterModuleType("llndk_libraries_txt", VndkLibrariesTxtFactory(libclangRTRemover(llndkLibraries)))
-	ctx.RegisterModuleType("vndksp_libraries_txt", VndkLibrariesTxtFactory(vndkSpLibraries))
-	ctx.RegisterModuleType("vndkcore_libraries_txt", VndkLibrariesTxtFactory(vndkCoreLibraries))
-	ctx.RegisterModuleType("vndkprivate_libraries_txt", VndkLibrariesTxtFactory(vndkPrivateLibraries))
-	ctx.RegisterModuleType("vndkproduct_libraries_txt", VndkLibrariesTxtFactory(vndkProductLibraries))
-	ctx.RegisterModuleType("vndkcorevariant_libraries_txt", VndkLibrariesTxtFactory(vndkUsingCoreVariantLibraries))
+	// Make uses LLNDK_LIBRARIES to determine which libraries to install.
+	// HWASAN is only part of the LL-NDK in builds in which libc depends on HWASAN.
+	// Therefore, by removing the library here, we cause it to only be installed if libc
+	// depends on it.
+	ctx.RegisterSingletonModuleType("llndk_libraries_txt", llndkLibrariesTxtFactory)
+	ctx.RegisterSingletonModuleType("vndksp_libraries_txt", vndkSPLibrariesTxtFactory)
+	ctx.RegisterSingletonModuleType("vndkcore_libraries_txt", vndkCoreLibrariesTxtFactory)
+	ctx.RegisterSingletonModuleType("vndkprivate_libraries_txt", vndkPrivateLibrariesTxtFactory)
+	ctx.RegisterSingletonModuleType("vndkproduct_libraries_txt", vndkProductLibrariesTxtFactory)
+	ctx.RegisterSingletonModuleType("vndkcorevariant_libraries_txt", vndkUsingCoreVariantLibrariesTxtFactory)
 }
 
 type vndkLibrariesTxt struct {
-	android.ModuleBase
+	android.SingletonModuleBase
 
-	lister     func(android.Config) map[string]string
+	lister      moduleListerFunc
+	makeVarName string
+
 	properties VndkLibrariesTxtProperties
 
-	outputFile android.OutputPath
+	outputFile  android.OutputPath
+	moduleNames []string
+	fileNames   []string
 }
 
 type VndkLibrariesTxtProperties struct {
@@ -478,25 +478,56 @@
 var _ etc.PrebuiltEtcModule = &vndkLibrariesTxt{}
 var _ android.OutputFileProducer = &vndkLibrariesTxt{}
 
-// vndk_libraries_txt is a special kind of module type in that it name is one of
-// - llndk.libraries.txt
-// - vndkcore.libraries.txt
-// - vndksp.libraries.txt
-// - vndkprivate.libraries.txt
-// - vndkproduct.libraries.txt
-// - vndkcorevariant.libraries.txt
-// A module behaves like a prebuilt_etc but its content is generated by soong.
-// By being a soong module, these files can be referenced by other soong modules.
+// llndk_libraries_txt is a singleton module whose content is a list of LLNDK libraries
+// generated by Soong but can be referenced by other modules.
 // For example, apex_vndk can depend on these files as prebuilt.
-func VndkLibrariesTxtFactory(lister func(android.Config) map[string]string) android.ModuleFactory {
-	return func() android.Module {
-		m := &vndkLibrariesTxt{
-			lister: lister,
-		}
-		m.AddProperties(&m.properties)
-		android.InitAndroidModule(m)
-		return m
+func llndkLibrariesTxtFactory() android.SingletonModule {
+	return newVndkLibrariesTxt(llndkLibrariesWithoutHWASAN, "LLNDK_LIBRARIES")
+}
+
+// vndksp_libraries_txt is a singleton module whose content is a list of VNDKSP libraries
+// generated by Soong but can be referenced by other modules.
+// For example, apex_vndk can depend on these files as prebuilt.
+func vndkSPLibrariesTxtFactory() android.SingletonModule {
+	return newVndkLibrariesTxt(vndkSPLibraries, "VNDK_SAMEPROCESS_LIBRARIES")
+}
+
+// vndkcore_libraries_txt is a singleton module whose content is a list of VNDK core libraries
+// generated by Soong but can be referenced by other modules.
+// For example, apex_vndk can depend on these files as prebuilt.
+func vndkCoreLibrariesTxtFactory() android.SingletonModule {
+	return newVndkLibrariesTxt(vndkCoreLibraries, "VNDK_CORE_LIBRARIES")
+}
+
+// vndkprivate_libraries_txt is a singleton module whose content is a list of VNDK private libraries
+// generated by Soong but can be referenced by other modules.
+// For example, apex_vndk can depend on these files as prebuilt.
+func vndkPrivateLibrariesTxtFactory() android.SingletonModule {
+	return newVndkLibrariesTxt(vndkPrivateLibraries, "VNDK_PRIVATE_LIBRARIES")
+}
+
+// vndkproduct_libraries_txt is a singleton module whose content is a list of VNDK product libraries
+// generated by Soong but can be referenced by other modules.
+// For example, apex_vndk can depend on these files as prebuilt.
+func vndkProductLibrariesTxtFactory() android.SingletonModule {
+	return newVndkLibrariesTxt(vndkProductLibraries, "VNDK_PRODUCT_LIBRARIES")
+}
+
+// vndkcorevariant_libraries_txt is a singleton module whose content is a list of VNDK libraries
+// that are using the core variant, generated by Soong but can be referenced by other modules.
+// For example, apex_vndk can depend on these files as prebuilt.
+func vndkUsingCoreVariantLibrariesTxtFactory() android.SingletonModule {
+	return newVndkLibrariesTxt(vndkUsingCoreVariantLibraries, "VNDK_USING_CORE_VARIANT_LIBRARIES")
+}
+
+func newVndkLibrariesTxt(lister moduleListerFunc, makeVarName string) android.SingletonModule {
+	m := &vndkLibrariesTxt{
+		lister:      lister,
+		makeVarName: makeVarName,
 	}
+	m.AddProperties(&m.properties)
+	android.InitAndroidModule(m)
+	return m
 }
 
 func insertVndkVersion(filename string, vndkVersion string) string {
@@ -506,23 +537,7 @@
 	return filename
 }
 
-func libclangRTRemover(lister func(android.Config) map[string]string) func(android.Config) map[string]string {
-	return func(config android.Config) map[string]string {
-		libs := lister(config)
-		filteredLibs := make(map[string]string, len(libs))
-		for lib, v := range libs {
-			if strings.HasPrefix(lib, "libclang_rt.hwasan-") {
-				continue
-			}
-			filteredLibs[lib] = v
-		}
-		return filteredLibs
-	}
-}
-
 func (txt *vndkLibrariesTxt) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	list := android.SortedStringMapValues(txt.lister(ctx.Config()))
-
 	var filename string
 	if BoolDefault(txt.properties.Insert_vndk_version, true) {
 		filename = insertVndkVersion(txt.Name(), ctx.DeviceConfig().PlatformVndkVersion())
@@ -531,12 +546,16 @@
 	}
 
 	txt.outputFile = android.PathForModuleOut(ctx, filename).OutputPath
-	android.WriteFileRule(ctx, txt.outputFile, strings.Join(list, "\n"))
 
 	installPath := android.PathForModuleInstall(ctx, "etc")
 	ctx.InstallFile(installPath, filename, txt.outputFile)
 }
 
+func (txt *vndkLibrariesTxt) GenerateSingletonBuildActions(ctx android.SingletonContext) {
+	txt.moduleNames, txt.fileNames = txt.lister(ctx)
+	android.WriteFileRule(ctx, txt.outputFile, strings.Join(txt.fileNames, "\n"))
+}
+
 func (txt *vndkLibrariesTxt) AndroidMkEntries() []android.AndroidMkEntries {
 	return []android.AndroidMkEntries{android.AndroidMkEntries{
 		Class:      "ETC",
@@ -549,6 +568,11 @@
 	}}
 }
 
+func (txt *vndkLibrariesTxt) MakeVars(ctx android.MakeVarsContext) {
+	ctx.Strict(txt.makeVarName, strings.Join(txt.moduleNames, " "))
+
+}
+
 // PrebuiltEtcModule interface
 func (txt *vndkLibrariesTxt) OutputFile() android.OutputPath {
 	return txt.outputFile
@@ -586,7 +610,7 @@
 	// !inVendor: There's product/vendor variants for VNDK libs. We only care about vendor variants.
 	// !installable: Snapshot only cares about "installable" modules.
 	// isSnapshotPrebuilt: Snapshotting a snapshot doesn't make sense.
-	if !m.inVendor() || !m.installable(apexInfo) || m.isSnapshotPrebuilt() {
+	if !m.InVendor() || !m.installable(apexInfo) || m.isSnapshotPrebuilt() {
 		return nil, "", false
 	}
 	l, ok := m.linker.(snapshotLibraryInterface)
@@ -829,33 +853,27 @@
 	if prebuilt, ok := m.linker.(*prebuiltLibraryLinker); ok {
 		return prebuilt.libraryDecorator.getLibNameHelper(m.BaseModuleName(), true, false) + ".so", nil
 	}
+	if library, ok := m.linker.(*llndkStubDecorator); ok {
+		return library.getLibNameHelper(m.BaseModuleName(), true, false) + ".so", nil
+	}
 	return "", fmt.Errorf("VNDK library should have libraryDecorator or prebuiltLibraryLinker as linker: %T", m.linker)
 }
 
 func (c *vndkSnapshotSingleton) buildVndkLibrariesTxtFiles(ctx android.SingletonContext) {
-	llndk := android.SortedStringMapValues(llndkLibraries(ctx.Config()))
-	vndkcore := android.SortedStringMapValues(vndkCoreLibraries(ctx.Config()))
-	vndksp := android.SortedStringMapValues(vndkSpLibraries(ctx.Config()))
-	vndkprivate := android.SortedStringMapValues(vndkPrivateLibraries(ctx.Config()))
-	vndkproduct := android.SortedStringMapValues(vndkProductLibraries(ctx.Config()))
-
 	// Build list of vndk libs as merged & tagged & filter-out(libclang_rt):
 	// Since each target have different set of libclang_rt.* files,
 	// keep the common set of files in vndk.libraries.txt
+	_, llndk := vndkModuleListRemover(llndkLibraries, "libclang_rt.")(ctx)
+	_, vndkcore := vndkModuleListRemover(vndkCoreLibraries, "libclang_rt.")(ctx)
+	_, vndksp := vndkSPLibraries(ctx)
+	_, vndkprivate := vndkPrivateLibraries(ctx)
+	_, vndkproduct := vndkModuleListRemover(vndkProductLibraries, "libclang_rt.")(ctx)
 	var merged []string
-	filterOutLibClangRt := func(libList []string) (filtered []string) {
-		for _, lib := range libList {
-			if !strings.HasPrefix(lib, "libclang_rt.") {
-				filtered = append(filtered, lib)
-			}
-		}
-		return
-	}
-	merged = append(merged, addPrefix(filterOutLibClangRt(llndk), "LLNDK: ")...)
+	merged = append(merged, addPrefix(llndk, "LLNDK: ")...)
 	merged = append(merged, addPrefix(vndksp, "VNDK-SP: ")...)
-	merged = append(merged, addPrefix(filterOutLibClangRt(vndkcore), "VNDK-core: ")...)
+	merged = append(merged, addPrefix(vndkcore, "VNDK-core: ")...)
 	merged = append(merged, addPrefix(vndkprivate, "VNDK-private: ")...)
-	merged = append(merged, addPrefix(filterOutLibClangRt(vndkproduct), "VNDK-product: ")...)
+	merged = append(merged, addPrefix(vndkproduct, "VNDK-product: ")...)
 	c.vndkLibrariesFile = android.PathForOutput(ctx, "vndk", "vndk.libraries.txt")
 	android.WriteFileRule(ctx, c.vndkLibrariesFile, strings.Join(merged, "\n"))
 }
@@ -877,25 +895,6 @@
 	ctx.Strict("LLNDK_MOVED_TO_APEX_LIBRARIES",
 		strings.Join(android.SortedStringKeys(movedToApexLlndkLibraries), " "))
 
-	// Make uses LLNDK_LIBRARIES to determine which libraries to install.
-	// HWASAN is only part of the LL-NDK in builds in which libc depends on HWASAN.
-	// Therefore, by removing the library here, we cause it to only be installed if libc
-	// depends on it.
-	installedLlndkLibraries := []string{}
-	for lib := range llndkLibraries(ctx.Config()) {
-		if strings.HasPrefix(lib, "libclang_rt.hwasan-") {
-			continue
-		}
-		installedLlndkLibraries = append(installedLlndkLibraries, lib)
-	}
-	sort.Strings(installedLlndkLibraries)
-	ctx.Strict("LLNDK_LIBRARIES", strings.Join(installedLlndkLibraries, " "))
-
-	ctx.Strict("VNDK_CORE_LIBRARIES", strings.Join(android.SortedStringKeys(vndkCoreLibraries(ctx.Config())), " "))
-	ctx.Strict("VNDK_SAMEPROCESS_LIBRARIES", strings.Join(android.SortedStringKeys(vndkSpLibraries(ctx.Config())), " "))
-	ctx.Strict("VNDK_PRIVATE_LIBRARIES", strings.Join(android.SortedStringKeys(vndkPrivateLibraries(ctx.Config())), " "))
-	ctx.Strict("VNDK_USING_CORE_VARIANT_LIBRARIES", strings.Join(android.SortedStringKeys(vndkUsingCoreVariantLibraries(ctx.Config())), " "))
-
 	ctx.Strict("VNDK_LIBRARIES_FILE", c.vndkLibrariesFile.String())
 	ctx.Strict("SOONG_VNDK_SNAPSHOT_ZIP", c.vndkSnapshotZipFile.String())
 }
diff --git a/cmd/soong_build/Android.bp b/cmd/soong_build/Android.bp
index 441ea0d..6714978 100644
--- a/cmd/soong_build/Android.bp
+++ b/cmd/soong_build/Android.bp
@@ -20,6 +20,7 @@
         "golang-protobuf-proto",
         "soong",
         "soong-android",
+        "soong-bp2build",
         "soong-env",
         "soong-ui-metrics_proto",
     ],
@@ -27,10 +28,6 @@
         "main.go",
         "writedocs.go",
         "queryview.go",
-        "queryview_templates.go",
-    ],
-    testSrcs: [
-        "queryview_test.go",
     ],
     primaryBuilder: true,
 }
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 907bed3..1d5e7b3 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -23,6 +23,7 @@
 	"github.com/google/blueprint/bootstrap"
 
 	"android/soong/android"
+	"android/soong/bp2build"
 )
 
 var (
@@ -54,18 +55,12 @@
 // bazelConversionRequested checks that the user is intending to convert
 // Blueprint to Bazel BUILD files.
 func bazelConversionRequested(configuration android.Config) bool {
-	return configuration.IsEnvTrue("CONVERT_TO_BAZEL")
+	return configuration.IsEnvTrue("GENERATE_BAZEL_FILES")
 }
 
-func newContext(srcDir string, configuration android.Config) *android.Context {
+func newContext(configuration android.Config) *android.Context {
 	ctx := android.NewContext(configuration)
-	if bazelConversionRequested(configuration) {
-		// Register an alternate set of singletons and mutators for bazel
-		// conversion for Bazel conversion.
-		ctx.RegisterForBazelConversion()
-	} else {
-		ctx.Register()
-	}
+	ctx.Register()
 	if !shouldPrepareBuildActions(configuration) {
 		configuration.SetStopBefore(bootstrap.StopBeforePrepareBuildActions)
 	}
@@ -100,13 +95,22 @@
 		// enabled even if it completed successfully.
 		extraNinjaDeps = append(extraNinjaDeps, filepath.Join(configuration.BuildDir(), "always_rerun_for_delve"))
 	}
+
+	if bazelConversionRequested(configuration) {
+		// Run the alternate pipeline of bp2build mutators and singleton to convert Blueprint to BUILD files
+		// before everything else.
+		runBp2Build(configuration, extraNinjaDeps)
+		// Short-circuit and return.
+		return
+	}
+
 	if configuration.BazelContext.BazelEnabled() {
 		// Bazel-enabled mode. Soong runs in two passes.
 		// First pass: Analyze the build tree, but only store all bazel commands
 		// needed to correctly evaluate the tree in the second pass.
 		// TODO(cparsons): Don't output any ninja file, as the second pass will overwrite
 		// the incorrect results from the first pass, and file I/O is expensive.
-		firstCtx := newContext(srcDir, configuration)
+		firstCtx := newContext(configuration)
 		configuration.SetStopBefore(bootstrap.StopBeforeWriteNinja)
 		bootstrap.Main(firstCtx.Context, configuration, extraNinjaDeps...)
 		// Invoke bazel commands and save results for second pass.
@@ -120,10 +124,10 @@
 			fmt.Fprintf(os.Stderr, "%s", err)
 			os.Exit(1)
 		}
-		ctx = newContext(srcDir, secondPassConfig)
+		ctx = newContext(secondPassConfig)
 		bootstrap.Main(ctx.Context, secondPassConfig, extraNinjaDeps...)
 	} else {
-		ctx = newContext(srcDir, configuration)
+		ctx = newContext(configuration)
 		bootstrap.Main(ctx.Context, configuration, extraNinjaDeps...)
 	}
 
@@ -154,6 +158,22 @@
 	}
 }
 
+// Run Soong in the bp2build mode. This creates a standalone context that registers
+// an alternate pipeline of mutators and singletons specifically for generating
+// Bazel BUILD files instead of Ninja files.
+func runBp2Build(configuration android.Config, extraNinjaDeps []string) {
+	// Register an alternate set of singletons and mutators for bazel
+	// conversion for Bazel conversion.
+	bp2buildCtx := android.NewContext(configuration)
+	bp2buildCtx.RegisterForBazelConversion()
+	configuration.SetStopBefore(bootstrap.StopBeforePrepareBuildActions)
+	bp2buildCtx.SetNameInterface(newNameResolver(configuration))
+	bootstrap.Main(bp2buildCtx.Context, configuration, extraNinjaDeps...)
+
+	codegenContext := bp2build.NewCodegenContext(configuration, *bp2buildCtx)
+	bp2build.Codegen(codegenContext)
+}
+
 // shouldPrepareBuildActions reads configuration and flags if build actions
 // should be generated.
 func shouldPrepareBuildActions(configuration android.Config) bool {
diff --git a/cmd/soong_build/queryview.go b/cmd/soong_build/queryview.go
index f5aa685..79ea94a 100644
--- a/cmd/soong_build/queryview.go
+++ b/cmd/soong_build/queryview.go
@@ -16,512 +16,45 @@
 
 import (
 	"android/soong/android"
-	"fmt"
+	"android/soong/bp2build"
 	"io/ioutil"
 	"os"
 	"path/filepath"
-	"reflect"
-	"strings"
-
-	"github.com/google/blueprint"
-	"github.com/google/blueprint/bootstrap/bpdoc"
-	"github.com/google/blueprint/proptools"
 )
 
-var (
-	// An allowlist of prop types that are surfaced from module props to rule
-	// attributes. (nested) dictionaries are notably absent here, because while
-	// Soong supports multi value typed and nested dictionaries, Bazel's rule
-	// attr() API supports only single-level string_dicts.
-	allowedPropTypes = map[string]bool{
-		"int":         true, // e.g. 42
-		"bool":        true, // e.g. True
-		"string_list": true, // e.g. ["a", "b"]
-		"string":      true, // e.g. "a"
-	}
-
-	// Certain module property names are blocklisted/ignored here, for the reasons commented.
-	ignoredPropNames = map[string]bool{
-		"name":       true, // redundant, since this is explicitly generated for every target
-		"from":       true, // reserved keyword
-		"in":         true, // reserved keyword
-		"arch":       true, // interface prop type is not supported yet.
-		"multilib":   true, // interface prop type is not supported yet.
-		"target":     true, // interface prop type is not supported yet.
-		"visibility": true, // Bazel has native visibility semantics. Handle later.
-		"features":   true, // There is already a built-in attribute 'features' which cannot be overridden.
-	}
-)
-
-func targetNameWithVariant(c *blueprint.Context, logicModule blueprint.Module) string {
-	name := ""
-	if c.ModuleSubDir(logicModule) != "" {
-		// TODO(b/162720883): Figure out a way to drop the "--" variant suffixes.
-		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 escapeString(s string) string {
-	s = strings.ReplaceAll(s, "\\", "\\\\")
-	return strings.ReplaceAll(s, "\"", "\\\"")
-}
-
-func makeIndent(indent int) string {
-	if indent < 0 {
-		panic(fmt.Errorf("indent column cannot be less than 0, but got %d", indent))
-	}
-	return strings.Repeat("    ", indent)
-}
-
-// prettyPrint a property value into the equivalent Starlark representation
-// recursively.
-func prettyPrint(propertyValue reflect.Value, indent int) (string, error) {
-	if isZero(propertyValue) {
-		// A property value being set or unset actually matters -- Soong does set default
-		// values for unset properties, like system_shared_libs = ["libc", "libm", "libdl"] at
-		// https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/linker.go;l=281-287;drc=f70926eef0b9b57faf04c17a1062ce50d209e480
-		//
-		// In Bazel-parlance, we would use "attr.<type>(default = <default value>)" to set the default
-		// value of unset attributes.
-		return "", nil
-	}
-
-	var ret string
-	switch propertyValue.Kind() {
-	case reflect.String:
-		ret = fmt.Sprintf("\"%v\"", escapeString(propertyValue.String()))
-	case reflect.Bool:
-		ret = strings.Title(fmt.Sprintf("%v", propertyValue.Interface()))
-	case reflect.Int, reflect.Uint, reflect.Int64:
-		ret = fmt.Sprintf("%v", propertyValue.Interface())
-	case reflect.Ptr:
-		return prettyPrint(propertyValue.Elem(), indent)
-	case reflect.Slice:
-		ret = "[\n"
-		for i := 0; i < propertyValue.Len(); i++ {
-			indexedValue, err := prettyPrint(propertyValue.Index(i), indent+1)
-			if err != nil {
-				return "", err
-			}
-
-			if indexedValue != "" {
-				ret += makeIndent(indent + 1)
-				ret += indexedValue
-				ret += ",\n"
-			}
-		}
-		ret += makeIndent(indent)
-		ret += "]"
-	case reflect.Struct:
-		ret = "{\n"
-		// Sort and print the struct props by the key.
-		structProps := extractStructProperties(propertyValue, indent)
-		for _, k := range android.SortedStringKeys(structProps) {
-			ret += makeIndent(indent + 1)
-			ret += fmt.Sprintf("%q: %s,\n", k, structProps[k])
-		}
-		ret += makeIndent(indent)
-		ret += "}"
-	case reflect.Interface:
-		// TODO(b/164227191): implement pretty print for interfaces.
-		// Interfaces are used for for arch, multilib and target properties.
-		return "", nil
-	default:
-		return "", fmt.Errorf(
-			"unexpected kind for property struct field: %s", propertyValue.Kind())
-	}
-	return ret, nil
-}
-
-// Converts a reflected property struct value into a map of property names and property values,
-// which each property value correctly pretty-printed and indented at the right nest level,
-// since property structs can be nested. In Starlark, nested structs are represented as nested
-// dicts: https://docs.bazel.build/skylark/lib/dict.html
-func extractStructProperties(structValue reflect.Value, indent int) map[string]string {
-	if structValue.Kind() != reflect.Struct {
-		panic(fmt.Errorf("Expected a reflect.Struct type, but got %s", structValue.Kind()))
-	}
-
-	ret := map[string]string{}
-	structType := structValue.Type()
-	for i := 0; i < structValue.NumField(); i++ {
-		field := structType.Field(i)
-		if field.PkgPath != "" {
-			// Skip unexported fields. Some properties are
-			// internal to Soong only, and these fields do not have PkgPath.
-			continue
-		}
-		if proptools.HasTag(field, "blueprint", "mutated") {
-			continue
-		}
-
-		fieldValue := structValue.Field(i)
-		if isZero(fieldValue) {
-			// Ignore zero-valued fields
-			continue
-		}
-
-		propertyName := proptools.PropertyNameForField(field.Name)
-		prettyPrintedValue, err := prettyPrint(fieldValue, indent+1)
-		if err != nil {
-			panic(
-				fmt.Errorf(
-					"Error while parsing property: %q. %s",
-					propertyName,
-					err))
-		}
-		if prettyPrintedValue != "" {
-			ret[propertyName] = prettyPrintedValue
-		}
-	}
-
-	return ret
-}
-
-func isStructPtr(t reflect.Type) bool {
-	return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct
-}
-
-// Generically extract module properties and types into a map, keyed by the module property name.
-func extractModuleProperties(aModule android.Module) map[string]string {
-	ret := map[string]string{}
-
-	// Iterate over this android.Module's property structs.
-	for _, properties := range aModule.GetProperties() {
-		propertiesValue := reflect.ValueOf(properties)
-		// Check that propertiesValue is a pointer to the Properties struct, like
-		// *cc.BaseLinkerProperties or *java.CompilerProperties.
-		//
-		// propertiesValue can also be type-asserted to the structs to
-		// manipulate internal props, if needed.
-		if isStructPtr(propertiesValue.Type()) {
-			structValue := propertiesValue.Elem()
-			for k, v := range extractStructProperties(structValue, 0) {
-				ret[k] = v
-			}
-		} else {
-			panic(fmt.Errorf(
-				"properties must be a pointer to a struct, got %T",
-				propertiesValue.Interface()))
-		}
-
-	}
-
-	return ret
-}
-
-// FIXME(b/168089390): In Bazel, rules ending with "_test" needs to be marked as
-// testonly = True, forcing other rules that depend on _test rules to also be
-// marked as testonly = True. This semantic constraint is not present in Soong.
-// To work around, rename "*_test" rules to "*_test_".
-func canonicalizeModuleType(moduleName string) string {
-	if strings.HasSuffix(moduleName, "_test") {
-		return moduleName + "_"
-	}
-
-	return moduleName
-}
-
-type RuleShim struct {
-	// The rule class shims contained in a bzl file. e.g. ["cc_object", "cc_library", ..]
-	rules []string
-
-	// The generated string content of the bzl file.
-	content string
-}
-
-// Create <module>.bzl containing Bazel rule shims for every module type available in Soong and
-// user-specified Go plugins.
-//
-// This function reuses documentation generation APIs to ensure parity between modules-as-docs
-// and modules-as-code, including the names and types of module properties.
-func createRuleShims(packages []*bpdoc.Package) (map[string]RuleShim, error) {
-	var propToAttr func(prop bpdoc.Property, propName string) string
-	propToAttr = func(prop bpdoc.Property, propName string) string {
-		// dots are not allowed in Starlark attribute names. Substitute them with double underscores.
-		propName = strings.ReplaceAll(propName, ".", "__")
-		if !shouldGenerateAttribute(propName) {
-			return ""
-		}
-
-		// Canonicalize and normalize module property types to Bazel attribute types
-		starlarkAttrType := prop.Type
-		if starlarkAttrType == "list of string" {
-			starlarkAttrType = "string_list"
-		} else if starlarkAttrType == "int64" {
-			starlarkAttrType = "int"
-		} else if starlarkAttrType == "" {
-			var attr string
-			for _, nestedProp := range prop.Properties {
-				nestedAttr := propToAttr(nestedProp, propName+"__"+nestedProp.Name)
-				if nestedAttr != "" {
-					// TODO(b/167662930): Fix nested props resulting in too many attributes.
-					// Let's still generate these, but comment them out.
-					attr += "# " + nestedAttr
-				}
-			}
-			return attr
-		}
-
-		if !allowedPropTypes[starlarkAttrType] {
-			return ""
-		}
-
-		return fmt.Sprintf("        %q: attr.%s(),\n", propName, starlarkAttrType)
-	}
-
-	ruleShims := map[string]RuleShim{}
-	for _, pkg := range packages {
-		content := "load(\"//build/bazel/queryview_rules:providers.bzl\", \"SoongModuleInfo\")\n"
-
-		bzlFileName := strings.ReplaceAll(pkg.Path, "android/soong/", "")
-		bzlFileName = strings.ReplaceAll(bzlFileName, ".", "_")
-		bzlFileName = strings.ReplaceAll(bzlFileName, "/", "_")
-
-		rules := []string{}
-
-		for _, moduleTypeTemplate := range moduleTypeDocsToTemplates(pkg.ModuleTypes) {
-			attrs := `{
-        "module_name": attr.string(mandatory = True),
-        "module_variant": attr.string(),
-        "module_deps": attr.label_list(providers = [SoongModuleInfo]),
-`
-			for _, prop := range moduleTypeTemplate.Properties {
-				attrs += propToAttr(prop, prop.Name)
-			}
-
-			moduleTypeName := moduleTypeTemplate.Name
-
-			// Certain SDK-related module types dynamically inject properties, instead of declaring
-			// them as structs. These properties are registered in an SdkMemberTypesRegistry. If
-			// the module type name matches, add these properties into the rule definition.
-			var registeredTypes []android.SdkMemberType
-			if moduleTypeName == "module_exports" || moduleTypeName == "module_exports_snapshot" {
-				registeredTypes = android.ModuleExportsMemberTypes.RegisteredTypes()
-			} else if moduleTypeName == "sdk" || moduleTypeName == "sdk_snapshot" {
-				registeredTypes = android.SdkMemberTypes.RegisteredTypes()
-			}
-			for _, memberType := range registeredTypes {
-				attrs += fmt.Sprintf("        %q: attr.string_list(),\n", memberType.SdkPropertyName())
-			}
-
-			attrs += "    },"
-
-			rule := canonicalizeModuleType(moduleTypeTemplate.Name)
-			content += fmt.Sprintf(moduleRuleShim, rule, attrs)
-			rules = append(rules, rule)
-		}
-
-		ruleShims[bzlFileName] = RuleShim{content: content, rules: rules}
-	}
-	return ruleShims, nil
-}
-
 func createBazelQueryView(ctx *android.Context, bazelQueryViewDir string) error {
-	blueprintCtx := ctx.Context
-	blueprintCtx.VisitAllModules(func(module blueprint.Module) {
-		buildFile, err := buildFileForModule(blueprintCtx, module, bazelQueryViewDir)
-		if err != nil {
-			panic(err)
-		}
+	ruleShims := bp2build.CreateRuleShims(android.ModuleTypeFactories())
+	buildToTargets := bp2build.GenerateSoongModuleTargets(*ctx)
 
-		buildFile.Write([]byte(generateSoongModuleTarget(blueprintCtx, module) + "\n\n"))
-		buildFile.Close()
-	})
-	var err error
-
-	// Write top level files: WORKSPACE and BUILD. These files are empty.
-	if err = writeReadOnlyFile(bazelQueryViewDir, "WORKSPACE", ""); err != nil {
-		return err
-	}
-
-	// Used to denote that the top level directory is a package.
-	if err = writeReadOnlyFile(bazelQueryViewDir, "BUILD", ""); err != nil {
-		return err
-	}
-
-	packages, err := getPackages(ctx)
-	if err != nil {
-		return err
-	}
-	ruleShims, err := createRuleShims(packages)
-	if err != nil {
-		return err
-	}
-
-	// Write .bzl Starlark files into the bazel_rules top level directory (provider and rule definitions)
-	bazelRulesDir := bazelQueryViewDir + "/build/bazel/queryview_rules"
-	if err = writeReadOnlyFile(bazelRulesDir, "BUILD", ""); err != nil {
-		return err
-	}
-	if err = writeReadOnlyFile(bazelRulesDir, "providers.bzl", providersBzl); err != nil {
-		return err
-	}
-
-	for bzlFileName, ruleShim := range ruleShims {
-		if err = writeReadOnlyFile(bazelRulesDir, bzlFileName+".bzl", ruleShim.content); err != nil {
+	filesToWrite := bp2build.CreateBazelFiles(ruleShims, buildToTargets)
+	for _, f := range filesToWrite {
+		if err := writeReadOnlyFile(bazelQueryViewDir, f); err != nil {
 			return err
 		}
 	}
 
-	return writeReadOnlyFile(bazelRulesDir, "soong_module.bzl", generateSoongModuleBzl(ruleShims))
+	return nil
 }
 
-// Generate the content of soong_module.bzl with the rule shim load statements
-// and mapping of module_type to rule shim map for every module type in Soong.
-func generateSoongModuleBzl(bzlLoads map[string]RuleShim) string {
-	var loadStmts string
-	var moduleRuleMap string
-	for bzlFileName, ruleShim := range bzlLoads {
-		loadStmt := "load(\"//build/bazel/queryview_rules:"
-		loadStmt += bzlFileName
-		loadStmt += ".bzl\""
-		for _, rule := range ruleShim.rules {
-			loadStmt += fmt.Sprintf(", %q", rule)
-			moduleRuleMap += "    \"" + rule + "\": " + rule + ",\n"
-		}
-		loadStmt += ")\n"
-		loadStmts += loadStmt
-	}
-
-	return fmt.Sprintf(soongModuleBzl, loadStmts, moduleRuleMap)
-}
-
-func shouldGenerateAttribute(prop string) bool {
-	return !ignoredPropNames[prop]
-}
-
-// props is an unsorted map. This function ensures that
-// the generated attributes are sorted to ensure determinism.
-func propsToAttributes(props map[string]string) string {
-	var attributes string
-	for _, propName := range android.SortedStringKeys(props) {
-		if shouldGenerateAttribute(propName) {
-			attributes += fmt.Sprintf("    %s = %s,\n", propName, props[propName])
-		}
-	}
-	return attributes
-}
-
-// Convert a module and its deps and props into a Bazel macro/rule
-// representation in the BUILD file.
-func generateSoongModuleTarget(
-	blueprintCtx *blueprint.Context,
-	module blueprint.Module) string {
-
-	var props map[string]string
-	if aModule, ok := module.(android.Module); ok {
-		props = extractModuleProperties(aModule)
-	}
-	attributes := propsToAttributes(props)
-
-	// 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
-	})
-
-	depLabelList := "[\n"
-	for depLabel, _ := range depLabels {
-		depLabelList += fmt.Sprintf("        %q,\n", depLabel)
-	}
-	depLabelList += "    ]"
-
-	return fmt.Sprintf(
-		soongModuleTarget,
-		targetNameWithVariant(blueprintCtx, module),
-		blueprintCtx.ModuleName(module),
-		canonicalizeModuleType(blueprintCtx.ModuleType(module)),
-		blueprintCtx.ModuleSubDir(module),
-		depLabelList,
-		attributes)
-}
-
-func buildFileForModule(
-	ctx *blueprint.Context, module blueprint.Module, bazelQueryViewDir string) (*os.File, error) {
-	// Create nested directories for the BUILD file
-	dirPath := filepath.Join(bazelQueryViewDir, packagePath(ctx, module))
-	createDirectoryIfNonexistent(dirPath)
-	// 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
-}
-
-func createDirectoryIfNonexistent(dir string) {
-	if _, err := os.Stat(dir); os.IsNotExist(err) {
-		os.MkdirAll(dir, os.ModePerm)
-	}
-}
-
-// The QueryView directory should be read-only, sufficient for bazel query. The files
+// The auto-conversion directory should be read-only, sufficient for bazel query. The files
 // are not intended to be edited by end users.
-func writeReadOnlyFile(dir string, baseName string, content string) error {
-	createDirectoryIfNonexistent(dir)
-	pathToFile := filepath.Join(dir, baseName)
+func writeReadOnlyFile(dir string, f bp2build.BazelFile) error {
+	dir = filepath.Join(dir, f.Dir)
+	if err := createDirectoryIfNonexistent(dir); err != nil {
+		return err
+	}
+	pathToFile := filepath.Join(dir, f.Basename)
+
 	// 0444 is read-only
-	return ioutil.WriteFile(pathToFile, []byte(content), 0444)
+	err := ioutil.WriteFile(pathToFile, []byte(f.Contents), 0444)
+
+	return err
 }
 
-func isZero(value reflect.Value) bool {
-	switch value.Kind() {
-	case reflect.Func, reflect.Map, reflect.Slice:
-		return value.IsNil()
-	case reflect.Array:
-		valueIsZero := true
-		for i := 0; i < value.Len(); i++ {
-			valueIsZero = valueIsZero && isZero(value.Index(i))
-		}
-		return valueIsZero
-	case reflect.Struct:
-		valueIsZero := true
-		for i := 0; i < value.NumField(); i++ {
-			if value.Field(i).CanSet() {
-				valueIsZero = valueIsZero && isZero(value.Field(i))
-			}
-		}
-		return valueIsZero
-	case reflect.Ptr:
-		if !value.IsNil() {
-			return isZero(reflect.Indirect(value))
-		} else {
-			return true
-		}
-	default:
-		zeroValue := reflect.Zero(value.Type())
-		result := value.Interface() == zeroValue.Interface()
-		return result
+func createDirectoryIfNonexistent(dir string) error {
+	if _, err := os.Stat(dir); os.IsNotExist(err) {
+		return os.MkdirAll(dir, os.ModePerm)
+	} else {
+		return err
 	}
 }
diff --git a/cmd/soong_build/queryview_test.go b/cmd/soong_build/queryview_test.go
deleted file mode 100644
index 9471a91..0000000
--- a/cmd/soong_build/queryview_test.go
+++ /dev/null
@@ -1,470 +0,0 @@
-// 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"
-	"io/ioutil"
-	"os"
-	"strings"
-	"testing"
-
-	"github.com/google/blueprint/bootstrap/bpdoc"
-)
-
-var buildDir string
-
-func setUp() {
-	var err error
-	buildDir, err = ioutil.TempDir("", "bazel_queryview_test")
-	if err != nil {
-		panic(err)
-	}
-}
-
-func tearDown() {
-	os.RemoveAll(buildDir)
-}
-
-func TestMain(m *testing.M) {
-	run := func() int {
-		setUp()
-		defer tearDown()
-
-		return m.Run()
-	}
-
-	os.Exit(run())
-}
-
-type customModule struct {
-	android.ModuleBase
-}
-
-// OutputFiles is needed because some instances of this module use dist with a
-// tag property which requires the module implements OutputFileProducer.
-func (m *customModule) OutputFiles(tag string) (android.Paths, error) {
-	return android.PathsForTesting("path" + tag), nil
-}
-
-func (m *customModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	// nothing for now.
-}
-
-func customModuleFactory() android.Module {
-	module := &customModule{}
-	android.InitAndroidModule(module)
-	return module
-}
-
-func TestGenerateBazelQueryViewFromBlueprint(t *testing.T) {
-	testCases := []struct {
-		bp                  string
-		expectedBazelTarget string
-	}{
-		{
-			bp: `custom {
-	name: "foo",
-}
-		`,
-			expectedBazelTarget: `soong_module(
-    name = "foo",
-    module_name = "foo",
-    module_type = "custom",
-    module_variant = "",
-    module_deps = [
-    ],
-)`,
-		},
-		{
-			bp: `custom {
-	name: "foo",
-	ramdisk: true,
-}
-		`,
-			expectedBazelTarget: `soong_module(
-    name = "foo",
-    module_name = "foo",
-    module_type = "custom",
-    module_variant = "",
-    module_deps = [
-    ],
-    ramdisk = True,
-)`,
-		},
-		{
-			bp: `custom {
-	name: "foo",
-	owner: "a_string_with\"quotes\"_and_\\backslashes\\\\",
-}
-		`,
-			expectedBazelTarget: `soong_module(
-    name = "foo",
-    module_name = "foo",
-    module_type = "custom",
-    module_variant = "",
-    module_deps = [
-    ],
-    owner = "a_string_with\"quotes\"_and_\\backslashes\\\\",
-)`,
-		},
-		{
-			bp: `custom {
-	name: "foo",
-	required: ["bar"],
-}
-		`,
-			expectedBazelTarget: `soong_module(
-    name = "foo",
-    module_name = "foo",
-    module_type = "custom",
-    module_variant = "",
-    module_deps = [
-    ],
-    required = [
-        "bar",
-    ],
-)`,
-		},
-		{
-			bp: `custom {
-	name: "foo",
-	target_required: ["qux", "bazqux"],
-}
-		`,
-			expectedBazelTarget: `soong_module(
-    name = "foo",
-    module_name = "foo",
-    module_type = "custom",
-    module_variant = "",
-    module_deps = [
-    ],
-    target_required = [
-        "qux",
-        "bazqux",
-    ],
-)`,
-		},
-		{
-			bp: `custom {
-	name: "foo",
-	dist: {
-		targets: ["goal_foo"],
-		tag: ".foo",
-	},
-	dists: [
-		{
-			targets: ["goal_bar"],
-			tag: ".bar",
-		},
-	],
-}
-		`,
-			expectedBazelTarget: `soong_module(
-    name = "foo",
-    module_name = "foo",
-    module_type = "custom",
-    module_variant = "",
-    module_deps = [
-    ],
-    dist = {
-        "tag": ".foo",
-        "targets": [
-            "goal_foo",
-        ],
-    },
-    dists = [
-        {
-            "tag": ".bar",
-            "targets": [
-                "goal_bar",
-            ],
-        },
-    ],
-)`,
-		},
-		{
-			bp: `custom {
-	name: "foo",
-	required: ["bar"],
-	target_required: ["qux", "bazqux"],
-	ramdisk: true,
-	owner: "custom_owner",
-	dists: [
-		{
-			tag: ".tag",
-			targets: ["my_goal"],
-		},
-	],
-}
-		`,
-			expectedBazelTarget: `soong_module(
-    name = "foo",
-    module_name = "foo",
-    module_type = "custom",
-    module_variant = "",
-    module_deps = [
-    ],
-    dists = [
-        {
-            "tag": ".tag",
-            "targets": [
-                "my_goal",
-            ],
-        },
-    ],
-    owner = "custom_owner",
-    ramdisk = True,
-    required = [
-        "bar",
-    ],
-    target_required = [
-        "qux",
-        "bazqux",
-    ],
-)`,
-		},
-	}
-
-	for _, testCase := range testCases {
-		config := android.TestConfig(buildDir, nil, testCase.bp, nil)
-		ctx := android.NewTestContext(config)
-		ctx.RegisterModuleType("custom", customModuleFactory)
-		ctx.Register()
-
-		_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
-		android.FailIfErrored(t, errs)
-		_, errs = ctx.PrepareBuildActions(config)
-		android.FailIfErrored(t, errs)
-
-		module := ctx.ModuleForTests("foo", "").Module().(*customModule)
-		blueprintCtx := ctx.Context.Context
-
-		actualBazelTarget := generateSoongModuleTarget(blueprintCtx, module)
-		if actualBazelTarget != testCase.expectedBazelTarget {
-			t.Errorf(
-				"Expected generated Bazel target to be '%s', got '%s'",
-				testCase.expectedBazelTarget,
-				actualBazelTarget,
-			)
-		}
-	}
-}
-
-func createPackageFixtures() []*bpdoc.Package {
-	properties := []bpdoc.Property{
-		bpdoc.Property{
-			Name: "int64_prop",
-			Type: "int64",
-		},
-		bpdoc.Property{
-			Name: "int_prop",
-			Type: "int",
-		},
-		bpdoc.Property{
-			Name: "bool_prop",
-			Type: "bool",
-		},
-		bpdoc.Property{
-			Name: "string_prop",
-			Type: "string",
-		},
-		bpdoc.Property{
-			Name: "string_list_prop",
-			Type: "list of string",
-		},
-		bpdoc.Property{
-			Name: "nested_prop",
-			Type: "",
-			Properties: []bpdoc.Property{
-				bpdoc.Property{
-					Name: "int_prop",
-					Type: "int",
-				},
-				bpdoc.Property{
-					Name: "bool_prop",
-					Type: "bool",
-				},
-				bpdoc.Property{
-					Name: "string_prop",
-					Type: "string",
-				},
-			},
-		},
-		bpdoc.Property{
-			Name: "unknown_type",
-			Type: "unknown",
-		},
-	}
-
-	fooPropertyStruct := &bpdoc.PropertyStruct{
-		Name:       "FooProperties",
-		Properties: properties,
-	}
-
-	moduleTypes := []*bpdoc.ModuleType{
-		&bpdoc.ModuleType{
-			Name: "foo_library",
-			PropertyStructs: []*bpdoc.PropertyStruct{
-				fooPropertyStruct,
-			},
-		},
-
-		&bpdoc.ModuleType{
-			Name: "foo_binary",
-			PropertyStructs: []*bpdoc.PropertyStruct{
-				fooPropertyStruct,
-			},
-		},
-		&bpdoc.ModuleType{
-			Name: "foo_test",
-			PropertyStructs: []*bpdoc.PropertyStruct{
-				fooPropertyStruct,
-			},
-		},
-	}
-
-	return [](*bpdoc.Package){
-		&bpdoc.Package{
-			Name:        "foo_language",
-			Path:        "android/soong/foo",
-			ModuleTypes: moduleTypes,
-		},
-	}
-}
-
-func TestGenerateModuleRuleShims(t *testing.T) {
-	ruleShims, err := createRuleShims(createPackageFixtures())
-	if err != nil {
-		panic(err)
-	}
-
-	if len(ruleShims) != 1 {
-		t.Errorf("Expected to generate 1 rule shim, but got %d", len(ruleShims))
-	}
-
-	fooRuleShim := ruleShims["foo"]
-	expectedRules := []string{"foo_binary", "foo_library", "foo_test_"}
-
-	if len(fooRuleShim.rules) != 3 {
-		t.Errorf("Expected 3 rules, but got %d", len(fooRuleShim.rules))
-	}
-
-	for i, rule := range fooRuleShim.rules {
-		if rule != expectedRules[i] {
-			t.Errorf("Expected rule shim to contain %s, but got %s", expectedRules[i], rule)
-		}
-	}
-
-	expectedBzl := `load("//build/bazel/queryview_rules:providers.bzl", "SoongModuleInfo")
-
-def _foo_binary_impl(ctx):
-    return [SoongModuleInfo()]
-
-foo_binary = rule(
-    implementation = _foo_binary_impl,
-    attrs = {
-        "module_name": attr.string(mandatory = True),
-        "module_variant": attr.string(),
-        "module_deps": attr.label_list(providers = [SoongModuleInfo]),
-        "bool_prop": attr.bool(),
-        "int64_prop": attr.int(),
-        "int_prop": attr.int(),
-#         "nested_prop__int_prop": attr.int(),
-#         "nested_prop__bool_prop": attr.bool(),
-#         "nested_prop__string_prop": attr.string(),
-        "string_list_prop": attr.string_list(),
-        "string_prop": attr.string(),
-    },
-)
-
-def _foo_library_impl(ctx):
-    return [SoongModuleInfo()]
-
-foo_library = rule(
-    implementation = _foo_library_impl,
-    attrs = {
-        "module_name": attr.string(mandatory = True),
-        "module_variant": attr.string(),
-        "module_deps": attr.label_list(providers = [SoongModuleInfo]),
-        "bool_prop": attr.bool(),
-        "int64_prop": attr.int(),
-        "int_prop": attr.int(),
-#         "nested_prop__int_prop": attr.int(),
-#         "nested_prop__bool_prop": attr.bool(),
-#         "nested_prop__string_prop": attr.string(),
-        "string_list_prop": attr.string_list(),
-        "string_prop": attr.string(),
-    },
-)
-
-def _foo_test__impl(ctx):
-    return [SoongModuleInfo()]
-
-foo_test_ = rule(
-    implementation = _foo_test__impl,
-    attrs = {
-        "module_name": attr.string(mandatory = True),
-        "module_variant": attr.string(),
-        "module_deps": attr.label_list(providers = [SoongModuleInfo]),
-        "bool_prop": attr.bool(),
-        "int64_prop": attr.int(),
-        "int_prop": attr.int(),
-#         "nested_prop__int_prop": attr.int(),
-#         "nested_prop__bool_prop": attr.bool(),
-#         "nested_prop__string_prop": attr.string(),
-        "string_list_prop": attr.string_list(),
-        "string_prop": attr.string(),
-    },
-)
-`
-
-	if fooRuleShim.content != expectedBzl {
-		t.Errorf(
-			"Expected the generated rule shim bzl to be:\n%s\nbut got:\n%s",
-			expectedBzl,
-			fooRuleShim.content)
-	}
-}
-
-func TestGenerateSoongModuleBzl(t *testing.T) {
-	ruleShims, err := createRuleShims(createPackageFixtures())
-	if err != nil {
-		panic(err)
-	}
-	actualSoongModuleBzl := generateSoongModuleBzl(ruleShims)
-
-	expectedLoad := "load(\"//build/bazel/queryview_rules:foo.bzl\", \"foo_binary\", \"foo_library\", \"foo_test_\")"
-	expectedRuleMap := `soong_module_rule_map = {
-    "foo_binary": foo_binary,
-    "foo_library": foo_library,
-    "foo_test_": foo_test_,
-}`
-	if !strings.Contains(actualSoongModuleBzl, expectedLoad) {
-		t.Errorf(
-			"Generated soong_module.bzl:\n\n%s\n\n"+
-				"Could not find the load statement in the generated soong_module.bzl:\n%s",
-			actualSoongModuleBzl,
-			expectedLoad)
-	}
-
-	if !strings.Contains(actualSoongModuleBzl, expectedRuleMap) {
-		t.Errorf(
-			"Generated soong_module.bzl:\n\n%s\n\n"+
-				"Could not find the module -> rule map in the generated soong_module.bzl:\n%s",
-			actualSoongModuleBzl,
-			expectedRuleMap)
-	}
-}
diff --git a/cmd/soong_build/writedocs.go b/cmd/soong_build/writedocs.go
index 253979e..f2c2c9b 100644
--- a/cmd/soong_build/writedocs.go
+++ b/cmd/soong_build/writedocs.go
@@ -20,7 +20,6 @@
 	"html/template"
 	"io/ioutil"
 	"path/filepath"
-	"reflect"
 	"sort"
 
 	"github.com/google/blueprint/bootstrap"
@@ -97,12 +96,8 @@
 }
 
 func getPackages(ctx *android.Context) ([]*bpdoc.Package, error) {
-	moduleTypeFactories := android.ModuleTypeFactories()
-	bpModuleTypeFactories := make(map[string]reflect.Value)
-	for moduleType, factory := range moduleTypeFactories {
-		bpModuleTypeFactories[moduleType] = reflect.ValueOf(factory)
-	}
-	return bootstrap.ModuleTypeDocs(ctx.Context, bpModuleTypeFactories)
+	moduleTypeFactories := android.ModuleTypeFactoriesForDocs()
+	return bootstrap.ModuleTypeDocs(ctx.Context, moduleTypeFactories)
 }
 
 func writeDocs(ctx *android.Context, filename string) error {
diff --git a/dexpreopt/class_loader_context.go b/dexpreopt/class_loader_context.go
index ab789aa..532d8fc 100644
--- a/dexpreopt/class_loader_context.go
+++ b/dexpreopt/class_loader_context.go
@@ -255,24 +255,13 @@
 
 // Add class loader context for the given library to the map entry for the given SDK version.
 func (clcMap ClassLoaderContextMap) addContext(ctx android.ModuleInstallPathContext, sdkVer int, lib string,
-	hostPath, installPath android.Path, strict bool, nestedClcMap ClassLoaderContextMap) error {
-
-	// If missing dependencies are allowed, the build shouldn't fail when a <uses-library> is
-	// not found. However, this is likely to result is disabling dexpreopt, as it won't be
-	// possible to construct class loader context without on-host and on-device library paths.
-	strict = strict && !ctx.Config().AllowMissingDependencies()
-
-	if hostPath == nil && strict {
-		return fmt.Errorf("unknown build path to <uses-library> \"%s\"", lib)
-	}
+	hostPath, installPath android.Path, nestedClcMap ClassLoaderContextMap) error {
 
 	devicePath := UnknownInstallLibraryPath
 	if installPath == nil {
 		if android.InList(lib, CompatUsesLibs) || android.InList(lib, OptionalCompatUsesLibs) {
 			// Assume that compatibility libraries are installed in /system/framework.
 			installPath = android.PathForModuleInstall(ctx, "framework", lib+".jar")
-		} else if strict {
-			return fmt.Errorf("unknown install path to <uses-library> \"%s\"", lib)
 		} else {
 			// For some stub libraries the only known thing is the name of their implementation
 			// library, but the library itself is unavailable (missing or part of a prebuilt). In
@@ -310,40 +299,17 @@
 	return nil
 }
 
-// Wrapper around addContext that reports errors.
-func (clcMap ClassLoaderContextMap) addContextOrReportError(ctx android.ModuleInstallPathContext, sdkVer int, lib string,
-	hostPath, installPath android.Path, strict bool, nestedClcMap ClassLoaderContextMap) {
-
-	err := clcMap.addContext(ctx, sdkVer, lib, hostPath, installPath, strict, nestedClcMap)
-	if err != nil {
-		ctx.ModuleErrorf(err.Error())
-	}
-}
-
-// Add class loader context. Fail on unknown build/install paths.
-func (clcMap ClassLoaderContextMap) AddContext(ctx android.ModuleInstallPathContext, lib string,
-	hostPath, installPath android.Path) {
-
-	clcMap.addContextOrReportError(ctx, AnySdkVersion, lib, hostPath, installPath, true, nil)
-}
-
-// Add class loader context if the library exists. Don't fail on unknown build/install paths.
-func (clcMap ClassLoaderContextMap) MaybeAddContext(ctx android.ModuleInstallPathContext, lib *string,
-	hostPath, installPath android.Path) {
-
-	if lib != nil {
-		clcMap.addContextOrReportError(ctx, AnySdkVersion, *lib, hostPath, installPath, false, nil)
-	}
-}
-
 // Add class loader context for the given SDK version. Don't fail on unknown build/install paths, as
 // libraries with unknown paths still need to be processed by manifest_fixer (which doesn't care
 // about paths). For the subset of libraries that are used in dexpreopt, their build/install paths
 // are validated later before CLC is used (in validateClassLoaderContext).
-func (clcMap ClassLoaderContextMap) AddContextForSdk(ctx android.ModuleInstallPathContext, sdkVer int,
+func (clcMap ClassLoaderContextMap) AddContext(ctx android.ModuleInstallPathContext, sdkVer int,
 	lib string, hostPath, installPath android.Path, nestedClcMap ClassLoaderContextMap) {
 
-	clcMap.addContextOrReportError(ctx, sdkVer, lib, hostPath, installPath, false, nestedClcMap)
+	err := clcMap.addContext(ctx, sdkVer, lib, hostPath, installPath, nestedClcMap)
+	if err != nil {
+		ctx.ModuleErrorf(err.Error())
+	}
 }
 
 // Merge the other class loader context map into this one, do not override existing entries.
diff --git a/dexpreopt/class_loader_context_test.go b/dexpreopt/class_loader_context_test.go
index 6b6b162..86f7871 100644
--- a/dexpreopt/class_loader_context_test.go
+++ b/dexpreopt/class_loader_context_test.go
@@ -18,6 +18,7 @@
 // For class loader context tests involving .bp files, see TestUsesLibraries in java package.
 
 import (
+	"fmt"
 	"reflect"
 	"strings"
 	"testing"
@@ -50,36 +51,30 @@
 
 	m := make(ClassLoaderContextMap)
 
-	m.AddContext(ctx, "a", buildPath(ctx, "a"), installPath(ctx, "a"))
-	m.AddContext(ctx, "b", buildPath(ctx, "b"), installPath(ctx, "b"))
-
-	// "Maybe" variant in the good case: add as usual.
-	c := "c"
-	m.MaybeAddContext(ctx, &c, buildPath(ctx, "c"), installPath(ctx, "c"))
-
-	// "Maybe" variant in the bad case: don't add library with unknown name, keep going.
-	m.MaybeAddContext(ctx, nil, nil, nil)
+	m.AddContext(ctx, AnySdkVersion, "a", buildPath(ctx, "a"), installPath(ctx, "a"), nil)
+	m.AddContext(ctx, AnySdkVersion, "b", buildPath(ctx, "b"), installPath(ctx, "b"), nil)
+	m.AddContext(ctx, AnySdkVersion, "c", buildPath(ctx, "c"), installPath(ctx, "c"), nil)
 
 	// Add some libraries with nested subcontexts.
 
 	m1 := make(ClassLoaderContextMap)
-	m1.AddContext(ctx, "a1", buildPath(ctx, "a1"), installPath(ctx, "a1"))
-	m1.AddContext(ctx, "b1", buildPath(ctx, "b1"), installPath(ctx, "b1"))
+	m1.AddContext(ctx, AnySdkVersion, "a1", buildPath(ctx, "a1"), installPath(ctx, "a1"), nil)
+	m1.AddContext(ctx, AnySdkVersion, "b1", buildPath(ctx, "b1"), installPath(ctx, "b1"), nil)
 
 	m2 := make(ClassLoaderContextMap)
-	m2.AddContext(ctx, "a2", buildPath(ctx, "a2"), installPath(ctx, "a2"))
-	m2.AddContext(ctx, "b2", buildPath(ctx, "b2"), installPath(ctx, "b2"))
-	m2.AddContextForSdk(ctx, AnySdkVersion, "c2", buildPath(ctx, "c2"), installPath(ctx, "c2"), m1)
+	m2.AddContext(ctx, AnySdkVersion, "a2", buildPath(ctx, "a2"), installPath(ctx, "a2"), nil)
+	m2.AddContext(ctx, AnySdkVersion, "b2", buildPath(ctx, "b2"), installPath(ctx, "b2"), nil)
+	m2.AddContext(ctx, AnySdkVersion, "c2", buildPath(ctx, "c2"), installPath(ctx, "c2"), m1)
 
 	m3 := make(ClassLoaderContextMap)
-	m3.AddContext(ctx, "a3", buildPath(ctx, "a3"), installPath(ctx, "a3"))
-	m3.AddContext(ctx, "b3", buildPath(ctx, "b3"), installPath(ctx, "b3"))
+	m3.AddContext(ctx, AnySdkVersion, "a3", buildPath(ctx, "a3"), installPath(ctx, "a3"), nil)
+	m3.AddContext(ctx, AnySdkVersion, "b3", buildPath(ctx, "b3"), installPath(ctx, "b3"), nil)
 
-	m.AddContextForSdk(ctx, AnySdkVersion, "d", buildPath(ctx, "d"), installPath(ctx, "d"), m2)
+	m.AddContext(ctx, AnySdkVersion, "d", buildPath(ctx, "d"), installPath(ctx, "d"), m2)
 	// When the same library is both in conditional and unconditional context, it should be removed
 	// from conditional context.
-	m.AddContextForSdk(ctx, 42, "f", buildPath(ctx, "f"), installPath(ctx, "f"), nil)
-	m.AddContextForSdk(ctx, AnySdkVersion, "f", buildPath(ctx, "f"), installPath(ctx, "f"), nil)
+	m.AddContext(ctx, 42, "f", buildPath(ctx, "f"), installPath(ctx, "f"), nil)
+	m.AddContext(ctx, AnySdkVersion, "f", buildPath(ctx, "f"), installPath(ctx, "f"), nil)
 
 	// Merge map with implicit root library that is among toplevel contexts => does nothing.
 	m.AddContextMap(m1, "c")
@@ -88,12 +83,12 @@
 	m.AddContextMap(m3, "m_g")
 
 	// Compatibility libraries with unknown install paths get default paths.
-	m.AddContextForSdk(ctx, 29, AndroidHidlManager, buildPath(ctx, AndroidHidlManager), nil, nil)
-	m.AddContextForSdk(ctx, 29, AndroidHidlBase, buildPath(ctx, AndroidHidlBase), nil, nil)
+	m.AddContext(ctx, 29, AndroidHidlManager, buildPath(ctx, AndroidHidlManager), nil, nil)
+	m.AddContext(ctx, 29, AndroidHidlBase, buildPath(ctx, AndroidHidlBase), nil, nil)
 
 	// Add "android.test.mock" to conditional CLC, observe that is gets removed because it is only
 	// needed as a compatibility library if "android.test.runner" is in CLC as well.
-	m.AddContextForSdk(ctx, 30, AndroidTestMock, buildPath(ctx, AndroidTestMock), nil, nil)
+	m.AddContext(ctx, 30, AndroidTestMock, buildPath(ctx, AndroidTestMock), nil, nil)
 
 	valid, validationError := validateClassLoaderContext(m)
 
@@ -160,31 +155,19 @@
 	})
 }
 
-// Test that an unexpected unknown build path causes immediate error.
-func TestCLCUnknownBuildPath(t *testing.T) {
-	ctx := testContext()
-	m := make(ClassLoaderContextMap)
-	err := m.addContext(ctx, AnySdkVersion, "a", nil, nil, true, nil)
-	checkError(t, err, "unknown build path to <uses-library> \"a\"")
-}
-
-// Test that an unexpected unknown install path causes immediate error.
-func TestCLCUnknownInstallPath(t *testing.T) {
-	ctx := testContext()
-	m := make(ClassLoaderContextMap)
-	err := m.addContext(ctx, AnySdkVersion, "a", buildPath(ctx, "a"), nil, true, nil)
-	checkError(t, err, "unknown install path to <uses-library> \"a\"")
-}
-
-func TestCLCMaybeAdd(t *testing.T) {
+// Test that unknown library paths cause a validation error.
+func testCLCUnknownPath(t *testing.T, whichPath string) {
 	ctx := testContext()
 
 	m := make(ClassLoaderContextMap)
-	a := "a"
-	m.MaybeAddContext(ctx, &a, nil, nil)
+	if whichPath == "build" {
+		m.AddContext(ctx, AnySdkVersion, "a", nil, nil, nil)
+	} else {
+		m.AddContext(ctx, AnySdkVersion, "a", buildPath(ctx, "a"), nil, nil)
+	}
 
 	// The library should be added to <uses-library> tags by the manifest_fixer.
-	t.Run("maybe add", func(t *testing.T) {
+	t.Run("uses libs", func(t *testing.T) {
 		haveUsesLibs := m.UsesLibs()
 		wantUsesLibs := []string{"a"}
 		if !reflect.DeepEqual(wantUsesLibs, haveUsesLibs) {
@@ -192,20 +175,28 @@
 		}
 	})
 
-	// But class loader context in such cases should raise an error on validation.
-	t.Run("validate", func(t *testing.T) {
-		_, err := validateClassLoaderContext(m)
-		checkError(t, err, "invalid build path for <uses-library> \"a\"")
-	})
+	// But CLC cannot be constructed: there is a validation error.
+	_, err := validateClassLoaderContext(m)
+	checkError(t, err, fmt.Sprintf("invalid %s path for <uses-library> \"a\"", whichPath))
+}
+
+// Test that unknown build path is an error.
+func TestCLCUnknownBuildPath(t *testing.T) {
+	testCLCUnknownPath(t, "build")
+}
+
+// Test that unknown install path is an error.
+func TestCLCUnknownInstallPath(t *testing.T) {
+	testCLCUnknownPath(t, "install")
 }
 
 // An attempt to add conditional nested subcontext should fail.
 func TestCLCNestedConditional(t *testing.T) {
 	ctx := testContext()
 	m1 := make(ClassLoaderContextMap)
-	m1.AddContextForSdk(ctx, 42, "a", buildPath(ctx, "a"), installPath(ctx, "a"), nil)
+	m1.AddContext(ctx, 42, "a", buildPath(ctx, "a"), installPath(ctx, "a"), nil)
 	m := make(ClassLoaderContextMap)
-	err := m.addContext(ctx, AnySdkVersion, "b", buildPath(ctx, "b"), installPath(ctx, "b"), true, m1)
+	err := m.addContext(ctx, AnySdkVersion, "b", buildPath(ctx, "b"), installPath(ctx, "b"), m1)
 	checkError(t, err, "nested class loader context shouldn't have conditional part")
 }
 
@@ -214,10 +205,10 @@
 func TestCLCSdkVersionOrder(t *testing.T) {
 	ctx := testContext()
 	m := make(ClassLoaderContextMap)
-	m.AddContextForSdk(ctx, 28, "a", buildPath(ctx, "a"), installPath(ctx, "a"), nil)
-	m.AddContextForSdk(ctx, 29, "b", buildPath(ctx, "b"), installPath(ctx, "b"), nil)
-	m.AddContextForSdk(ctx, 30, "c", buildPath(ctx, "c"), installPath(ctx, "c"), nil)
-	m.AddContextForSdk(ctx, AnySdkVersion, "d", buildPath(ctx, "d"), installPath(ctx, "d"), nil)
+	m.AddContext(ctx, 28, "a", buildPath(ctx, "a"), installPath(ctx, "a"), nil)
+	m.AddContext(ctx, 29, "b", buildPath(ctx, "b"), installPath(ctx, "b"), nil)
+	m.AddContext(ctx, 30, "c", buildPath(ctx, "c"), installPath(ctx, "c"), nil)
+	m.AddContext(ctx, AnySdkVersion, "d", buildPath(ctx, "d"), installPath(ctx, "d"), nil)
 
 	valid, validationError := validateClassLoaderContext(m)
 
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
index f52ecb4..6a80649 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -27,8 +27,9 @@
 // GlobalConfig stores the configuration for dex preopting. The fields are set
 // from product variables via dex_preopt_config.mk.
 type GlobalConfig struct {
-	DisablePreopt        bool     // disable preopt for all modules
-	DisablePreoptModules []string // modules with preopt disabled by product-specific config
+	DisablePreopt           bool     // disable preopt for all modules (excluding boot images)
+	DisablePreoptBootImages bool     // disable prepot for boot images
+	DisablePreoptModules    []string // modules with preopt disabled by product-specific config
 
 	OnlyPreoptBootImageAndSystemServer bool // only preopt jars in the boot image or system server
 
@@ -234,8 +235,9 @@
 		return ctx.Config().Once(testGlobalConfigOnceKey, func() interface{} {
 			// Nope, return a config with preopting disabled
 			return globalConfigAndRaw{&GlobalConfig{
-				DisablePreopt:          true,
-				DisableGenerateProfile: true,
+				DisablePreopt:           true,
+				DisablePreoptBootImages: true,
+				DisableGenerateProfile:  true,
 			}, nil}
 		})
 	}).(globalConfigAndRaw)
@@ -305,7 +307,7 @@
 	}
 }
 
-var dex2oatDepTag = struct {
+var Dex2oatDepTag = struct {
 	blueprint.BaseDependencyTag
 }{}
 
@@ -316,7 +318,7 @@
 func RegisterToolDeps(ctx android.BottomUpMutatorContext) {
 	dex2oatBin := dex2oatModuleName(ctx.Config())
 	v := ctx.Config().BuildOSTarget.Variations()
-	ctx.AddFarVariationDependencies(v, dex2oatDepTag, dex2oatBin)
+	ctx.AddFarVariationDependencies(v, Dex2oatDepTag, dex2oatBin)
 }
 
 func dex2oatPathFromDep(ctx android.ModuleContext) android.Path {
@@ -332,7 +334,7 @@
 	// prebuilt explicitly here instead.
 	var dex2oatModule android.Module
 	ctx.WalkDeps(func(child, parent android.Module) bool {
-		if parent == ctx.Module() && ctx.OtherModuleDependencyTag(child) == dex2oatDepTag {
+		if parent == ctx.Module() && ctx.OtherModuleDependencyTag(child) == Dex2oatDepTag {
 			// Found the source module, or prebuilt module that has replaced the source.
 			dex2oatModule = child
 			if p, ok := child.(android.PrebuiltInterface); ok && p.Prebuilt() != nil {
@@ -385,10 +387,10 @@
 
 // The main reason for this Once cache for GlobalSoongConfig is to make the
 // dex2oat path available to singletons. In ordinary modules we get it through a
-// dex2oatDepTag dependency, but in singletons there's no simple way to do the
+// Dex2oatDepTag dependency, but in singletons there's no simple way to do the
 // same thing and ensure the right variant is selected, hence this cache to make
 // the resolved path available to singletons. This means we depend on there
-// being at least one ordinary module with a dex2oatDepTag dependency.
+// being at least one ordinary module with a Dex2oatDepTag dependency.
 //
 // TODO(b/147613152): Implement a way to deal with dependencies from singletons,
 // and then possibly remove this cache altogether (but the use in
diff --git a/go.mod b/go.mod
index 117fa65..7297dea 100644
--- a/go.mod
+++ b/go.mod
@@ -8,4 +8,4 @@
 
 replace github.com/google/blueprint v0.0.0 => ../blueprint
 
-go 1.15.6
+go 1.15
diff --git a/java/androidmk.go b/java/androidmk.go
index aaad44f..cc454b0 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -196,6 +196,9 @@
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 			func(entries *android.AndroidMkEntries) {
 				entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", !Bool(prebuilt.properties.Installable))
+				if prebuilt.dexJarFile != nil {
+					entries.SetPath("LOCAL_SOONG_DEX_JAR", prebuilt.dexJarFile)
+				}
 				entries.SetPath("LOCAL_SOONG_HEADER_JAR", prebuilt.combinedClasspathFile)
 				entries.SetPath("LOCAL_SOONG_CLASSES_JAR", prebuilt.combinedClasspathFile)
 				entries.SetString("LOCAL_SDK_VERSION", prebuilt.makeSdkVersion())
diff --git a/java/androidmk_test.go b/java/androidmk_test.go
index 233e9d5..e2647cf 100644
--- a/java/androidmk_test.go
+++ b/java/androidmk_test.go
@@ -166,3 +166,25 @@
 		}
 	}
 }
+
+func TestImportSoongDexJar(t *testing.T) {
+	ctx, config := testJava(t, `
+		java_import {
+			name: "my-java-import",
+			jars: ["a.jar"],
+			prefer: true,
+			compile_dex: true,
+		}
+	`)
+
+	mod := ctx.ModuleForTests("my-java-import", "android_common").Module()
+	entries := android.AndroidMkEntriesForTest(t, config, "", mod)[0]
+	expectedSoongDexJar := buildDir + "/.intermediates/my-java-import/android_common/dex/my-java-import.jar"
+	actualSoongDexJar := entries.EntryMap["LOCAL_SOONG_DEX_JAR"]
+
+	if len(actualSoongDexJar) != 1 {
+		t.Errorf("LOCAL_SOONG_DEX_JAR incorrect len %d", len(actualSoongDexJar))
+	} else if actualSoongDexJar[0] != expectedSoongDexJar {
+		t.Errorf("LOCAL_SOONG_DEX_JAR mismatch, actual: %s, expected: %s", actualSoongDexJar[0], expectedSoongDexJar)
+	}
+}
diff --git a/java/app.go b/java/app.go
index 574472c..e6c9a2d 100755
--- a/java/app.go
+++ b/java/app.go
@@ -1226,7 +1226,7 @@
 			if tag, ok := ctx.OtherModuleDependencyTag(m).(usesLibraryDependencyTag); ok {
 				dep := ctx.OtherModuleName(m)
 				if lib, ok := m.(UsesLibraryDependency); ok {
-					clcMap.AddContextForSdk(ctx, tag.sdkVersion, dep,
+					clcMap.AddContext(ctx, tag.sdkVersion, dep,
 						lib.DexJarBuildPath(), lib.DexJarInstallPath(), lib.ClassLoaderContexts())
 				} else if ctx.Config().AllowMissingDependencies() {
 					ctx.AddMissingDependencies([]string{dep})
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index 062005b..0a71df1 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -347,8 +347,8 @@
 	ctx.RegisterSingletonType("dex_bootjars", dexpreoptBootJarsFactory)
 }
 
-func skipDexpreoptBootJars(ctx android.PathContext) bool {
-	return dexpreopt.GetGlobalConfig(ctx).DisablePreopt
+func SkipDexpreoptBootJars(ctx android.PathContext) bool {
+	return dexpreopt.GetGlobalConfig(ctx).DisablePreoptBootImages
 }
 
 // Singleton for generating boot image build rules.
@@ -371,7 +371,7 @@
 
 // Accessor function for the apex package. Returns nil if dexpreopt is disabled.
 func DexpreoptedArtApexJars(ctx android.BuilderContext) map[android.ArchType]android.OutputPaths {
-	if skipDexpreoptBootJars(ctx) {
+	if SkipDexpreoptBootJars(ctx) {
 		return nil
 	}
 	// Include dexpreopt files for the primary boot image.
@@ -387,7 +387,7 @@
 
 // Generate build rules for boot images.
 func (d *dexpreoptBootJars) GenerateBuildActions(ctx android.SingletonContext) {
-	if skipDexpreoptBootJars(ctx) {
+	if SkipDexpreoptBootJars(ctx) {
 		return
 	}
 	if dexpreopt.GetCachedGlobalSoongConfig(ctx) == nil {
@@ -422,16 +422,19 @@
 // Note that the same jar may occur in multiple modules.
 // This logic is tested in the apex package to avoid import cycle apex <-> java.
 func getBootImageJar(ctx android.SingletonContext, image *bootImageConfig, module android.Module) (int, android.Path) {
-	// Ignore any module that is not listed in the boot image configuration.
 	name := ctx.ModuleName(module)
+
+	// Strip a prebuilt_ prefix so that this can access the dex jar from a prebuilt module.
+	name = android.RemoveOptionalPrebuiltPrefix(name)
+
+	// Ignore any module that is not listed in the boot image configuration.
 	index := image.modules.IndexOfJar(name)
 	if index == -1 {
 		return -1, nil
 	}
 
-	// It is an error if a module configured in the boot image does not support
-	// accessing the dex jar. This is safe because every module that has the same
-	// name has to have the same module type.
+	// It is an error if a module configured in the boot image does not support accessing the dex jar.
+	// This is safe because every module that has the same name has to have the same module type.
 	jar, hasJar := module.(interface{ DexJarBuildPath() android.Path })
 	if !hasJar {
 		ctx.Errorf("module %q configured in boot image %q does not support accessing dex jar", module, image.name)
@@ -724,7 +727,7 @@
 	globalSoong := dexpreopt.GetCachedGlobalSoongConfig(ctx)
 	global := dexpreopt.GetGlobalConfig(ctx)
 
-	if global.DisableGenerateProfile || ctx.Config().UnbundledBuild() {
+	if global.DisableGenerateProfile {
 		return nil
 	}
 	profile := ctx.Config().Once(bootImageProfileRuleKey, func() interface{} {
diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go
index 419dc34..4bd255c 100644
--- a/java/hiddenapi_singleton.go
+++ b/java/hiddenapi_singleton.go
@@ -67,6 +67,19 @@
 
 	stubFlagsRule(ctx)
 
+	// If there is a prebuilt hiddenapi dir, generate rules to use the
+	// files within. Generally, we build the hiddenapi files from source
+	// during the build, ensuring consistency. It's possible, in a split
+	// build (framework and vendor) scenario, for the vendor build to use
+	// prebuilt hiddenapi files from the framework build. In this scenario,
+	// the framework and vendor builds must use the same source to ensure
+	// consistency.
+
+	if ctx.Config().PrebuiltHiddenApiDir(ctx) != "" {
+		h.flags = prebuiltFlagsRule(ctx)
+		return
+	}
+
 	// These rules depend on files located in frameworks/base, skip them if running in a tree that doesn't have them.
 	if ctx.Config().FrameworksBaseDirExists(ctx) {
 		h.flags = flagsRule(ctx)
@@ -212,6 +225,19 @@
 	rule.Build("hiddenAPIStubFlagsFile", "hiddenapi stub flags")
 }
 
+func prebuiltFlagsRule(ctx android.SingletonContext) android.Path {
+	outputPath := hiddenAPISingletonPaths(ctx).flags
+	inputPath := android.PathForSource(ctx, ctx.Config().PrebuiltHiddenApiDir(ctx), "hiddenapi-flags.csv")
+
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   android.Cp,
+		Output: outputPath,
+		Input:  inputPath,
+	})
+
+	return outputPath
+}
+
 // flagsRule creates a rule to build hiddenapi-flags.csv out of flags.csv files generated for boot image modules and
 // the unsupported API.
 func flagsRule(ctx android.SingletonContext) android.Path {
@@ -251,6 +277,8 @@
 		FlagWithInput("--unsupported ",
 			android.PathForSource(ctx, "frameworks/base/config/hiddenapi-unsupported.txt")).
 		FlagWithInput("--unsupported ", combinedRemovedApis).Flag("--ignore-conflicts ").FlagWithArg("--tag ", "removed").
+		FlagWithInput("--max-target-r ",
+			android.PathForSource(ctx, "frameworks/base/config/hiddenapi-max-target-r-loprio.txt")).FlagWithArg("--tag ", "lo-prio").
 		FlagWithInput("--max-target-q ",
 			android.PathForSource(ctx, "frameworks/base/config/hiddenapi-max-target-q.txt")).
 		FlagWithInput("--max-target-p ",
@@ -259,8 +287,6 @@
 			ctx, "frameworks/base/config/hiddenapi-max-target-o.txt")).Flag("--ignore-conflicts ").FlagWithArg("--tag ", "lo-prio").
 		FlagWithInput("--blocked ",
 			android.PathForSource(ctx, "frameworks/base/config/hiddenapi-force-blocked.txt")).
-		FlagWithInput("--blocked ",
-			android.PathForSource(ctx, "frameworks/base/config/hiddenapi-temp-blocklist.txt")).FlagWithArg("--tag ", "lo-prio").
 		FlagWithInput("--unsupported ", android.PathForSource(
 			ctx, "frameworks/base/config/hiddenapi-unsupported-packages.txt")).Flag("--packages ").
 		FlagWithOutput("--output ", tempPath)
@@ -391,6 +417,20 @@
 		return
 	}
 
+	if ctx.Config().PrebuiltHiddenApiDir(ctx) != "" {
+		outputPath := hiddenAPISingletonPaths(ctx).index
+		inputPath := android.PathForSource(ctx, ctx.Config().PrebuiltHiddenApiDir(ctx), "hiddenapi-index.csv")
+
+		ctx.Build(pctx, android.BuildParams{
+			Rule:   android.Cp,
+			Output: outputPath,
+			Input:  inputPath,
+		})
+
+		h.index = outputPath
+		return
+	}
+
 	indexes := android.Paths{}
 	ctx.VisitAllModules(func(module android.Module) {
 		if h, ok := module.(hiddenAPIIntf); ok {
diff --git a/java/hiddenapi_singleton_test.go b/java/hiddenapi_singleton_test.go
index 955c739..0f9ef58 100644
--- a/java/hiddenapi_singleton_test.go
+++ b/java/hiddenapi_singleton_test.go
@@ -23,9 +23,10 @@
 	"github.com/google/blueprint/proptools"
 )
 
-func testConfigWithBootJars(bp string, bootJars []string) android.Config {
+func testConfigWithBootJars(bp string, bootJars []string, prebuiltHiddenApiDir *string) android.Config {
 	config := testConfig(nil, bp, nil)
 	config.TestProductVariables.BootJars = android.CreateTestConfiguredJarList(bootJars)
+	config.TestProductVariables.PrebuiltHiddenApiDir = prebuiltHiddenApiDir
 	return config
 }
 
@@ -44,8 +45,8 @@
 	return ctx
 }
 
-func testHiddenAPIBootJars(t *testing.T, bp string, bootJars []string) (*android.TestContext, android.Config) {
-	config := testConfigWithBootJars(bp, bootJars)
+func testHiddenAPIBootJars(t *testing.T, bp string, bootJars []string, prebuiltHiddenApiDir *string) (*android.TestContext, android.Config) {
+	config := testConfigWithBootJars(bp, bootJars, prebuiltHiddenApiDir)
 
 	return testHiddenAPIWithConfig(t, config), config
 }
@@ -64,7 +65,7 @@
 			srcs: ["a.java"],
 			compile_dex: true,
 	}
-	`, []string{":foo"})
+	`, []string{":foo"}, nil)
 
 	hiddenAPI := ctx.SingletonForTests("hiddenapi")
 	hiddenapiRule := hiddenAPI.Rule("hiddenapi")
@@ -81,7 +82,7 @@
 			jars: ["a.jar"],
 			compile_dex: true,
 	}
-	`, []string{":foo"})
+	`, []string{":foo"}, nil)
 
 	hiddenAPI := ctx.SingletonForTests("hiddenapi")
 	hiddenapiRule := hiddenAPI.Rule("hiddenapi")
@@ -105,7 +106,7 @@
 			compile_dex: true,
 			prefer: false,
 	}
-	`, []string{":foo"})
+	`, []string{":foo"}, nil)
 
 	hiddenAPI := ctx.SingletonForTests("hiddenapi")
 	hiddenapiRule := hiddenAPI.Rule("hiddenapi")
@@ -134,7 +135,7 @@
 			compile_dex: true,
 			prefer: true,
 	}
-	`, []string{":foo"})
+	`, []string{":foo"}, nil)
 
 	hiddenAPI := ctx.SingletonForTests("hiddenapi")
 	hiddenapiRule := hiddenAPI.Rule("hiddenapi")
@@ -217,3 +218,48 @@
 	}
 	return generateDexPath(module)
 }
+
+func TestHiddenAPISingletonWithPrebuiltCsvFile(t *testing.T) {
+
+	// The idea behind this test is to ensure that when the build is
+	// confugured with a PrebuiltHiddenApiDir that the rules for the
+	// hiddenapi singleton copy the prebuilts to the typical output
+	// location, and then use that output location for the hiddenapi encode
+	// dex step.
+
+	// Where to find the prebuilt hiddenapi files:
+	prebuiltHiddenApiDir := "path/to/prebuilt/hiddenapi"
+
+	ctx, _ := testHiddenAPIBootJars(t, `
+		java_import {
+			name: "foo",
+			jars: ["a.jar"],
+			compile_dex: true,
+	}
+	`, []string{":foo"}, &prebuiltHiddenApiDir)
+
+	expectedCpInput := prebuiltHiddenApiDir + "/hiddenapi-flags.csv"
+	expectedCpOutput := buildDir + "/hiddenapi/hiddenapi-flags.csv"
+	expectedFlagsCsv := buildDir + "/hiddenapi/hiddenapi-flags.csv"
+
+	foo := ctx.ModuleForTests("foo", "android_common")
+
+	hiddenAPI := ctx.SingletonForTests("hiddenapi")
+	cpRule := hiddenAPI.Rule("Cp")
+	actualCpInput := cpRule.BuildParams.Input
+	actualCpOutput := cpRule.BuildParams.Output
+	encodeDexRule := foo.Rule("hiddenAPIEncodeDex")
+	actualFlagsCsv := encodeDexRule.BuildParams.Args["flagsCsv"]
+
+	if actualCpInput.String() != expectedCpInput {
+		t.Errorf("Prebuilt hiddenapi cp rule input mismatch, actual: %s, expected: %s", actualCpInput, expectedCpInput)
+	}
+
+	if actualCpOutput.String() != expectedCpOutput {
+		t.Errorf("Prebuilt hiddenapi cp rule output mismatch, actual: %s, expected: %s", actualCpOutput, expectedCpOutput)
+	}
+
+	if actualFlagsCsv != expectedFlagsCsv {
+		t.Errorf("Prebuilt hiddenapi encode dex rule flags csv mismatch, actual: %s, expected: %s", actualFlagsCsv, expectedFlagsCsv)
+	}
+}
diff --git a/java/java.go b/java/java.go
index 82b53be..59ec94d 100644
--- a/java/java.go
+++ b/java/java.go
@@ -40,20 +40,55 @@
 	// Register sdk member types.
 	android.RegisterSdkMemberType(javaHeaderLibsSdkMemberType)
 
+	// Register java implementation libraries for use only in module_exports (not sdk).
 	android.RegisterSdkMemberType(&librarySdkMemberType{
 		android.SdkMemberTypeBase{
 			PropertyName: "java_libs",
 		},
-		func(j *Library) android.Path {
+		func(_ android.SdkMemberContext, j *Library) android.Path {
 			implementationJars := j.ImplementationAndResourcesJars()
 			if len(implementationJars) != 1 {
 				panic(fmt.Errorf("there must be only one implementation jar from %q", j.Name()))
 			}
-
 			return implementationJars[0]
 		},
+		sdkSnapshotFilePathForJar,
+		copyEverythingToSnapshot,
 	})
 
+	// Register java boot libraries for use in sdk.
+	//
+	// The build has some implicit dependencies (via the boot jars configuration) on a number of
+	// modules, e.g. core-oj, apache-xml, that are part of the java boot class path and which are
+	// provided by mainline modules (e.g. art, conscrypt, runtime-i18n) but which are not otherwise
+	// used outside those mainline modules.
+	//
+	// As they are not needed outside the mainline modules adding them to the sdk/module-exports as
+	// either java_libs, or java_header_libs would end up exporting more information than was strictly
+	// necessary. The java_boot_libs property to allow those modules to be exported as part of the
+	// sdk/module_exports without exposing any unnecessary information.
+	android.RegisterSdkMemberType(&librarySdkMemberType{
+		android.SdkMemberTypeBase{
+			PropertyName: "java_boot_libs",
+			SupportsSdk:  true,
+		},
+		func(ctx android.SdkMemberContext, j *Library) android.Path {
+			// Java boot libs are only provided in the SDK to provide access to their dex implementation
+			// jar for use by dexpreopting and boot jars package check. They do not need to provide an
+			// actual implementation jar but the java_import will need a file that exists so just copy an
+			// empty file. Any attempt to use that file as a jar will cause a build error.
+			return ctx.SnapshotBuilder().EmptyFile()
+		},
+		func(osPrefix, name string) string {
+			// Create a special name for the implementation jar to try and provide some useful information
+			// to a developer that attempts to compile against this.
+			// TODO(b/175714559): Provide a proper error message in Soong not ninja.
+			return filepath.Join(osPrefix, "java_boot_libs", "snapshot", "jars", "are", "invalid", name+jarFileSuffix)
+		},
+		onlyCopyJarToSnapshot,
+	})
+
+	// Register java test libraries for use only in module_exports (not sdk).
 	android.RegisterSdkMemberType(&testSdkMemberType{
 		SdkMemberTypeBase: android.SdkMemberTypeBase{
 			PropertyName: "java_tests",
@@ -2165,9 +2200,22 @@
 
 	// Function to retrieve the appropriate output jar (implementation or header) from
 	// the library.
-	jarToExportGetter func(j *Library) android.Path
+	jarToExportGetter func(ctx android.SdkMemberContext, j *Library) android.Path
+
+	// Function to compute the snapshot relative path to which the named library's
+	// jar should be copied.
+	snapshotPathGetter func(osPrefix, name string) string
+
+	// True if only the jar should be copied to the snapshot, false if the jar plus any additional
+	// files like aidl files should also be copied.
+	onlyCopyJarToSnapshot bool
 }
 
+const (
+	onlyCopyJarToSnapshot    = true
+	copyEverythingToSnapshot = false
+)
+
 func (mt *librarySdkMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
 	mctx.AddVariationDependencies(nil, dependencyTag, names...)
 }
@@ -2195,21 +2243,32 @@
 func (p *librarySdkMemberProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) {
 	j := variant.(*Library)
 
-	p.JarToExport = ctx.MemberType().(*librarySdkMemberType).jarToExportGetter(j)
+	p.JarToExport = ctx.MemberType().(*librarySdkMemberType).jarToExportGetter(ctx, j)
+
 	p.AidlIncludeDirs = j.AidlIncludeDirs()
 }
 
 func (p *librarySdkMemberProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) {
 	builder := ctx.SnapshotBuilder()
 
+	memberType := ctx.MemberType().(*librarySdkMemberType)
+
 	exportedJar := p.JarToExport
 	if exportedJar != nil {
-		snapshotRelativeJavaLibPath := sdkSnapshotFilePathForJar(p.OsPrefix(), ctx.Name())
+		// Delegate the creation of the snapshot relative path to the member type.
+		snapshotRelativeJavaLibPath := memberType.snapshotPathGetter(p.OsPrefix(), ctx.Name())
+
+		// Copy the exported jar to the snapshot.
 		builder.CopyToSnapshot(exportedJar, snapshotRelativeJavaLibPath)
 
 		propertySet.AddProperty("jars", []string{snapshotRelativeJavaLibPath})
 	}
 
+	// Do not copy anything else to the snapshot.
+	if memberType.onlyCopyJarToSnapshot {
+		return
+	}
+
 	aidlIncludeDirs := p.AidlIncludeDirs
 	if len(aidlIncludeDirs) != 0 {
 		sdkModuleContext := ctx.SdkModuleContext()
@@ -2230,7 +2289,7 @@
 		PropertyName: "java_header_libs",
 		SupportsSdk:  true,
 	},
-	func(j *Library) android.Path {
+	func(_ android.SdkMemberContext, j *Library) android.Path {
 		headerJars := j.HeaderJars()
 		if len(headerJars) != 1 {
 			panic(fmt.Errorf("there must be only one header jar from %q", j.Name()))
@@ -2238,6 +2297,8 @@
 
 		return headerJars[0]
 	},
+	sdkSnapshotFilePathForJar,
+	copyEverythingToSnapshot,
 }
 
 // java_library builds and links sources into a `.jar` file for the device, and possibly for the host as well.
@@ -2770,6 +2831,10 @@
 	return nil
 }
 
+func (j *Import) LintDepSets() LintDepSets {
+	return LintDepSets{}
+}
+
 func (j *Import) DepsMutator(ctx android.BottomUpMutatorContext) {
 	ctx.AddVariationDependencies(nil, libTag, j.properties.Libs...)
 
@@ -2798,6 +2863,7 @@
 	j.classLoaderContexts = make(dexpreopt.ClassLoaderContextMap)
 
 	var flags javaBuilderFlags
+	var deapexerModule android.Module
 
 	ctx.VisitDirectDeps(func(module android.Module) {
 		tag := ctx.OtherModuleDependencyTag(module)
@@ -2818,6 +2884,11 @@
 		}
 
 		addCLCFromDep(ctx, module, j.classLoaderContexts)
+
+		// Save away the `deapexer` module on which this depends, if any.
+		if tag == android.DeapexerTag {
+			deapexerModule = module
+		}
 	})
 
 	if Bool(j.properties.Installable) {
@@ -2827,39 +2898,60 @@
 
 	j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.properties.Aidl.Export_include_dirs)
 
-	if ctx.Device() && Bool(j.dexProperties.Compile_dex) {
-		sdkDep := decodeSdkDep(ctx, sdkContext(j))
-		if sdkDep.invalidVersion {
-			ctx.AddMissingDependencies(sdkDep.bootclasspath)
-			ctx.AddMissingDependencies(sdkDep.java9Classpath)
-		} else if sdkDep.useFiles {
-			// sdkDep.jar is actually equivalent to turbine header.jar.
-			flags.classpath = append(flags.classpath, sdkDep.jars...)
+	if ctx.Device() {
+		// If this is a variant created for a prebuilt_apex then use the dex implementation jar
+		// obtained from the associated deapexer module.
+		ai := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+		if ai.ForPrebuiltApex {
+			if deapexerModule == nil {
+				// This should never happen as a variant for a prebuilt_apex is only created if the
+				// deapxer module has been configured to export the dex implementation jar for this module.
+				ctx.ModuleErrorf("internal error: module %q does not depend on a `deapexer` module for prebuilt_apex %q",
+					j.Name(), ai.ApexVariationName)
+			}
+
+			// Get the path of the dex implementation jar from the `deapexer` module.
+			di := ctx.OtherModuleProvider(deapexerModule, android.DeapexerProvider).(android.DeapexerInfo)
+			j.dexJarFile = di.PrebuiltExportPath(j.BaseModuleName(), ".dexjar")
+			if j.dexJarFile == nil {
+				// This should never happen as a variant for a prebuilt_apex is only created if the
+				// prebuilt_apex has been configured to export the java library dex file.
+				ctx.ModuleErrorf("internal error: no dex implementation jar available from prebuilt_apex %q", deapexerModule.Name())
+			}
+		} else if Bool(j.dexProperties.Compile_dex) {
+			sdkDep := decodeSdkDep(ctx, sdkContext(j))
+			if sdkDep.invalidVersion {
+				ctx.AddMissingDependencies(sdkDep.bootclasspath)
+				ctx.AddMissingDependencies(sdkDep.java9Classpath)
+			} else if sdkDep.useFiles {
+				// sdkDep.jar is actually equivalent to turbine header.jar.
+				flags.classpath = append(flags.classpath, sdkDep.jars...)
+			}
+
+			// Dex compilation
+
+			j.dexpreopter.installPath = android.PathForModuleInstall(ctx, "framework", jarName)
+			if j.dexProperties.Uncompress_dex == nil {
+				// If the value was not force-set by the user, use reasonable default based on the module.
+				j.dexProperties.Uncompress_dex = proptools.BoolPtr(shouldUncompressDex(ctx, &j.dexpreopter))
+			}
+			j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex
+
+			var dexOutputFile android.ModuleOutPath
+			dexOutputFile = j.dexer.compileDex(ctx, flags, j.minSdkVersion(), outputFile, jarName)
+			if ctx.Failed() {
+				return
+			}
+
+			configurationName := j.BaseModuleName()
+			primary := j.Prebuilt().UsePrebuilt()
+
+			// Hidden API CSV generation and dex encoding
+			dexOutputFile = j.hiddenAPI.hiddenAPI(ctx, configurationName, primary, dexOutputFile, outputFile,
+				proptools.Bool(j.dexProperties.Uncompress_dex))
+
+			j.dexJarFile = dexOutputFile
 		}
-
-		// Dex compilation
-
-		j.dexpreopter.installPath = android.PathForModuleInstall(ctx, "framework", jarName)
-		if j.dexProperties.Uncompress_dex == nil {
-			// If the value was not force-set by the user, use reasonable default based on the module.
-			j.dexProperties.Uncompress_dex = proptools.BoolPtr(shouldUncompressDex(ctx, &j.dexpreopter))
-		}
-		j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex
-
-		var dexOutputFile android.ModuleOutPath
-		dexOutputFile = j.dexer.compileDex(ctx, flags, j.minSdkVersion(), outputFile, jarName)
-		if ctx.Failed() {
-			return
-		}
-
-		configurationName := j.BaseModuleName()
-		primary := j.Prebuilt().UsePrebuilt()
-
-		// Hidden API CSV generation and dex encoding
-		dexOutputFile = j.hiddenAPI.hiddenAPI(ctx, configurationName, primary, dexOutputFile, outputFile,
-			proptools.Bool(j.dexProperties.Uncompress_dex))
-
-		j.dexJarFile = dexOutputFile
 	}
 }
 
@@ -3305,7 +3397,7 @@
 	}
 
 	if implicitSdkLib != nil {
-		clcMap.AddContextForSdk(ctx, dexpreopt.AnySdkVersion, *implicitSdkLib,
+		clcMap.AddContext(ctx, dexpreopt.AnySdkVersion, *implicitSdkLib,
 			dep.DexJarBuildPath(), dep.DexJarInstallPath(), dep.ClassLoaderContexts())
 	} else {
 		depName := ctx.OtherModuleName(depModule)
diff --git a/java/java_test.go b/java/java_test.go
index a2466f9..1c0738f 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -867,83 +867,43 @@
 		return config
 	}
 
-	isValidDependency := func(configInfo testConfigInfo) bool {
-		if configInfo.enforceVendorInterface == false {
-			return true
-		}
-
-		if configInfo.enforceJavaSdkLibraryCheck == false {
-			return true
-		}
-
-		if inList("bar", configInfo.allowList) {
-			return true
-		}
-
-		if configInfo.libraryType == "java_library" {
-			if configInfo.fromPartition != configInfo.toPartition {
-				if !configInfo.enforceProductInterface &&
-					((configInfo.fromPartition == "system" && configInfo.toPartition == "product") ||
-						(configInfo.fromPartition == "product" && configInfo.toPartition == "system")) {
-					return true
-				}
-				return false
-			}
-		}
-
-		return true
-	}
-
 	errorMessage := "is not allowed across the partitions"
 
-	allPartitionCombinations := func() [][2]string {
-		var result [][2]string
-		partitions := []string{"system", "vendor", "product"}
+	testJavaWithConfig(t, createTestConfig(testConfigInfo{
+		libraryType:                "java_library",
+		fromPartition:              "product",
+		toPartition:                "system",
+		enforceVendorInterface:     true,
+		enforceProductInterface:    true,
+		enforceJavaSdkLibraryCheck: false,
+	}))
 
-		for _, fromPartition := range partitions {
-			for _, toPartition := range partitions {
-				result = append(result, [2]string{fromPartition, toPartition})
-			}
-		}
+	testJavaWithConfig(t, createTestConfig(testConfigInfo{
+		libraryType:                "java_library",
+		fromPartition:              "product",
+		toPartition:                "system",
+		enforceVendorInterface:     true,
+		enforceProductInterface:    false,
+		enforceJavaSdkLibraryCheck: true,
+	}))
 
-		return result
-	}
+	testJavaErrorWithConfig(t, errorMessage, createTestConfig(testConfigInfo{
+		libraryType:                "java_library",
+		fromPartition:              "product",
+		toPartition:                "system",
+		enforceVendorInterface:     true,
+		enforceProductInterface:    true,
+		enforceJavaSdkLibraryCheck: true,
+	}))
 
-	allFlagCombinations := func() [][3]bool {
-		var result [][3]bool
-		flagValues := [2]bool{false, true}
-
-		for _, vendorInterface := range flagValues {
-			for _, productInterface := range flagValues {
-				for _, enableEnforce := range flagValues {
-					result = append(result, [3]bool{vendorInterface, productInterface, enableEnforce})
-				}
-			}
-		}
-
-		return result
-	}
-
-	for _, libraryType := range []string{"java_library", "java_sdk_library"} {
-		for _, partitionValues := range allPartitionCombinations() {
-			for _, flagValues := range allFlagCombinations() {
-				testInfo := testConfigInfo{
-					libraryType:                libraryType,
-					fromPartition:              partitionValues[0],
-					toPartition:                partitionValues[1],
-					enforceVendorInterface:     flagValues[0],
-					enforceProductInterface:    flagValues[1],
-					enforceJavaSdkLibraryCheck: flagValues[2],
-				}
-
-				if isValidDependency(testInfo) {
-					testJavaWithConfig(t, createTestConfig(testInfo))
-				} else {
-					testJavaErrorWithConfig(t, errorMessage, createTestConfig(testInfo))
-				}
-			}
-		}
-	}
+	testJavaErrorWithConfig(t, errorMessage, createTestConfig(testConfigInfo{
+		libraryType:                "java_library",
+		fromPartition:              "vendor",
+		toPartition:                "system",
+		enforceVendorInterface:     true,
+		enforceProductInterface:    true,
+		enforceJavaSdkLibraryCheck: true,
+	}))
 
 	testJavaWithConfig(t, createTestConfig(testConfigInfo{
 		libraryType:                "java_library",
@@ -958,11 +918,37 @@
 	testJavaErrorWithConfig(t, errorMessage, createTestConfig(testConfigInfo{
 		libraryType:                "java_library",
 		fromPartition:              "vendor",
+		toPartition:                "product",
+		enforceVendorInterface:     true,
+		enforceProductInterface:    true,
+		enforceJavaSdkLibraryCheck: true,
+	}))
+
+	testJavaWithConfig(t, createTestConfig(testConfigInfo{
+		libraryType:                "java_sdk_library",
+		fromPartition:              "product",
 		toPartition:                "system",
 		enforceVendorInterface:     true,
 		enforceProductInterface:    true,
 		enforceJavaSdkLibraryCheck: true,
-		allowList:                  []string{"foo"},
+	}))
+
+	testJavaWithConfig(t, createTestConfig(testConfigInfo{
+		libraryType:                "java_sdk_library",
+		fromPartition:              "vendor",
+		toPartition:                "system",
+		enforceVendorInterface:     true,
+		enforceProductInterface:    true,
+		enforceJavaSdkLibraryCheck: true,
+	}))
+
+	testJavaWithConfig(t, createTestConfig(testConfigInfo{
+		libraryType:                "java_sdk_library",
+		fromPartition:              "vendor",
+		toPartition:                "product",
+		enforceVendorInterface:     true,
+		enforceProductInterface:    true,
+		enforceJavaSdkLibraryCheck: true,
 	}))
 }
 
diff --git a/rust/binary.go b/rust/binary.go
index c2d97f3..ca07d07 100644
--- a/rust/binary.go
+++ b/rust/binary.go
@@ -164,3 +164,7 @@
 	}
 	return binary.baseCompiler.stdLinkage(ctx)
 }
+
+func (binary *binaryDecorator) isDependencyRoot() bool {
+	return true
+}
diff --git a/rust/compiler.go b/rust/compiler.go
index ee88a27..bcea6cc 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -236,6 +236,10 @@
 	panic(fmt.Errorf("baseCrater doesn't know how to crate things!"))
 }
 
+func (compiler *baseCompiler) isDependencyRoot() bool {
+	return false
+}
+
 func (compiler *baseCompiler) compilerDeps(ctx DepsContext, deps Deps) Deps {
 	deps.Rlibs = append(deps.Rlibs, compiler.Properties.Rlibs...)
 	deps.Dylibs = append(deps.Dylibs, compiler.Properties.Dylibs...)
diff --git a/rust/config/global.go b/rust/config/global.go
index b7fff4a..08ec877 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -24,7 +24,7 @@
 var pctx = android.NewPackageContext("android/soong/rust/config")
 
 var (
-	RustDefaultVersion = "1.48.0"
+	RustDefaultVersion = "1.49.0"
 	RustDefaultBase    = "prebuilts/rust/"
 	DefaultEdition     = "2018"
 	Stdlibs            = []string{
diff --git a/rust/rust.go b/rust/rust.go
index 1053846..1fa97af 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -106,6 +106,42 @@
 	hideApexVariantFromMake bool
 }
 
+func (mod *Module) Header() bool {
+	//TODO: If Rust libraries provide header variants, this needs to be updated.
+	return false
+}
+
+func (mod *Module) SetPreventInstall() {
+	mod.Properties.PreventInstall = true
+}
+
+// Returns true if the module is "vendor" variant. Usually these modules are installed in /vendor
+func (mod *Module) InVendor() bool {
+	return mod.Properties.ImageVariationPrefix == cc.VendorVariationPrefix
+}
+
+func (mod *Module) SetHideFromMake() {
+	mod.Properties.HideFromMake = true
+}
+
+func (mod *Module) SanitizePropDefined() bool {
+	return false
+}
+
+func (mod *Module) IsDependencyRoot() bool {
+	if mod.compiler != nil {
+		return mod.compiler.isDependencyRoot()
+	}
+	panic("IsDependencyRoot called on a non-compiler Rust module")
+}
+
+func (mod *Module) IsPrebuilt() bool {
+	if _, ok := mod.compiler.(*prebuiltLibraryDecorator); ok {
+		return true
+	}
+	return false
+}
+
 func (mod *Module) OutputFiles(tag string) (android.Paths, error) {
 	switch tag {
 	case "":
@@ -281,6 +317,7 @@
 	SetDisabled()
 
 	stdLinkage(ctx *depsContext) RustLinkage
+	isDependencyRoot() bool
 }
 
 type exportedFlagsProducer interface {
diff --git a/scripts/build-mainline-modules.sh b/scripts/build-mainline-modules.sh
index 6db870f..ea62af4 100755
--- a/scripts/build-mainline-modules.sh
+++ b/scripts/build-mainline-modules.sh
@@ -26,6 +26,7 @@
   platform-mainline-test-exports
   runtime-module-host-exports
   runtime-module-sdk
+  tzdata-module-test-exports
 )
 
 # List of libraries installed on the platform that are needed for ART chroot
diff --git a/scripts/manifest_fixer.py b/scripts/manifest_fixer.py
index c59732b..55d0fd1 100755
--- a/scripts/manifest_fixer.py
+++ b/scripts/manifest_fixer.py
@@ -121,7 +121,7 @@
       # is empty.  Set it to something low so that it will be overriden by the
       # main manifest, but high enough that it doesn't cause implicit
       # permissions grants.
-      target_attr.value = '15'
+      target_attr.value = '16'
     else:
       target_attr.value = target_sdk_version
     element.setAttributeNode(target_attr)
diff --git a/scripts/manifest_fixer_test.py b/scripts/manifest_fixer_test.py
index d6e7f26..3a0a25d 100755
--- a/scripts/manifest_fixer_test.py
+++ b/scripts/manifest_fixer_test.py
@@ -173,7 +173,7 @@
     """Tests inserting targetSdkVersion when minSdkVersion exists."""
 
     manifest_input = self.manifest_tmpl % self.uses_sdk(min='27')
-    expected = self.manifest_tmpl % self.uses_sdk(min='28', target='15')
+    expected = self.manifest_tmpl % self.uses_sdk(min='28', target='16')
     output = self.raise_min_sdk_version_test(manifest_input, '28', '29', True)
     self.assertEqual(output, expected)
 
@@ -189,7 +189,7 @@
     """Tests inserting targetSdkVersion when minSdkVersion does not exist."""
 
     manifest_input = self.manifest_tmpl % ''
-    expected = self.manifest_tmpl % self.uses_sdk(min='28', target='15')
+    expected = self.manifest_tmpl % self.uses_sdk(min='28', target='16')
     output = self.raise_min_sdk_version_test(manifest_input, '28', '29', True)
     self.assertEqual(output, expected)
 
diff --git a/scripts/unpack-prebuilt-apex.sh b/scripts/unpack-prebuilt-apex.sh
new file mode 100755
index 0000000..1acdeb5
--- /dev/null
+++ b/scripts/unpack-prebuilt-apex.sh
@@ -0,0 +1,59 @@
+#!/bin/bash
+
+set -eu
+
+# 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.
+
+# Tool to unpack an apex file and verify that the required files were extracted.
+if [ $# -lt 5 ]; then
+  echo "usage: $0 <deapaxer_path> <debugfs_path> <apex file> <output_dir> <required_files>+" >&2
+  exit 1
+fi
+
+DEAPEXER_PATH=$1
+DEBUGFS_PATH=$2
+APEX_FILE=$3
+OUTPUT_DIR=$4
+shift 4
+REQUIRED_PATHS=$@
+
+set -x 1
+
+rm -fr $OUTPUT_DIR
+mkdir -p $OUTPUT_DIR
+
+# Unpack the apex file contents.
+$DEAPEXER_PATH --debugfs_path $DEBUGFS_PATH extract $APEX_FILE $OUTPUT_DIR
+
+# Verify that the files that the build expects to be in the .apex file actually
+# exist, and make sure they have a fresh mtime to not confuse ninja.
+typeset -i FAILED=0
+for r in $REQUIRED_PATHS; do
+  if [ ! -f $r ]; then
+    echo "Required file $r not present in apex $APEX_FILE" >&2
+    FAILED=$FAILED+1
+  else
+    # TODO(http:/b/177646343) - deapexer extracts the files with a timestamp of 1 Jan 1970.
+    # touch the file so that ninja knows it has changed.
+    touch $r
+  fi
+done
+
+if [ $FAILED -gt 0 ]; then
+  echo "$FAILED required files were missing from $APEX_FILE" >&2
+  echo "Available files are:" >&2
+  find $OUTPUT_DIR -type f | sed "s|^|    |" >&2
+  exit 1
+fi
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index d989c5b..488afd8 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -472,6 +472,61 @@
 	)
 }
 
+func TestSnapshotWithJavaBootLibrary(t *testing.T) {
+	result := testSdkWithJava(t, `
+		module_exports {
+			name: "myexports",
+			java_boot_libs: ["myjavalib"],
+		}
+
+		java_library {
+			name: "myjavalib",
+			srcs: ["Test.java"],
+			java_resources: ["resource.txt"],
+			// The aidl files should not be copied to the snapshot because a java_boot_libs member is not
+			// intended to be used for compiling Java, only for accessing the dex implementation jar.
+			aidl: {
+				export_include_dirs: ["aidl"],
+			},
+			system_modules: "none",
+			sdk_version: "none",
+			compile_dex: true,
+		}
+	`)
+
+	result.CheckSnapshot("myexports", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+    name: "myexports_myjavalib@current",
+    sdk_member_name: "myjavalib",
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    jars: ["java_boot_libs/snapshot/jars/are/invalid/myjavalib.jar"],
+}
+
+java_import {
+    name: "myjavalib",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    jars: ["java_boot_libs/snapshot/jars/are/invalid/myjavalib.jar"],
+}
+
+module_exports_snapshot {
+    name: "myexports@current",
+    visibility: ["//visibility:public"],
+    java_boot_libs: ["myexports_myjavalib@current"],
+}
+
+`),
+		checkAllCopyRules(`
+.intermediates/myexports/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/myjavalib.jar
+`),
+	)
+}
+
 func TestHostSnapshotWithJavaImplLibrary(t *testing.T) {
 	result := testSdkWithJava(t, `
 		module_exports {
diff --git a/sdk/update.go b/sdk/update.go
index 377aaae..b5bc9f4 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -653,6 +653,9 @@
 	filesToZip  android.Paths
 	zipsToMerge android.Paths
 
+	// The path to an empty file.
+	emptyFile android.WritablePath
+
 	prebuiltModules map[string]*bpModule
 	prebuiltOrder   []*bpModule
 
@@ -703,6 +706,19 @@
 	s.zipsToMerge = append(s.zipsToMerge, tmpZipPath)
 }
 
+func (s *snapshotBuilder) EmptyFile() android.Path {
+	if s.emptyFile == nil {
+		ctx := s.ctx
+		s.emptyFile = android.PathForModuleOut(ctx, "empty")
+		s.ctx.Build(pctx, android.BuildParams{
+			Rule:   android.Touch,
+			Output: s.emptyFile,
+		})
+	}
+
+	return s.emptyFile
+}
+
 func (s *snapshotBuilder) AddPrebuiltModule(member android.SdkMember, moduleType string) android.BpModule {
 	name := member.Name()
 	if s.prebuiltModules[name] != nil {
diff --git a/sh/sh_binary.go b/sh/sh_binary.go
index 8db33a3..18749b5 100644
--- a/sh/sh_binary.go
+++ b/sh/sh_binary.go
@@ -345,15 +345,8 @@
 		depTag := ctx.OtherModuleDependencyTag(dep)
 		switch depTag {
 		case shTestDataBinsTag, shTestDataDeviceBinsTag:
-			if cc, isCc := dep.(*cc.Module); isCc {
-				s.addToDataModules(ctx, cc.OutputFile().Path().Base(), cc.OutputFile().Path())
-				return
-			}
-			property := "data_bins"
-			if depTag == shTestDataDeviceBinsTag {
-				property = "data_device_bins"
-			}
-			ctx.PropertyErrorf(property, "%q of type %q is not supported", dep.Name(), ctx.OtherModuleType(dep))
+			path := android.OutputFileForModule(ctx, dep, "")
+			s.addToDataModules(ctx, path.Base(), path)
 		case shTestDataLibsTag, shTestDataDeviceLibsTag:
 			if cc, isCc := dep.(*cc.Module); isCc {
 				// Copy to an intermediate output directory to append "lib[64]" to the path,
diff --git a/sysprop/sysprop_library.go b/sysprop/sysprop_library.go
index 38306ad..2ccce15 100644
--- a/sysprop/sysprop_library.go
+++ b/sysprop/sysprop_library.go
@@ -19,6 +19,7 @@
 import (
 	"fmt"
 	"io"
+	"os"
 	"path"
 	"sync"
 
@@ -125,8 +126,8 @@
 	properties syspropLibraryProperties
 
 	checkApiFileTimeStamp android.WritablePath
-	latestApiFile         android.Path
-	currentApiFile        android.Path
+	latestApiFile         android.OptionalPath
+	currentApiFile        android.OptionalPath
 	dumpedApiFile         android.WritablePath
 }
 
@@ -224,7 +225,7 @@
 	return proptools.Bool(m.properties.Public_stub)
 }
 
-func (m *syspropLibrary) CurrentSyspropApiFile() android.Path {
+func (m *syspropLibrary) CurrentSyspropApiFile() android.OptionalPath {
 	return m.currentApiFile
 }
 
@@ -243,8 +244,11 @@
 		return
 	}
 
-	m.currentApiFile = android.PathForSource(ctx, ctx.ModuleDir(), "api", baseModuleName+"-current.txt")
-	m.latestApiFile = android.PathForSource(ctx, ctx.ModuleDir(), "api", baseModuleName+"-latest.txt")
+	apiDirectoryPath := path.Join(ctx.ModuleDir(), "api")
+	currentApiFilePath := path.Join(apiDirectoryPath, baseModuleName+"-current.txt")
+	latestApiFilePath := path.Join(apiDirectoryPath, baseModuleName+"-latest.txt")
+	m.currentApiFile = android.ExistentPathForSource(ctx, currentApiFilePath)
+	m.latestApiFile = android.ExistentPathForSource(ctx, latestApiFilePath)
 
 	// dump API rule
 	rule := android.NewRuleBuilder(pctx, ctx)
@@ -258,19 +262,37 @@
 	// check API rule
 	rule = android.NewRuleBuilder(pctx, ctx)
 
+	// We allow that the API txt files don't exist, when the sysprop_library only contains internal
+	// properties. But we have to feed current api file and latest api file to the rule builder.
+	// Currently we can't get android.Path representing the null device, so we add any existing API
+	// txt files to implicits, and then directly feed string paths, rather than calling Input(Path)
+	// method.
+	var apiFileList android.Paths
+	currentApiArgument := os.DevNull
+	if m.currentApiFile.Valid() {
+		apiFileList = append(apiFileList, m.currentApiFile.Path())
+		currentApiArgument = m.currentApiFile.String()
+	}
+
+	latestApiArgument := os.DevNull
+	if m.latestApiFile.Valid() {
+		apiFileList = append(apiFileList, m.latestApiFile.Path())
+		latestApiArgument = m.latestApiFile.String()
+	}
+
 	// 1. compares current.txt to api-dump.txt
 	// current.txt should be identical to api-dump.txt.
 	msg := fmt.Sprintf(`\n******************************\n`+
 		`API of sysprop_library %s doesn't match with current.txt\n`+
 		`Please update current.txt by:\n`+
-		`m %s-dump-api && rm -rf %q && cp -f %q %q\n`+
+		`m %s-dump-api && mkdir -p %q && rm -rf %q && cp -f %q %q\n`+
 		`******************************\n`, baseModuleName, baseModuleName,
-		m.currentApiFile.String(), m.dumpedApiFile.String(), m.currentApiFile.String())
+		apiDirectoryPath, currentApiFilePath, m.dumpedApiFile.String(), currentApiFilePath)
 
 	rule.Command().
 		Text("( cmp").Flag("-s").
 		Input(m.dumpedApiFile).
-		Input(m.currentApiFile).
+		Text(currentApiArgument).
 		Text("|| ( echo").Flag("-e").
 		Flag(`"` + msg + `"`).
 		Text("; exit 38) )")
@@ -285,11 +307,12 @@
 	rule.Command().
 		Text("( ").
 		BuiltTool("sysprop_api_checker").
-		Input(m.latestApiFile).
-		Input(m.currentApiFile).
+		Text(latestApiArgument).
+		Text(currentApiArgument).
 		Text(" || ( echo").Flag("-e").
 		Flag(`"` + msg + `"`).
-		Text("; exit 38) )")
+		Text("; exit 38) )").
+		Implicits(apiFileList)
 
 	m.checkApiFileTimeStamp = android.PathForModuleOut(ctx, "check_api.timestamp")
 
@@ -393,31 +416,6 @@
 		ctx.PropertyErrorf("srcs", "sysprop_library must specify srcs")
 	}
 
-	missingApi := false
-
-	for _, txt := range []string{"-current.txt", "-latest.txt"} {
-		path := path.Join(ctx.ModuleDir(), "api", m.BaseModuleName()+txt)
-		file := android.ExistentPathForSource(ctx, path)
-		if !file.Valid() {
-			ctx.ModuleErrorf("API file %#v doesn't exist", path)
-			missingApi = true
-		}
-	}
-
-	if missingApi {
-		script := "build/soong/scripts/gen-sysprop-api-files.sh"
-		p := android.ExistentPathForSource(ctx, script)
-
-		if !p.Valid() {
-			panic(fmt.Sprintf("script file %s doesn't exist", script))
-		}
-
-		ctx.ModuleErrorf("One or more api files are missing. "+
-			"You can create them by:\n"+
-			"%s %q %q", script, ctx.ModuleDir(), m.BaseModuleName())
-		return
-	}
-
 	// ctx's Platform or Specific functions represent where this sysprop_library installed.
 	installedInSystem := ctx.Platform() || ctx.SystemExtSpecific()
 	installedInVendorOrOdm := ctx.SocSpecific() || ctx.DeviceSpecific()