Merge "Migrate Rust to LLVM coverage."
diff --git a/Android.bp b/Android.bp
index 866ed25..1012dba 100644
--- a/Android.bp
+++ b/Android.bp
@@ -222,3 +222,8 @@
     srcs: [":linker"],
     out: ["linker.flags"],
 }
+
+// Instantiate the dex_bootjars singleton module.
+dex_bootjars {
+    name: "dex_bootjars",
+}
diff --git a/README.md b/README.md
index f1857f8..b7e93f4 100644
--- a/README.md
+++ b/README.md
@@ -430,14 +430,24 @@
 
 soong_config_string_variable {
     name: "board",
-    values: ["soc_a", "soc_b"],
+    values: ["soc_a", "soc_b", "soc_c"],
 }
 ```
 
 This example describes a new `acme_cc_defaults` module type that extends the
 `cc_defaults` module type, with three additional conditionals based on
 variables `board`, `feature` and `width`, which can affect properties `cflags`
-and `srcs`.
+and `srcs`. Additionally, each conditional will contain a `conditions_default`
+property can affect `cflags` and `srcs` in the following conditions:
+
+* bool variable (e.g. `feature`): the variable is unspecified or not set to a true value
+* value variable (e.g. `width`): the variable is unspecified
+* string variable (e.g. `board`): the variable is unspecified or the variable is set to a string unused in the
+given module. For example, with `board`, if the `board`
+conditional contains the properties `soc_a` and `conditions_default`, when
+board=soc_b, the `cflags` and `srcs` values under `conditions_default` will be
+used. To specify that no properties should be amended for `soc_b`, you can set
+`soc_b: {},`.
 
 The values of the variables can be set from a product's `BoardConfig.mk` file:
 ```
@@ -445,6 +455,7 @@
 SOONG_CONFIG_acme += \
     board \
     feature \
+    width \
 
 SOONG_CONFIG_acme_board := soc_a
 SOONG_CONFIG_acme_feature := true
@@ -473,12 +484,21 @@
             soc_b: {
                 cflags: ["-DSOC_B"],
             },
+            conditions_default: {
+                cflags: ["-DSOC_DEFAULT"],
+            },
         },
         feature: {
             cflags: ["-DFEATURE"],
+            conditions_default: {
+                cflags: ["-DFEATURE_DEFAULT"],
+            },
         },
         width: {
             cflags: ["-DWIDTH=%s"],
+            conditions_default: {
+                cflags: ["-DWIDTH=DEFAULT"],
+            },
         },
     },
 }
@@ -490,8 +510,37 @@
 }
 ```
 
-With the `BoardConfig.mk` snippet above, libacme_foo would build with
-cflags "-DGENERIC -DSOC_A -DFEATURE -DWIDTH=200".
+With the `BoardConfig.mk` snippet above, `libacme_foo` would build with
+`cflags: "-DGENERIC -DSOC_A -DFEATURE -DWIDTH=200"`.
+
+Alternatively, with `DefaultBoardConfig.mk`:
+
+```
+SOONG_CONFIG_NAMESPACES += acme
+SOONG_CONFIG_acme += \
+    board \
+    feature \
+    width \
+
+SOONG_CONFIG_acme_feature := false
+```
+
+then `libacme_foo` would build with `cflags: "-DGENERIC -DSOC_DEFAULT -DFEATURE_DEFAULT -DSIZE=DEFAULT"`.
+
+Alternatively, with `DefaultBoardConfig.mk`:
+
+```
+SOONG_CONFIG_NAMESPACES += acme
+SOONG_CONFIG_acme += \
+    board \
+    feature \
+    width \
+
+SOONG_CONFIG_acme_board := soc_c
+```
+
+then `libacme_foo` would build with `cflags: "-DGENERIC -DSOC_DEFAULT
+-DFEATURE_DEFAULT -DSIZE=DEFAULT"`.
 
 `soong_config_module_type` modules will work best when used to wrap defaults
 modules (`cc_defaults`, `java_defaults`, etc.), which can then be referenced
diff --git a/android/Android.bp b/android/Android.bp
index efa70a9..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",
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/arch.go b/android/arch.go
index 34f9b29..baee9be 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -617,11 +617,15 @@
 	}
 
 	// Some modules want compile_multilib: "first" to mean 32-bit, not 64-bit.
-	// This is used for Windows support and for HOST_PREFER_32_BIT=true support for Art modules.
+	// This is used for HOST_PREFER_32_BIT=true support for Art modules.
 	prefer32 := false
 	if base.prefer32 != nil {
 		prefer32 = base.prefer32(mctx, base, os)
 	}
+	if os == Windows {
+		// Windows builds always prefer 32-bit
+		prefer32 = true
+	}
 
 	// Determine the multilib selection for this module.
 	multilib, extraMultilib := decodeMultilib(base, os.Class)
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 a7e0b67..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] {
@@ -1401,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/filegroup.go b/android/filegroup.go
index 9425616..fd4a2fe 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -17,10 +17,50 @@
 import (
 	"android/soong/bazel"
 	"strings"
+
+	"github.com/google/blueprint/proptools"
 )
 
 func init() {
 	RegisterModuleType("filegroup", FileGroupFactory)
+	RegisterBp2BuildMutator("filegroup", bp2buildMutator)
+}
+
+// https://docs.bazel.build/versions/master/be/general.html#filegroup
+type bazelFilegroupAttributes struct {
+	Name *string
+	Srcs []string
+}
+
+type bazelFilegroup struct {
+	BazelTargetModuleBase
+	bazelFilegroupAttributes
+}
+
+func BazelFileGroupFactory() Module {
+	module := &bazelFilegroup{}
+	module.AddProperties(&module.bazelFilegroupAttributes)
+	InitBazelTargetModule(module)
+	return module
+}
+
+func (bfg *bazelFilegroup) Name() string {
+	return bfg.BaseModuleName()
+}
+
+func (bfg *bazelFilegroup) GenerateAndroidBuildActions(ctx ModuleContext) {}
+
+// TODO: Create helper functions to avoid this boilerplate.
+func bp2buildMutator(ctx TopDownMutatorContext) {
+	if m, ok := ctx.Module().(*fileGroup); ok {
+		name := "__bp2build__" + m.base().BaseModuleName()
+		ctx.CreateModule(BazelFileGroupFactory, &bazelFilegroupAttributes{
+			Name: proptools.StringPtr(name),
+			Srcs: m.properties.Srcs,
+		}, &bazel.BazelTargetModuleProperties{
+			Rule_class: "filegroup",
+		})
+	}
 }
 
 type fileGroupProperties struct {
diff --git a/android/module.go b/android/module.go
index 17035bb..a20dc56 100644
--- a/android/module.go
+++ b/android/module.go
@@ -15,6 +15,7 @@
 package android
 
 import (
+	"android/soong/bazel"
 	"fmt"
 	"os"
 	"path"
@@ -491,6 +492,43 @@
 	TransitivePackagingSpecs() []PackagingSpec
 }
 
+// BazelTargetModule is a lightweight wrapper interface around Module for
+// bp2build conversion purposes.
+//
+// In bp2build's bootstrap.Main execution, Soong runs an alternate pipeline of
+// mutators that creates BazelTargetModules from regular Module objects,
+// performing the mapping from Soong properties to Bazel rule attributes in the
+// process. This process may optionally create additional BazelTargetModules,
+// resulting in a 1:many mapping.
+//
+// bp2build.Codegen is then responsible for visiting all modules in the graph,
+// filtering for BazelTargetModules, and code-generating BUILD targets from
+// them.
+type BazelTargetModule interface {
+	Module
+
+	BazelTargetModuleProperties() *bazel.BazelTargetModuleProperties
+}
+
+// InitBazelTargetModule is a wrapper function that decorates BazelTargetModule
+// with property structs containing metadata for bp2build conversion.
+func InitBazelTargetModule(module BazelTargetModule) {
+	module.AddProperties(module.BazelTargetModuleProperties())
+	InitAndroidModule(module)
+}
+
+// BazelTargetModuleBase contains the property structs with metadata for
+// bp2build conversion.
+type BazelTargetModuleBase struct {
+	ModuleBase
+	Properties bazel.BazelTargetModuleProperties
+}
+
+// BazelTargetModuleProperties getter.
+func (btmb *BazelTargetModuleBase) BazelTargetModuleProperties() *bazel.BazelTargetModuleProperties {
+	return &btmb.Properties
+}
+
 // Qualified id for a module
 type qualifiedModuleName struct {
 	// The package (i.e. directory) in which the module is defined, without trailing /
@@ -1069,6 +1107,9 @@
 	archProperties          [][]interface{}
 	customizableProperties  []interface{}
 
+	// Properties specific to the Blueprint to BUILD migration.
+	bazelTargetModuleProperties bazel.BazelTargetModuleProperties
+
 	// Information about all the properties on the module that contains visibility rules that need
 	// checking.
 	visibilityPropertyInfo []visibilityProperty
@@ -2434,10 +2475,6 @@
 	m.commonProperties.System_ext_specific = boolPtr(false)
 }
 
-func (m *ModuleBase) EnableNativeBridgeSupportByDefault() {
-	m.commonProperties.Native_bridge_supported = boolPtr(true)
-}
-
 func (m *ModuleBase) MakeAsSystemExt() {
 	m.commonProperties.Vendor = boolPtr(false)
 	m.commonProperties.Proprietary = boolPtr(false)
diff --git a/android/mutator.go b/android/mutator.go
index 72c68b2..2a2be6c 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -44,9 +44,16 @@
 	}
 }
 
-func registerMutatorsForBazelConversion(ctx *blueprint.Context) {
-	// FIXME(b/171263886): Start bringing in mutators to make the Bionic
-	// module subgraph suitable for automated conversion.
+// RegisterMutatorsForBazelConversion is a alternate registration pipeline for bp2build. Exported for testing.
+func RegisterMutatorsForBazelConversion(ctx *blueprint.Context, bp2buildMutators []RegisterMutatorFunc) {
+	mctx := &registerMutatorsContext{}
+
+	// Register bp2build mutators
+	for _, f := range bp2buildMutators {
+		f(mctx)
+	}
+
+	registerMutatorsToContext(ctx, mctx.mutators)
 }
 
 func registerMutators(ctx *blueprint.Context, preArch, preDeps, postDeps, finalDeps []RegisterMutatorFunc) {
@@ -196,6 +203,21 @@
 	finalDeps = append(finalDeps, f)
 }
 
+var bp2buildMutators = []RegisterMutatorFunc{}
+
+// RegisterBp2BuildMutator registers specially crafted mutators for
+// converting Blueprint/Android modules into special modules that can
+// be code-generated into Bazel BUILD targets.
+//
+// TODO(b/178068862): bring this into TestContext.
+func RegisterBp2BuildMutator(moduleType string, m func(TopDownMutatorContext)) {
+	mutatorName := moduleType + "_bp2build"
+	f := func(ctx RegisterMutatorsContext) {
+		ctx.TopDown(mutatorName, m)
+	}
+	bp2buildMutators = append(bp2buildMutators, f)
+}
+
 type BaseMutatorContext interface {
 	BaseModuleContext
 
diff --git a/android/neverallow.go b/android/neverallow.go
index 031b3f4..7455e6a 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -51,7 +51,6 @@
 func init() {
 	AddNeverAllowRules(createIncludeDirsRules()...)
 	AddNeverAllowRules(createTrebleRules()...)
-	AddNeverAllowRules(createMediaRules()...)
 	AddNeverAllowRules(createJavaDeviceForHostRules()...)
 	AddNeverAllowRules(createCcSdkVariantRules()...)
 	AddNeverAllowRules(createUncompressDexRules()...)
@@ -132,14 +131,6 @@
 	}
 }
 
-func createMediaRules() []Rule {
-	return []Rule{
-		NeverAllow().
-			With("libs", "updatable-media").
-			Because("updatable-media includes private APIs. Use updatable_media_stubs instead."),
-	}
-}
-
 func createJavaDeviceForHostRules() []Rule {
 	javaDeviceForHostProjectsAllowedList := []string{
 		"external/guava",
diff --git a/android/neverallow_test.go b/android/neverallow_test.go
index 8c7a538..b761065 100644
--- a/android/neverallow_test.go
+++ b/android/neverallow_test.go
@@ -190,19 +190,6 @@
 		},
 	},
 	{
-		name: "dependency on updatable-media",
-		fs: map[string][]byte{
-			"Android.bp": []byte(`
-				java_library {
-					name: "needs_updatable_media",
-					libs: ["updatable-media"],
-				}`),
-		},
-		expectedErrors: []string{
-			"updatable-media includes private APIs. Use updatable_media_stubs instead.",
-		},
-	},
-	{
 		name: "java_device_for_host",
 		fs: map[string][]byte{
 			"Android.bp": []byte(`
diff --git a/android/register.go b/android/register.go
index ca658f5..02fc97e 100644
--- a/android/register.go
+++ b/android/register.go
@@ -37,9 +37,6 @@
 var singletons []singleton
 var preSingletons []singleton
 
-var bazelConverterSingletons []singleton
-var bazelConverterPreSingletons []singleton
-
 type mutator struct {
 	name            string
 	bottomUpMutator blueprint.BottomUpMutator
@@ -94,14 +91,6 @@
 	preSingletons = append(preSingletons, singleton{name, factory})
 }
 
-func RegisterBazelConverterSingletonType(name string, factory SingletonFactory) {
-	bazelConverterSingletons = append(bazelConverterSingletons, singleton{name, factory})
-}
-
-func RegisterBazelConverterPreSingletonType(name string, factory SingletonFactory) {
-	bazelConverterPreSingletons = append(bazelConverterPreSingletons, singleton{name, factory})
-}
-
 type Context struct {
 	*blueprint.Context
 	config Config
@@ -117,21 +106,20 @@
 // singletons, module types and mutators to register for converting Blueprint
 // files to semantically equivalent BUILD files.
 func (ctx *Context) RegisterForBazelConversion() {
-	for _, t := range bazelConverterPreSingletons {
-		ctx.RegisterPreSingletonType(t.name, SingletonFactoryAdaptor(ctx, t.factory))
-	}
-
 	for _, t := range moduleTypes {
 		ctx.RegisterModuleType(t.name, ModuleFactoryAdaptor(t.factory))
 	}
 
-	for _, t := range bazelConverterSingletons {
+	// 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)
+	RegisterMutatorsForBazelConversion(ctx.Context, bp2buildMutators)
 }
 
+// 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))
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/soong_config_modules.go b/android/soong_config_modules.go
index 619cf86..289e910 100644
--- a/android/soong_config_modules.go
+++ b/android/soong_config_modules.go
@@ -51,6 +51,16 @@
 // variables from another Android.bp file.  The imported module type will exist for all
 // modules after the import in the Android.bp file.
 //
+// Each soong_config_variable supports an additional value `conditions_default`. The properties
+// specified in `conditions_default` will only be used under the following conditions:
+//   bool variable: the variable is unspecified or not set to a true value
+//   value variable: the variable is unspecified
+//   string variable: the variable is unspecified or the variable is set to a string unused in the
+//                    given module. For example, string variable `test` takes values: "a" and "b",
+//                    if the module contains a property `a` and `conditions_default`, when test=b,
+//                    the properties under `conditions_default` will be used. To specify that no
+//                    properties should be amended for `b`, you can set `b: {},`.
+//
 // For example, an Android.bp file could have:
 //
 //     soong_config_module_type_import {
@@ -69,12 +79,21 @@
 //                 soc_b: {
 //                     cflags: ["-DSOC_B"],
 //                 },
+//                 conditions_default: {
+//                     cflags: ["-DSOC_DEFAULT"],
+//                 },
 //             },
 //             feature: {
 //                 cflags: ["-DFEATURE"],
+//                 conditions_default: {
+//                     cflags: ["-DFEATURE_DEFAULT"],
+//                 },
 //             },
 //             width: {
 //                 cflags: ["-DWIDTH=%s"],
+//                 conditions_default: {
+//                     cflags: ["-DWIDTH=DEFAULT"],
+//                 },
 //             },
 //         },
 //     }
@@ -99,7 +118,7 @@
 //
 //     soong_config_string_variable {
 //         name: "board",
-//         values: ["soc_a", "soc_b"],
+//         values: ["soc_a", "soc_b", "soc_c"],
 //     }
 //
 // If an acme BoardConfig.mk file contained:
@@ -114,6 +133,31 @@
 //     SOONG_CONFIG_acme_width := 200
 //
 // Then libacme_foo would build with cflags "-DGENERIC -DSOC_A -DFEATURE -DWIDTH=200".
+//
+// Alternatively, if acme BoardConfig.mk file contained:
+//
+//     SOONG_CONFIG_NAMESPACES += acme
+//     SOONG_CONFIG_acme += \
+//         board \
+//         feature \
+//
+//     SOONG_CONFIG_acme_feature := false
+//
+// Then libacme_foo would build with cflags:
+//   "-DGENERIC -DSOC_DEFAULT -DFEATURE_DEFAULT -DSIZE=DEFAULT".
+//
+// Similarly, if acme BoardConfig.mk file contained:
+//
+//     SOONG_CONFIG_NAMESPACES += acme
+//     SOONG_CONFIG_acme += \
+//         board \
+//         feature \
+//
+//     SOONG_CONFIG_acme_board := soc_c
+//
+// Then libacme_foo would build with cflags:
+//   "-DGENERIC -DSOC_DEFAULT -DFEATURE_DEFAULT -DSIZE=DEFAULT".
+
 func soongConfigModuleTypeImportFactory() Module {
 	module := &soongConfigModuleTypeImport{}
 
@@ -148,6 +192,16 @@
 // in an Android.bp file, and can be imported into other Android.bp files using
 // soong_config_module_type_import.
 //
+// Each soong_config_variable supports an additional value `conditions_default`. The properties
+// specified in `conditions_default` will only be used under the following conditions:
+//   bool variable: the variable is unspecified or not set to a true value
+//   value variable: the variable is unspecified
+//   string variable: the variable is unspecified or the variable is set to a string unused in the
+//                    given module. For example, string variable `test` takes values: "a" and "b",
+//                    if the module contains a property `a` and `conditions_default`, when test=b,
+//                    the properties under `conditions_default` will be used. To specify that no
+//                    properties should be amended for `b`, you can set `b: {},`.
+//
 // For example, an Android.bp file could have:
 //
 //     soong_config_module_type {
@@ -176,12 +230,21 @@
 //                 soc_b: {
 //                     cflags: ["-DSOC_B"],
 //                 },
+//                 conditions_default: {
+//                     cflags: ["-DSOC_DEFAULT"],
+//                 },
 //             },
 //             feature: {
 //                 cflags: ["-DFEATURE"],
+//                 conditions_default: {
+//                     cflags: ["-DFEATURE_DEFAULT"],
+//                 },
 //             },
 //             width: {
 //	               cflags: ["-DWIDTH=%s"],
+//                 conditions_default: {
+//                     cflags: ["-DWIDTH=DEFAULT"],
+//                 },
 //             },
 //         },
 //     }
diff --git a/android/soong_config_modules_test.go b/android/soong_config_modules_test.go
index b1810b3..45463fd 100644
--- a/android/soong_config_modules_test.go
+++ b/android/soong_config_modules_test.go
@@ -60,15 +60,20 @@
 			name: "acme_test",
 			module_type: "test",
 			config_namespace: "acme",
-			variables: ["board", "feature1", "FEATURE3"],
-			bool_variables: ["feature2"],
-			value_variables: ["size"],
+			variables: ["board", "feature1", "FEATURE3", "unused_string_var"],
+			bool_variables: ["feature2", "unused_feature"],
+			value_variables: ["size", "unused_size"],
 			properties: ["cflags", "srcs", "defaults"],
 		}
 
 		soong_config_string_variable {
 			name: "board",
-			values: ["soc_a", "soc_b"],
+			values: ["soc_a", "soc_b", "soc_c", "soc_d"],
+		}
+
+		soong_config_string_variable {
+			name: "unused_string_var",
+			values: ["a", "b"],
 		}
 
 		soong_config_bool_variable {
@@ -105,15 +110,28 @@
 					soc_b: {
 						cflags: ["-DSOC_B"],
 					},
+					soc_c: {},
+					conditions_default: {
+						cflags: ["-DSOC_CONDITIONS_DEFAULT"],
+					},
 				},
 				size: {
 					cflags: ["-DSIZE=%s"],
+					conditions_default: {
+						cflags: ["-DSIZE=CONDITIONS_DEFAULT"],
+					},
 				},
 				feature1: {
+					  conditions_default: {
+						  cflags: ["-DF1_CONDITIONS_DEFAULT"],
+					  },
 					cflags: ["-DFEATURE1"],
 				},
 				feature2: {
 					cflags: ["-DFEATURE2"],
+					 conditions_default: {
+						 cflags: ["-DF2_CONDITIONS_DEFAULT"],
+					 },
 				},
 				FEATURE3: {
 					cflags: ["-DFEATURE3"],
@@ -145,6 +163,7 @@
 						cflags: ["-DSOC_B"],
 						defaults: ["foo_defaults_b"],
 					},
+					soc_c: {},
 				},
 				size: {
 					cflags: ["-DSIZE=%s"],
@@ -163,43 +182,120 @@
     `
 
 	run := func(t *testing.T, bp string, fs map[string][]byte) {
-		config := TestConfig(buildDir, nil, bp, fs)
+		testCases := []struct {
+			name                     string
+			config                   Config
+			fooExpectedFlags         []string
+			fooDefaultsExpectedFlags []string
+		}{
+			{
+				name: "withValues",
+				config: testConfigWithVendorVars(buildDir, bp, fs, map[string]map[string]string{
+					"acme": map[string]string{
+						"board":    "soc_a",
+						"size":     "42",
+						"feature1": "true",
+						"feature2": "false",
+						// FEATURE3 unset
+						"unused_feature":    "true", // unused
+						"unused_size":       "1",    // unused
+						"unused_string_var": "a",    // unused
+					},
+				}),
+				fooExpectedFlags: []string{
+					"DEFAULT",
+					"-DGENERIC",
+					"-DF2_CONDITIONS_DEFAULT",
+					"-DSIZE=42",
+					"-DSOC_A",
+					"-DFEATURE1",
+				},
+				fooDefaultsExpectedFlags: []string{
+					"DEFAULT_A",
+					"DEFAULT",
+					"-DGENERIC",
+					"-DSIZE=42",
+					"-DSOC_A",
+					"-DFEATURE1",
+				},
+			},
+			{
+				name: "empty_prop_for_string_var",
+				config: testConfigWithVendorVars(buildDir, bp, fs, map[string]map[string]string{
+					"acme": map[string]string{"board": "soc_c"}}),
+				fooExpectedFlags: []string{
+					"DEFAULT",
+					"-DGENERIC",
+					"-DF2_CONDITIONS_DEFAULT",
+					"-DSIZE=CONDITIONS_DEFAULT",
+					"-DF1_CONDITIONS_DEFAULT",
+				},
+				fooDefaultsExpectedFlags: []string{
+					"DEFAULT",
+					"-DGENERIC",
+				},
+			},
+			{
+				name: "unused_string_var",
+				config: testConfigWithVendorVars(buildDir, bp, fs, map[string]map[string]string{
+					"acme": map[string]string{"board": "soc_d"}}),
+				fooExpectedFlags: []string{
+					"DEFAULT",
+					"-DGENERIC",
+					"-DF2_CONDITIONS_DEFAULT",
+					"-DSIZE=CONDITIONS_DEFAULT",
+					"-DSOC_CONDITIONS_DEFAULT", // foo does not contain a prop "soc_d", so we use the default
+					"-DF1_CONDITIONS_DEFAULT",
+				},
+				fooDefaultsExpectedFlags: []string{
+					"DEFAULT",
+					"-DGENERIC",
+				},
+			},
 
-		config.TestProductVariables.VendorVars = map[string]map[string]string{
-			"acme": map[string]string{
-				"board":    "soc_a",
-				"size":     "42",
-				"feature1": "true",
-				"feature2": "false",
-				// FEATURE3 unset
+			{
+				name:   "conditions_default",
+				config: testConfigWithVendorVars(buildDir, bp, fs, map[string]map[string]string{}),
+				fooExpectedFlags: []string{
+					"DEFAULT",
+					"-DGENERIC",
+					"-DF2_CONDITIONS_DEFAULT",
+					"-DSIZE=CONDITIONS_DEFAULT",
+					"-DSOC_CONDITIONS_DEFAULT",
+					"-DF1_CONDITIONS_DEFAULT",
+				},
+				fooDefaultsExpectedFlags: []string{
+					"DEFAULT",
+					"-DGENERIC",
+				},
 			},
 		}
 
-		ctx := NewTestContext(config)
-		ctx.RegisterModuleType("soong_config_module_type_import", soongConfigModuleTypeImportFactory)
-		ctx.RegisterModuleType("soong_config_module_type", soongConfigModuleTypeFactory)
-		ctx.RegisterModuleType("soong_config_string_variable", soongConfigStringVariableDummyFactory)
-		ctx.RegisterModuleType("soong_config_bool_variable", soongConfigBoolVariableDummyFactory)
-		ctx.RegisterModuleType("test_defaults", soongConfigTestDefaultsModuleFactory)
-		ctx.RegisterModuleType("test", soongConfigTestModuleFactory)
-		ctx.PreArchMutators(RegisterDefaultsPreArchMutators)
-		ctx.Register()
+		for _, tc := range testCases {
+			ctx := NewTestContext(tc.config)
+			ctx.RegisterModuleType("soong_config_module_type_import", soongConfigModuleTypeImportFactory)
+			ctx.RegisterModuleType("soong_config_module_type", soongConfigModuleTypeFactory)
+			ctx.RegisterModuleType("soong_config_string_variable", soongConfigStringVariableDummyFactory)
+			ctx.RegisterModuleType("soong_config_bool_variable", soongConfigBoolVariableDummyFactory)
+			ctx.RegisterModuleType("test_defaults", soongConfigTestDefaultsModuleFactory)
+			ctx.RegisterModuleType("test", soongConfigTestModuleFactory)
+			ctx.PreArchMutators(RegisterDefaultsPreArchMutators)
+			ctx.Register()
 
-		_, errs := ctx.ParseBlueprintsFiles("Android.bp")
-		FailIfErrored(t, errs)
-		_, errs = ctx.PrepareBuildActions(config)
-		FailIfErrored(t, errs)
+			_, errs := ctx.ParseBlueprintsFiles("Android.bp")
+			FailIfErrored(t, errs)
+			_, errs = ctx.PrepareBuildActions(tc.config)
+			FailIfErrored(t, errs)
 
-		basicCFlags := []string{"DEFAULT", "-DGENERIC", "-DSIZE=42", "-DSOC_A", "-DFEATURE1"}
+			foo := ctx.ModuleForTests("foo", "").Module().(*soongConfigTestModule)
+			if g, w := foo.props.Cflags, tc.fooExpectedFlags; !reflect.DeepEqual(g, w) {
+				t.Errorf("%s: wanted foo cflags %q, got %q", tc.name, w, g)
+			}
 
-		foo := ctx.ModuleForTests("foo", "").Module().(*soongConfigTestModule)
-		if g, w := foo.props.Cflags, basicCFlags; !reflect.DeepEqual(g, w) {
-			t.Errorf("wanted foo cflags %q, got %q", w, g)
-		}
-
-		fooDefaults := ctx.ModuleForTests("foo_with_defaults", "").Module().(*soongConfigTestModule)
-		if g, w := fooDefaults.props.Cflags, append([]string{"DEFAULT_A"}, basicCFlags...); !reflect.DeepEqual(g, w) {
-			t.Errorf("wanted foo_with_defaults cflags %q, got %q", w, g)
+			fooDefaults := ctx.ModuleForTests("foo_with_defaults", "").Module().(*soongConfigTestModule)
+			if g, w := fooDefaults.props.Cflags, tc.fooDefaultsExpectedFlags; !reflect.DeepEqual(g, w) {
+				t.Errorf("%s: wanted foo_with_defaults cflags %q, got %q", tc.name, w, g)
+			}
 		}
 
 	}
@@ -214,3 +310,11 @@
 		})
 	})
 }
+
+func testConfigWithVendorVars(buildDir, bp string, fs map[string][]byte, vendorVars map[string]map[string]string) Config {
+	config := TestConfig(buildDir, nil, bp, fs)
+
+	config.TestProductVariables.VendorVars = vendorVars
+
+	return config
+}
diff --git a/android/soongconfig/Android.bp b/android/soongconfig/Android.bp
index df912e6..6bb68eb 100644
--- a/android/soongconfig/Android.bp
+++ b/android/soongconfig/Android.bp
@@ -10,4 +10,7 @@
         "config.go",
         "modules.go",
     ],
+    testSrcs: [
+        "modules_test.go",
+    ],
 }
diff --git a/android/soongconfig/config.go b/android/soongconfig/config.go
index 39a776c..c72da2f 100644
--- a/android/soongconfig/config.go
+++ b/android/soongconfig/config.go
@@ -14,7 +14,10 @@
 
 package soongconfig
 
-import "strings"
+import (
+	"fmt"
+	"strings"
+)
 
 type SoongConfig interface {
 	// Bool interprets the variable named `name` as a boolean, returning true if, after
@@ -31,7 +34,16 @@
 }
 
 func Config(vars map[string]string) SoongConfig {
-	return soongConfig(vars)
+	configVars := make(map[string]string)
+	if len(vars) > 0 {
+		for k, v := range vars {
+			configVars[k] = v
+		}
+		if _, exists := configVars[conditionsDefault]; exists {
+			panic(fmt.Sprintf("%q is a reserved soong config variable name", conditionsDefault))
+		}
+	}
+	return soongConfig(configVars)
 }
 
 type soongConfig map[string]string
diff --git a/android/soongconfig/modules.go b/android/soongconfig/modules.go
index 9f3f804..c62e76d 100644
--- a/android/soongconfig/modules.go
+++ b/android/soongconfig/modules.go
@@ -26,6 +26,8 @@
 	"github.com/google/blueprint/proptools"
 )
 
+const conditionsDefault = "conditions_default"
+
 var soongConfigProperty = proptools.FieldNameForProperty("soong_config_variables")
 
 // loadSoongConfigModuleTypeDefinition loads module types from an Android.bp file.  It caches the
@@ -145,32 +147,10 @@
 		return errs
 	}
 
-	mt := &ModuleType{
-		affectableProperties: props.Properties,
-		ConfigNamespace:      props.Config_namespace,
-		BaseModuleType:       props.Module_type,
-		variableNames:        props.Variables,
-	}
-	v.ModuleTypes[props.Name] = mt
-
-	for _, name := range props.Bool_variables {
-		if name == "" {
-			return []error{fmt.Errorf("bool_variable name must not be blank")}
-		}
-
-		mt.Variables = append(mt.Variables, newBoolVariable(name))
-	}
-
-	for _, name := range props.Value_variables {
-		if name == "" {
-			return []error{fmt.Errorf("value_variables entry must not be blank")}
-		}
-
-		mt.Variables = append(mt.Variables, &valueVariable{
-			baseVariable: baseVariable{
-				variable: name,
-			},
-		})
+	if mt, errs := newModuleType(props); len(errs) > 0 {
+		return errs
+	} else {
+		v.ModuleTypes[props.Name] = mt
 	}
 
 	return nil
@@ -196,6 +176,12 @@
 		return []error{fmt.Errorf("values property must be set")}
 	}
 
+	for _, name := range stringProps.Values {
+		if err := checkVariableName(name); err != nil {
+			return []error{fmt.Errorf("soong_config_string_variable: values property error %s", err)}
+		}
+	}
+
 	v.variables[base.variable] = &stringVariable{
 		baseVariable: base,
 		values:       CanonicalizeToProperties(stringProps.Values),
@@ -417,8 +403,7 @@
 // PropertiesToApply returns the applicable properties from a ModuleType that should be applied
 // based on SoongConfig values.
 // Expects that props contains a struct field with name soong_config_variables. The fields within
-// soong_config_variables are expected to be in the same order as moduleType.Variables. In general,
-// props should be generated via CreateProperties.
+// soong_config_variables are expected to be in the same order as moduleType.Variables.
 func PropertiesToApply(moduleType *ModuleType, props reflect.Value, config SoongConfig) ([]interface{}, error) {
 	var ret []interface{}
 	props = props.Elem().FieldByName(soongConfigProperty)
@@ -441,6 +426,46 @@
 	variableNames        []string
 }
 
+func newModuleType(props *ModuleTypeProperties) (*ModuleType, []error) {
+	mt := &ModuleType{
+		affectableProperties: props.Properties,
+		ConfigNamespace:      props.Config_namespace,
+		BaseModuleType:       props.Module_type,
+		variableNames:        props.Variables,
+	}
+
+	for _, name := range props.Bool_variables {
+		if err := checkVariableName(name); err != nil {
+			return nil, []error{fmt.Errorf("bool_variables %s", err)}
+		}
+
+		mt.Variables = append(mt.Variables, newBoolVariable(name))
+	}
+
+	for _, name := range props.Value_variables {
+		if err := checkVariableName(name); err != nil {
+			return nil, []error{fmt.Errorf("value_variables %s", err)}
+		}
+
+		mt.Variables = append(mt.Variables, &valueVariable{
+			baseVariable: baseVariable{
+				variable: name,
+			},
+		})
+	}
+
+	return mt, nil
+}
+
+func checkVariableName(name string) error {
+	if name == "" {
+		return fmt.Errorf("name must not be blank")
+	} else if name == conditionsDefault {
+		return fmt.Errorf("%q is reserved", conditionsDefault)
+	}
+	return nil
+}
+
 type soongConfigVariable interface {
 	// variableProperty returns the name of the variable.
 	variableProperty() string
@@ -474,7 +499,10 @@
 func (s *stringVariable) variableValuesType() reflect.Type {
 	var fields []reflect.StructField
 
-	for _, v := range s.values {
+	var values []string
+	values = append(values, s.values...)
+	values = append(values, conditionsDefault)
+	for _, v := range values {
 		fields = append(fields, reflect.StructField{
 			Name: proptools.FieldNameForProperty(v),
 			Type: emptyInterfaceType,
@@ -484,26 +512,36 @@
 	return reflect.StructOf(fields)
 }
 
+// initializeProperties initializes properties to zero value of typ for supported values and a final
+// conditions default field.
 func (s *stringVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
 	for i := range s.values {
 		v.Field(i).Set(reflect.Zero(typ))
 	}
+	v.Field(len(s.values)).Set(reflect.Zero(typ)) // conditions default is the final value
 }
 
+// Extracts an interface from values containing the properties to apply based on config.
+// If config does not match a value with a non-nil property set, the default value will be returned.
 func (s *stringVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
 	for j, v := range s.values {
-		if config.String(s.variable) == v {
-			return values.Field(j).Interface(), nil
+		f := values.Field(j)
+		if config.String(s.variable) == v && !f.Elem().IsNil() {
+			return f.Interface(), nil
 		}
 	}
-
-	return nil, nil
+	// if we have reached this point, we have checked all valid values of string and either:
+	//   * the value was not set
+	//   * the value was set but that value was not specified in the Android.bp file
+	return values.Field(len(s.values)).Interface(), nil
 }
 
+// Struct to allow conditions set based on a boolean variable
 type boolVariable struct {
 	baseVariable
 }
 
+// newBoolVariable constructs a boolVariable with the given name
 func newBoolVariable(name string) *boolVariable {
 	return &boolVariable{
 		baseVariable{
@@ -516,18 +554,82 @@
 	return emptyInterfaceType
 }
 
+// initializeProperties initializes a property to zero value of typ with an additional conditions
+// default field.
 func (b boolVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
-	v.Set(reflect.Zero(typ))
+	initializePropertiesWithDefault(v, typ)
 }
 
-func (b boolVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
-	if config.Bool(b.variable) {
-		return values.Interface(), nil
+// initializePropertiesWithDefault, initialize with zero value,  v to contain a field for each field
+// in typ, with an additional field for defaults of type typ. This should be used to initialize
+// boolVariable, valueVariable, or any future implementations of soongConfigVariable which support
+// one variable and a default.
+func initializePropertiesWithDefault(v reflect.Value, typ reflect.Type) {
+	sTyp := typ.Elem()
+	var fields []reflect.StructField
+	for i := 0; i < sTyp.NumField(); i++ {
+		fields = append(fields, sTyp.Field(i))
 	}
 
+	// create conditions_default field
+	nestedFieldName := proptools.FieldNameForProperty(conditionsDefault)
+	fields = append(fields, reflect.StructField{
+		Name: nestedFieldName,
+		Type: typ,
+	})
+
+	newTyp := reflect.PtrTo(reflect.StructOf(fields))
+	v.Set(reflect.Zero(newTyp))
+}
+
+// conditionsDefaultField extracts the conditions_default field from v. This is always the final
+// field if initialized with initializePropertiesWithDefault.
+func conditionsDefaultField(v reflect.Value) reflect.Value {
+	return v.Field(v.NumField() - 1)
+}
+
+// removeDefault removes the conditions_default field from values while retaining values from all
+// other fields. This allows
+func removeDefault(values reflect.Value) reflect.Value {
+	v := values.Elem().Elem()
+	s := conditionsDefaultField(v)
+	// if conditions_default field was not set, there will be no issues extending properties.
+	if !s.IsValid() {
+		return v
+	}
+
+	// If conditions_default field was set, it has the correct type for our property. Create a new
+	// reflect.Value of the conditions_default type and copy all fields (except for
+	// conditions_default) based on values to the result.
+	res := reflect.New(s.Type().Elem())
+	for i := 0; i < res.Type().Elem().NumField(); i++ {
+		val := v.Field(i)
+		res.Elem().Field(i).Set(val)
+	}
+
+	return res
+}
+
+// PropertiesToApply returns an interface{} value based on initializeProperties to be applied to
+// the module. If the value was not set, conditions_default interface will be returned; otherwise,
+// the interface in values, without conditions_default will be returned.
+func (b boolVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
+	// If this variable was not referenced in the module, there are no properties to apply.
+	if values.Elem().IsZero() {
+		return nil, nil
+	}
+	if config.Bool(b.variable) {
+		values = removeDefault(values)
+		return values.Interface(), nil
+	}
+	v := values.Elem().Elem()
+	if f := conditionsDefaultField(v); f.IsValid() {
+		return f.Interface(), nil
+	}
 	return nil, nil
 }
 
+// Struct to allow conditions set based on a value variable, supporting string substitution.
 type valueVariable struct {
 	baseVariable
 }
@@ -536,17 +638,28 @@
 	return emptyInterfaceType
 }
 
+// initializeProperties initializes a property to zero value of typ with an additional conditions
+// default field.
 func (s *valueVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
-	v.Set(reflect.Zero(typ))
+	initializePropertiesWithDefault(v, typ)
 }
 
+// PropertiesToApply returns an interface{} value based on initializeProperties to be applied to
+// the module. If the variable was not set, conditions_default interface will be returned;
+// otherwise, the interface in values, without conditions_default will be returned with all
+// appropriate string substitutions based on variable being set.
 func (s *valueVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
-	if !config.IsSet(s.variable) || !values.IsValid() {
+	// If this variable was not referenced in the module, there are no properties to apply.
+	if !values.IsValid() || values.Elem().IsZero() {
 		return nil, nil
 	}
+	if !config.IsSet(s.variable) {
+		return conditionsDefaultField(values.Elem().Elem()).Interface(), nil
+	}
 	configValue := config.String(s.variable)
 
-	propStruct := values.Elem().Elem()
+	values = removeDefault(values)
+	propStruct := values.Elem()
 	if !propStruct.IsValid() {
 		return nil, nil
 	}
diff --git a/android/soongconfig/modules_test.go b/android/soongconfig/modules_test.go
index fb0e189..b824c78 100644
--- a/android/soongconfig/modules_test.go
+++ b/android/soongconfig/modules_test.go
@@ -254,67 +254,75 @@
 	A *string
 	B bool
 }
-type soongConfigVariables struct {
-	Bool_var       properties
-	Other_bool_var properties
+
+type boolVarProps struct {
+	A                  *string
+	B                  bool
+	Conditions_default *properties
 }
 
-type soongConfigProps struct {
-	Soong_config_variables soongConfigVariables
+type soongConfigVars struct {
+	Bool_var interface{}
 }
 
 func Test_PropertiesToApply(t *testing.T) {
-
-	mt := &ModuleType{
-		BaseModuleType:  "foo",
-		ConfigNamespace: "bar",
-		Variables: []soongConfigVariable{
-			newBoolVariable("bool_var"),
-			newBoolVariable("other_bool_var"),
-		},
-		affectableProperties: []string{
-			"a",
-			"b",
-		},
+	mt, _ := newModuleType(&ModuleTypeProperties{
+		Module_type:      "foo",
+		Config_namespace: "bar",
+		Bool_variables:   []string{"bool_var"},
+		Properties:       []string{"a", "b"},
+	})
+	boolVarPositive := &properties{
+		A: proptools.StringPtr("A"),
+		B: true,
 	}
-	props := soongConfigProps{
-		Soong_config_variables: soongConfigVariables{
-			Bool_var: properties{
-				A: proptools.StringPtr("a"),
-				B: true,
-			},
-			Other_bool_var: properties{
-				A: proptools.StringPtr("other"),
-				B: false,
+	conditionsDefault := &properties{
+		A: proptools.StringPtr("default"),
+		B: false,
+	}
+	actualProps := &struct {
+		Soong_config_variables soongConfigVars
+	}{
+		Soong_config_variables: soongConfigVars{
+			Bool_var: &boolVarProps{
+				A:                  boolVarPositive.A,
+				B:                  boolVarPositive.B,
+				Conditions_default: conditionsDefault,
 			},
 		},
 	}
+	props := reflect.ValueOf(actualProps)
 
 	testCases := []struct {
+		name      string
 		config    SoongConfig
 		wantProps []interface{}
 	}{
 		{
-			config: Config(map[string]string{}),
+			name:      "no_vendor_config",
+			config:    Config(map[string]string{}),
+			wantProps: []interface{}{conditionsDefault},
 		},
 		{
+			name:      "vendor_config_false",
+			config:    Config(map[string]string{"bool_var": "n"}),
+			wantProps: []interface{}{conditionsDefault},
+		},
+		{
+			name:      "bool_var_true",
 			config:    Config(map[string]string{"bool_var": "y"}),
-			wantProps: []interface{}{props.Soong_config_variables.Bool_var},
-		},
-		{
-			config:    Config(map[string]string{"other_bool_var": "y"}),
-			wantProps: []interface{}{props.Soong_config_variables.Other_bool_var},
+			wantProps: []interface{}{boolVarPositive},
 		},
 	}
 
 	for _, tc := range testCases {
-		gotProps, err := PropertiesToApply(mt, reflect.ValueOf(&props), tc.config)
+		gotProps, err := PropertiesToApply(mt, props, tc.config)
 		if err != nil {
-			t.Errorf("Unexpected error in PropertiesToApply: %s", err)
+			t.Errorf("%s: Unexpected error in PropertiesToApply: %s", tc.name, err)
 		}
 
 		if !reflect.DeepEqual(gotProps, tc.wantProps) {
-			t.Errorf("Expected %s, got %s", tc.wantProps, gotProps)
+			t.Errorf("%s: Expected %s, got %s", tc.name, tc.wantProps, gotProps)
 		}
 	}
 }
diff --git a/android/testing.go b/android/testing.go
index a66b1e1..76172d1 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -57,6 +57,7 @@
 type TestContext struct {
 	*Context
 	preArch, preDeps, postDeps, finalDeps []RegisterMutatorFunc
+	bp2buildMutators                      []RegisterMutatorFunc
 	NameResolver                          *NameResolver
 }
 
@@ -81,12 +82,27 @@
 	ctx.finalDeps = append(ctx.finalDeps, f)
 }
 
+// RegisterBp2BuildMutator registers a BazelTargetModule mutator for converting a module
+// type to the equivalent Bazel target.
+func (ctx *TestContext) RegisterBp2BuildMutator(moduleType string, m func(TopDownMutatorContext)) {
+	mutatorName := moduleType + "_bp2build"
+	f := func(ctx RegisterMutatorsContext) {
+		ctx.TopDown(mutatorName, m)
+	}
+	bp2buildMutators = append(bp2buildMutators, f)
+}
+
 func (ctx *TestContext) Register() {
 	registerMutators(ctx.Context.Context, ctx.preArch, ctx.preDeps, ctx.postDeps, ctx.finalDeps)
 
 	ctx.RegisterSingletonType("env", EnvSingleton)
 }
 
+// RegisterForBazelConversion prepares a test context for bp2build conversion.
+func (ctx *TestContext) RegisterForBazelConversion() {
+	RegisterMutatorsForBazelConversion(ctx.Context.Context, bp2buildMutators)
+}
+
 func (ctx *TestContext) ParseFileList(rootDir string, filePaths []string) (deps []string, errs []error) {
 	// This function adapts the old style ParseFileList calls that are spread throughout the tests
 	// to the new style that takes a config.
@@ -469,7 +485,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 41cd337..9b3ed17 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -309,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"`
@@ -364,6 +367,8 @@
 	BoardKernelModuleInterfaceVersions []string `json:",omitempty"`
 
 	BoardMoveRecoveryResourcesToVendorBoot *bool `json:",omitempty"`
+
+	PrebuiltHiddenApiDir *string `json:",omitempty"`
 }
 
 func boolPtr(v bool) *bool {
@@ -435,13 +440,15 @@
 
 	variableValues := reflect.ValueOf(a.variableProperties).Elem().FieldByName("Product_variables")
 
+	productVariables := reflect.ValueOf(mctx.Config().productVariables)
+
 	for i := 0; i < variableValues.NumField(); i++ {
 		variableValue := variableValues.Field(i)
 		name := variableValues.Type().Field(i).Name
 		property := "product_variables." + proptools.PropertyNameForField(name)
 
 		// Check that the variable was set for the product
-		val := reflect.ValueOf(mctx.Config().productVariables).FieldByName(name)
+		val := productVariables.FieldByName(name)
 		if !val.IsValid() || val.Kind() != reflect.Ptr || val.IsNil() {
 			continue
 		}
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 c11a89c..5da11ce 100644
--- a/apex/allowed_deps.txt
+++ b/apex/allowed_deps.txt
@@ -18,6 +18,7 @@
 android.hardware.cas@1.0(minSdkVersion:29)
 android.hardware.common-ndk_platform(minSdkVersion:29)
 android.hardware.common-unstable-ndk_platform(minSdkVersion:29)
+android.hardware.common-V2-ndk_platform(minSdkVersion:29)
 android.hardware.graphics.allocator@2.0(minSdkVersion:29)
 android.hardware.graphics.allocator@3.0(minSdkVersion:29)
 android.hardware.graphics.allocator@4.0(minSdkVersion:29)
@@ -76,6 +77,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 +131,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)
@@ -169,7 +172,10 @@
 ExtServices-core(minSdkVersion:current)
 flatbuffer_headers(minSdkVersion:(no version))
 fmtlib(minSdkVersion:29)
+framework-permission(minSdkVersion:30)
 framework-permission(minSdkVersion:current)
+framework-permission-s(minSdkVersion:30)
+framework-permission-s-shared(minSdkVersion:30)
 framework-sdkextensions(minSdkVersion:30)
 framework-sdkextensions(minSdkVersion:current)
 framework-statsd(minSdkVersion:current)
@@ -189,6 +195,7 @@
 InProcessTethering(minSdkVersion:current)
 ipmemorystore-aidl-interfaces-java(minSdkVersion:29)
 ipmemorystore-aidl-interfaces-unstable-java(minSdkVersion:29)
+ipmemorystore-aidl-interfaces-V11-java(minSdkVersion:29)
 jni_headers(minSdkVersion:29)
 jsr305(minSdkVersion:14)
 kotlinx-coroutines-android(minSdkVersion:current)
@@ -454,6 +461,7 @@
 mediaswcodec(minSdkVersion:29)
 metrics-constants-protos(minSdkVersion:29)
 modules-utils-build(minSdkVersion:29)
+modules-utils-os(minSdkVersion:30)
 ndk_crtbegin_so.19(minSdkVersion:(no version))
 ndk_crtbegin_so.21(minSdkVersion:(no version))
 ndk_crtbegin_so.27(minSdkVersion:(no version))
@@ -513,6 +521,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))
@@ -566,9 +575,13 @@
 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-media-s(minSdkVersion:29)
+service-permission(minSdkVersion:30)
 service-permission(minSdkVersion:current)
+service-permission-shared(minSdkVersion:30)
 service-statsd(minSdkVersion:current)
 SettingsLibActionBarShadow(minSdkVersion:21)
 SettingsLibAppPreference(minSdkVersion:21)
diff --git a/apex/androidmk.go b/apex/androidmk.go
index 0b114f8..99cd75e 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -126,8 +126,18 @@
 
 		moduleName := a.fullModuleName(apexBundleName, &fi)
 
-		if !android.InList(moduleName, moduleNames) {
-			moduleNames = append(moduleNames, moduleName)
+		// This name will be added to LOCAL_REQUIRED_MODULES of the APEX. We need to be
+		// arch-specific otherwise we will end up installing both ABIs even when only
+		// either of the ABI is requested.
+		aName := moduleName
+		switch fi.multilib {
+		case "lib32":
+			aName = aName + ":32"
+		case "lib64":
+			aName = aName + ":64"
+		}
+		if !android.InList(aName, moduleNames) {
+			moduleNames = append(moduleNames, aName)
 		}
 
 		if linkToSystemLib {
@@ -440,12 +450,21 @@
 					fmt.Fprintf(w, dist)
 				}
 
-				if a.coverageOutputPath.String() != "" {
+				if a.apisUsedByModuleFile.String() != "" {
 					goal := "apps_only"
-					distFile := a.coverageOutputPath.String()
+					distFile := a.apisUsedByModuleFile.String()
 					fmt.Fprintf(w, "ifneq (,$(filter $(my_register_name),$(TARGET_BUILD_APPS)))\n"+
 						" $(call dist-for-goals,%s,%s:ndk_apis_usedby_apex/$(notdir %s))\n"+
-						"endif",
+						"endif\n",
+						goal, distFile, distFile)
+				}
+
+				if a.apisBackedByModuleFile.String() != "" {
+					goal := "apps_only"
+					distFile := a.apisBackedByModuleFile.String()
+					fmt.Fprintf(w, "ifneq (,$(filter $(my_register_name),$(TARGET_BUILD_APPS)))\n"+
+						" $(call dist-for-goals,%s,%s:ndk_apis_backedby_apex/$(notdir %s))\n"+
+						"endif\n",
 						goal, distFile, distFile)
 				}
 			}
diff --git a/apex/apex.go b/apex/apex.go
index 5cd18ed..384d6c7 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"
@@ -389,7 +390,8 @@
 	isCompressed bool
 
 	// Path of API coverage generate file
-	coverageOutputPath android.ModuleOutPath
+	apisUsedByModuleFile   android.ModuleOutPath
+	apisBackedByModuleFile android.ModuleOutPath
 }
 
 // apexFileClass represents a type of file that can be included in APEX.
@@ -440,6 +442,8 @@
 	transitiveDep bool
 	isJniLib      bool
 
+	multilib string
+
 	// TODO(jiyong): remove this
 	module android.Module
 }
@@ -459,6 +463,7 @@
 		ret.requiredModuleNames = module.RequiredModuleNames()
 		ret.targetRequiredModuleNames = module.TargetRequiredModuleNames()
 		ret.hostRequiredModuleNames = module.HostRequiredModuleNames()
+		ret.multilib = module.Target().Arch.ArchType.Multilib
 	}
 	return ret
 }
@@ -720,9 +725,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
@@ -1428,6 +1439,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)
@@ -1501,7 +1513,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.
@@ -1520,6 +1532,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)
@@ -1629,7 +1644,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)
@@ -1841,10 +1856,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 {
@@ -1853,6 +1868,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
@@ -2854,7 +2874,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..56487ce 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)
@@ -247,6 +248,7 @@
 
 	cc.RegisterRequiredBuildComponentsForTest(ctx)
 	rust.RegisterRequiredBuildComponentsForTest(ctx)
+	java.RegisterRequiredBuildComponentsForTest(ctx)
 
 	ctx.RegisterModuleType("cc_test", cc.TestFactory)
 	ctx.RegisterModuleType("vndk_prebuilt_shared", cc.VndkPrebuiltSharedFactory)
@@ -255,14 +257,6 @@
 	ctx.RegisterModuleType("platform_compat_config", java.PlatformCompatConfigFactory)
 	ctx.RegisterModuleType("sh_binary", sh.ShBinaryFactory)
 	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
-	java.RegisterJavaBuildComponents(ctx)
-	java.RegisterSystemModulesBuildComponents(ctx)
-	java.RegisterAppBuildComponents(ctx)
-	java.RegisterAppImportBuildComponents(ctx)
-	java.RegisterAppSetBuildComponents(ctx)
-	java.RegisterRuntimeResourceOverlayBuildComponents(ctx)
-	java.RegisterSdkLibraryBuildComponents(ctx)
-	java.RegisterPrebuiltApisBuildComponents(ctx)
 	ctx.RegisterSingletonType("apex_keys_text", apexKeysTextFactory)
 	ctx.RegisterModuleType("bpf", bpf.BpfFactory)
 
@@ -1751,10 +1745,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 +4213,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 +4508,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 {
@@ -5536,7 +5673,7 @@
 	ensureNotContains(t, androidMk, "LOCAL_MODULE := prebuilt_myotherlib.myapex\n")
 	ensureNotContains(t, androidMk, "LOCAL_MODULE := myotherlib.myapex\n")
 	// `myapex` should have `myotherlib` in its required line, not `prebuilt_myotherlib`
-	ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES += mylib.myapex myotherlib apex_manifest.pb.myapex apex_pubkey.myapex\n")
+	ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES += mylib.myapex:64 myotherlib:64 apex_manifest.pb.myapex apex_pubkey.myapex\n")
 }
 
 func TestApexWithJniLibs(t *testing.T) {
@@ -5783,12 +5920,11 @@
 	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)
 	bp += java.GatherRequiredDepsForTest()
-	bp += dexpreopt.BpToolModulesForTest()
 
 	fs := map[string][]byte{
 		"a.java":                             nil,
@@ -5808,22 +5944,18 @@
 	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)
 	cc.RegisterRequiredBuildComponentsForTest(ctx)
-	java.RegisterJavaBuildComponents(ctx)
-	java.RegisterSystemModulesBuildComponents(ctx)
-	java.RegisterAppBuildComponents(ctx)
-	java.RegisterDexpreoptBootJarsComponents(ctx)
+	java.RegisterRequiredBuildComponentsForTest(ctx)
 	ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators)
 	ctx.PreDepsMutators(RegisterPreDepsMutators)
 	ctx.PostDepsMutators(RegisterPostDepsMutators)
 
 	ctx.Register()
 
-	_ = dexpreopt.GlobalSoongConfigForTests(config)
-	dexpreopt.RegisterToolModulesForTest(ctx)
 	pathCtx := android.PathContextForTesting(config)
 	dexpreoptConfig := dexpreopt.GlobalConfigForTests(pathCtx)
 	transformDexpreoptConfig(dexpreoptConfig)
@@ -5837,10 +5969,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 +6072,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) {
@@ -5968,9 +6151,7 @@
 	ctx.RegisterModuleType("apex_key", ApexKeyFactory)
 	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
 	cc.RegisterRequiredBuildComponentsForTest(ctx)
-	java.RegisterJavaBuildComponents(ctx)
-	java.RegisterSystemModulesBuildComponents(ctx)
-	java.RegisterDexpreoptBootJarsComponents(ctx)
+	java.RegisterRequiredBuildComponentsForTest(ctx)
 	ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators)
 	ctx.PreDepsMutators(RegisterPreDepsMutators)
 	ctx.PostDepsMutators(RegisterPostDepsMutators)
diff --git a/apex/builder.go b/apex/builder.go
index bc1b566..67314d8 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -687,7 +687,7 @@
 		implicitInputs = append(implicitInputs, unsignedOutputFile)
 
 		// Run coverage analysis
-		apisUsedbyOutputFile := android.PathForModuleOut(ctx, a.Name()+".txt")
+		apisUsedbyOutputFile := android.PathForModuleOut(ctx, a.Name()+"_using.txt")
 		ctx.Build(pctx, android.BuildParams{
 			Rule:        generateAPIsUsedbyApexRule,
 			Implicits:   implicitInputs,
@@ -698,7 +698,19 @@
 				"readelf":   "${config.ClangBin}/llvm-readelf",
 			},
 		})
-		a.coverageOutputPath = apisUsedbyOutputFile
+		a.apisUsedByModuleFile = apisUsedbyOutputFile
+
+		apisBackedbyOutputFile := android.PathForModuleOut(ctx, a.Name()+"_backing.txt")
+		ndkLibraryList := android.PathForSource(ctx, "system/core/rootdir/etc/public.libraries.android.txt")
+		rule := android.NewRuleBuilder(pctx, ctx)
+		rule.Command().
+			Tool(android.PathForSource(ctx, "build/soong/scripts/gen_ndk_backedby_apex.sh")).
+			Text(imageDir.String()).
+			Implicits(implicitInputs).
+			Output(apisBackedbyOutputFile).
+			Input(ndkLibraryList)
+		rule.Build("ndk_backedby_list", "Generate API libraries backed by Apex")
+		a.apisBackedByModuleFile = apisBackedbyOutputFile
 
 		bundleConfig := a.buildBundleConfig(ctx)
 
@@ -786,6 +798,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 404be8c..eb4bdfe 100644
--- a/bazel/aquery.go
+++ b/bazel/aquery.go
@@ -46,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 int
-	// TODO(cparsons): Handle non-flat depsets.
-	DirectArtifactIds []int
+	Id                  int
+	DirectArtifactIds   []int
+	TransitiveDepSetIds []int
 }
 
 // action contains relevant portions of Bazel's aquery proto, Action.
@@ -83,11 +83,15 @@
 // 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)
+
+	if err != nil {
+		return nil, err
+	}
 
 	pathFragments := map[int]pathFragment{}
 	for _, pathFragment := range aqueryResult.PathFragments {
@@ -97,28 +101,42 @@
 	for _, artifact := range aqueryResult.Artifacts {
 		artifactPath, err := expandPathFragment(artifact.PathFragmentId, pathFragments)
 		if err != nil {
-			// TODO(cparsons): Better error handling.
-			panic(err.Error())
+			return nil, err
 		}
 		artifactIdToPath[artifact.Id] = artifactPath
 	}
-	depsetIdToArtifactIds := map[int][]int{}
+
+	depsetIdToDepset := map[int]depSetOfFiles{}
 	for _, depset := range aqueryResult.DepSetOfFiles {
-		depsetIdToArtifactIds[depset.Id] = depset.DirectArtifactIds
+		depsetIdToDepset[depset.Id] = depset
 	}
 
+	// depsetIdToArtifactIdsCache is a memoization of depset flattening, because flattening
+	// may be an expensive operation.
+	depsetIdToArtifactIdsCache := map[int][]int{}
+
 	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, err :=
+				artifactIdsFromDepsetId(depsetIdToDepset, depsetIdToArtifactIdsCache, inputDepSetId)
+			if err != nil {
+				return nil, err
+			}
+			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{
@@ -130,7 +148,29 @@
 		buildStatements = append(buildStatements, buildStatement)
 	}
 
-	return buildStatements
+	return buildStatements, nil
+}
+
+func artifactIdsFromDepsetId(depsetIdToDepset map[int]depSetOfFiles,
+	depsetIdToArtifactIdsCache map[int][]int, depsetId int) ([]int, error) {
+	if result, exists := depsetIdToArtifactIdsCache[depsetId]; exists {
+		return result, nil
+	}
+	if depset, exists := depsetIdToDepset[depsetId]; exists {
+		result := depset.DirectArtifactIds
+		for _, childId := range depset.TransitiveDepSetIds {
+			childArtifactIds, err :=
+				artifactIdsFromDepsetId(depsetIdToDepset, depsetIdToArtifactIdsCache, childId)
+			if err != nil {
+				return nil, err
+			}
+			result = append(result, childArtifactIds...)
+		}
+		depsetIdToArtifactIdsCache[depsetId] = result
+		return result, nil
+	} else {
+		return nil, fmt.Errorf("undefined input depsetId %d", depsetId)
+	}
 }
 
 func expandPathFragment(id int, pathFragmentsMap map[int]pathFragment) (string, error) {
@@ -140,7 +180,7 @@
 	for currId > 0 {
 		currFragment, ok := pathFragmentsMap[currId]
 		if !ok {
-			return "", fmt.Errorf("undefined path fragment id '%s'", currId)
+			return "", fmt.Errorf("undefined path fragment id %d", currId)
 		}
 		labels = append([]string{currFragment.Label}, labels...)
 		currId = currFragment.ParentId
diff --git a/bazel/aquery_test.go b/bazel/aquery_test.go
new file mode 100644
index 0000000..a48e083
--- /dev/null
+++ b/bazel/aquery_test.go
@@ -0,0 +1,674 @@
+// 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 TestTransitiveInputDepsets(t *testing.T) {
+	// The input aquery for this test comes from a proof-of-concept starlark rule which registers
+	// a single action with many inputs given via a deep depset.
+	const inputString = `
+{
+  "artifacts": [{
+    "id": 1,
+    "pathFragmentId": 1
+  }, {
+    "id": 2,
+    "pathFragmentId": 7
+  }, {
+    "id": 3,
+    "pathFragmentId": 8
+  }, {
+    "id": 4,
+    "pathFragmentId": 9
+  }, {
+    "id": 5,
+    "pathFragmentId": 10
+  }, {
+    "id": 6,
+    "pathFragmentId": 11
+  }, {
+    "id": 7,
+    "pathFragmentId": 12
+  }, {
+    "id": 8,
+    "pathFragmentId": 13
+  }, {
+    "id": 9,
+    "pathFragmentId": 14
+  }, {
+    "id": 10,
+    "pathFragmentId": 15
+  }, {
+    "id": 11,
+    "pathFragmentId": 16
+  }, {
+    "id": 12,
+    "pathFragmentId": 17
+  }, {
+    "id": 13,
+    "pathFragmentId": 18
+  }, {
+    "id": 14,
+    "pathFragmentId": 19
+  }, {
+    "id": 15,
+    "pathFragmentId": 20
+  }, {
+    "id": 16,
+    "pathFragmentId": 21
+  }, {
+    "id": 17,
+    "pathFragmentId": 22
+  }, {
+    "id": 18,
+    "pathFragmentId": 23
+  }, {
+    "id": 19,
+    "pathFragmentId": 24
+  }, {
+    "id": 20,
+    "pathFragmentId": 25
+  }, {
+    "id": 21,
+    "pathFragmentId": 26
+  }],
+  "actions": [{
+    "targetId": 1,
+    "actionKey": "3b826d17fadbbbcd8313e456b90ec47c078c438088891dd45b4adbcd8889dc50",
+    "mnemonic": "Action",
+    "configurationId": 1,
+    "arguments": ["/bin/bash", "-c", "touch bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out"],
+    "inputDepSetIds": [1],
+    "outputIds": [21],
+    "primaryOutputId": 21
+  }],
+  "depSetOfFiles": [{
+    "id": 3,
+    "directArtifactIds": [1, 2, 3, 4, 5]
+  }, {
+    "id": 4,
+    "directArtifactIds": [6, 7, 8, 9, 10]
+  }, {
+    "id": 2,
+    "transitiveDepSetIds": [3, 4],
+    "directArtifactIds": [11, 12, 13, 14, 15]
+  }, {
+    "id": 5,
+    "directArtifactIds": [16, 17, 18, 19]
+  }, {
+    "id": 1,
+    "transitiveDepSetIds": [2, 5],
+    "directArtifactIds": [20]
+  }],
+  "pathFragments": [{
+    "id": 6,
+    "label": "bazel-out"
+  }, {
+    "id": 5,
+    "label": "sourceroot",
+    "parentId": 6
+  }, {
+    "id": 4,
+    "label": "k8-fastbuild",
+    "parentId": 5
+  }, {
+    "id": 3,
+    "label": "bin",
+    "parentId": 4
+  }, {
+    "id": 2,
+    "label": "testpkg",
+    "parentId": 3
+  }, {
+    "id": 1,
+    "label": "test_1",
+    "parentId": 2
+  }, {
+    "id": 7,
+    "label": "test_2",
+    "parentId": 2
+  }, {
+    "id": 8,
+    "label": "test_3",
+    "parentId": 2
+  }, {
+    "id": 9,
+    "label": "test_4",
+    "parentId": 2
+  }, {
+    "id": 10,
+    "label": "test_5",
+    "parentId": 2
+  }, {
+    "id": 11,
+    "label": "test_6",
+    "parentId": 2
+  }, {
+    "id": 12,
+    "label": "test_7",
+    "parentId": 2
+  }, {
+    "id": 13,
+    "label": "test_8",
+    "parentId": 2
+  }, {
+    "id": 14,
+    "label": "test_9",
+    "parentId": 2
+  }, {
+    "id": 15,
+    "label": "test_10",
+    "parentId": 2
+  }, {
+    "id": 16,
+    "label": "test_11",
+    "parentId": 2
+  }, {
+    "id": 17,
+    "label": "test_12",
+    "parentId": 2
+  }, {
+    "id": 18,
+    "label": "test_13",
+    "parentId": 2
+  }, {
+    "id": 19,
+    "label": "test_14",
+    "parentId": 2
+  }, {
+    "id": 20,
+    "label": "test_15",
+    "parentId": 2
+  }, {
+    "id": 21,
+    "label": "test_16",
+    "parentId": 2
+  }, {
+    "id": 22,
+    "label": "test_17",
+    "parentId": 2
+  }, {
+    "id": 23,
+    "label": "test_18",
+    "parentId": 2
+  }, {
+    "id": 24,
+    "label": "test_19",
+    "parentId": 2
+  }, {
+    "id": 25,
+    "label": "test_root",
+    "parentId": 2
+  }, {
+    "id": 26,
+    "label": "test_out",
+    "parentId": 2
+  }]
+}`
+
+	actualbuildStatements, _ := AqueryBuildStatements([]byte(inputString))
+	// Inputs for the action are test_{i} from 1 to 20, and test_root. These inputs
+	// are given via a deep depset, but the depset is flattened when returned as a
+	// BuildStatement slice.
+	inputPaths := []string{"bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_root"}
+	for i := 1; i < 20; i++ {
+		inputPaths = append(inputPaths, fmt.Sprintf("bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_%d", i))
+	}
+	expectedBuildStatements := []BuildStatement{
+		BuildStatement{
+			Command:     "/bin/bash -c touch bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out",
+			OutputPaths: []string{"bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out"},
+			InputPaths:  inputPaths,
+			Mnemonic:    "Action",
+		},
+	}
+	assertBuildStatements(t, expectedBuildStatements, actualbuildStatements)
+}
+
+func assertError(t *testing.T, err error, expected string) {
+	if err == nil {
+		t.Errorf("expected error '%s', but got no error", expected)
+	} else if err.Error() != expected {
+		t.Errorf("expected error '%s', but got: %s", expected, err.Error())
+	}
+}
+
+// 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/bazel/properties.go b/bazel/properties.go
index 8bb1956..ac0047b 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -19,9 +19,16 @@
 	Label string
 }
 
-// Properties contains common module properties for migration purposes.
+// Properties contains common module properties for Bazel migration purposes.
 type Properties struct {
 	// In USE_BAZEL_ANALYSIS=1 mode, this represents the Bazel target replacing
 	// this Soong module.
 	Bazel_module bazelModuleProperties
 }
+
+// BazelTargetModuleProperties contain properties and metadata used for
+// Blueprint to BUILD file conversion.
+type BazelTargetModuleProperties struct {
+	// The Bazel rule class for this target.
+	Rule_class string
+}
diff --git a/bp2build/Android.bp b/bp2build/Android.bp
index 49587f4..2bbe4b5 100644
--- a/bp2build/Android.bp
+++ b/bp2build/Android.bp
@@ -10,6 +10,8 @@
     ],
     deps: [
         "soong-android",
+        "soong-bazel",
+        "soong-genrule",
     ],
     testSrcs: [
         "build_conversion_test.go",
diff --git a/bp2build/androidbp_to_build_templates.go b/bp2build/androidbp_to_build_templates.go
index 75c3ccb..5fed4fa 100644
--- a/bp2build/androidbp_to_build_templates.go
+++ b/bp2build/androidbp_to_build_templates.go
@@ -15,7 +15,7 @@
 package bp2build
 
 const (
-	// The default `load` preamble for every generated BUILD file.
+	// The default `load` preamble for every generated queryview BUILD file.
 	soongModuleLoad = `package(default_visibility = ["//visibility:public"])
 load("//build/bazel/queryview_rules:soong_module.bzl", "soong_module")
 
@@ -25,16 +25,20 @@
 	// for expanding more attributes.
 	soongModuleTarget = `soong_module(
     name = "%s",
-    module_name = "%s",
-    module_type = "%s",
-    module_variant = "%s",
-    module_deps = %s,
+    soong_module_name = "%s",
+    soong_module_type = "%s",
+    soong_module_variant = "%s",
+    soong_module_deps = %s,
+%s)`
+
+	bazelTarget = `%s(
+    name = "%s",
 %s)`
 
 	// A simple provider to mark and differentiate Soong module rule shims from
 	// regular Bazel rules. Every Soong module rule shim returns a
 	// SoongModuleInfo provider, and can only depend on rules returning
-	// SoongModuleInfo in the `module_deps` attribute.
+	// SoongModuleInfo in the `soong_module_deps` attribute.
 	providersBzl = `SoongModuleInfo = provider(
     fields = {
         "name": "Name of module",
@@ -53,19 +57,19 @@
 def _generic_soong_module_impl(ctx):
     return [
         SoongModuleInfo(
-            name = ctx.attr.module_name,
-            type = ctx.attr.module_type,
-            variant = ctx.attr.module_variant,
+            name = ctx.attr.soong_module_name,
+            type = ctx.attr.soong_module_type,
+            variant = ctx.attr.soong_module_variant,
         ),
     ]
 
 generic_soong_module = rule(
     implementation = _generic_soong_module_impl,
     attrs = {
-        "module_name": attr.string(mandatory = True),
-        "module_type": attr.string(mandatory = True),
-        "module_variant": attr.string(),
-        "module_deps": attr.label_list(providers = [SoongModuleInfo]),
+        "soong_module_name": attr.string(mandatory = True),
+        "soong_module_type": attr.string(mandatory = True),
+        "soong_module_variant": attr.string(),
+        "soong_module_deps": attr.label_list(providers = [SoongModuleInfo]),
     },
 )
 
@@ -85,20 +89,20 @@
     else:
         return False
 
-# soong_module is a macro that supports arbitrary kwargs, and uses module_type to
+# soong_module is a macro that supports arbitrary kwargs, and uses soong_module_type to
 # expand to the right underlying shim.
-def soong_module(name, module_type, **kwargs):
-    soong_module_rule = soong_module_rule_map.get(module_type)
+def soong_module(name, soong_module_type, **kwargs):
+    soong_module_rule = soong_module_rule_map.get(soong_module_type)
 
     if soong_module_rule == None:
         # This module type does not have an existing rule to map to, so use the
         # generic_soong_module rule instead.
         generic_soong_module(
             name = name,
-            module_type = module_type,
-            module_name = kwargs.pop("module_name", ""),
-            module_variant = kwargs.pop("module_variant", ""),
-            module_deps = kwargs.pop("module_deps", []),
+            soong_module_type = soong_module_type,
+            soong_module_name = kwargs.pop("soong_module_name", ""),
+            soong_module_variant = kwargs.pop("soong_module_variant", ""),
+            soong_module_deps = kwargs.pop("soong_module_deps", []),
         )
     else:
         supported_kwargs = dict()
diff --git a/bp2build/bp2build.go b/bp2build/bp2build.go
index 30f298a..a414f04 100644
--- a/bp2build/bp2build.go
+++ b/bp2build/bp2build.go
@@ -16,52 +16,34 @@
 
 import (
 	"android/soong/android"
+	"fmt"
 	"os"
 )
 
-// The Bazel bp2build singleton is responsible for writing .bzl files that are equivalent to
+// 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 init() {
-	android.RegisterBazelConverterPreSingletonType("androidbp_to_build", AndroidBpToBuildSingleton)
-}
-
-func AndroidBpToBuildSingleton() android.Singleton {
-	return &androidBpToBuildSingleton{
-		name: "bp2build",
-	}
-}
-
-type androidBpToBuildSingleton struct {
-	name      string
-	outputDir android.OutputPath
-}
-
-func (s *androidBpToBuildSingleton) GenerateBuildActions(ctx android.SingletonContext) {
-	s.outputDir = android.PathForOutput(ctx, s.name)
-	android.RemoveAllOutputDir(s.outputDir)
-
-	if !ctx.Config().IsEnvTrue("CONVERT_TO_BAZEL") {
-		return
-	}
+func Codegen(ctx CodegenContext) {
+	outputDir := android.PathForOutput(ctx, "bp2build")
+	android.RemoveAllOutputDir(outputDir)
 
 	ruleShims := CreateRuleShims(android.ModuleTypeFactories())
 
-	buildToTargets := GenerateSoongModuleTargets(ctx)
+	buildToTargets := GenerateSoongModuleTargets(ctx.Context(), ctx.mode)
 
-	filesToWrite := CreateBazelFiles(ruleShims, buildToTargets)
+	filesToWrite := CreateBazelFiles(ruleShims, buildToTargets, ctx.mode)
 	for _, f := range filesToWrite {
-		if err := s.writeFile(ctx, f); err != nil {
-			ctx.Errorf("Failed to write %q (dir %q) due to %q", f.Basename, f.Dir, err)
+		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 (s *androidBpToBuildSingleton) getOutputPath(ctx android.PathContext, dir string) android.OutputPath {
-	return s.outputDir.Join(ctx, dir)
+func writeFile(outputDir android.OutputPath, ctx android.PathContext, f BazelFile) error {
+	return writeReadOnlyFile(ctx, getOutputPath(outputDir, ctx, f.Dir), f.Basename, f.Contents)
 }
 
-func (s *androidBpToBuildSingleton) writeFile(ctx android.PathContext, f BazelFile) error {
-	return writeReadOnlyFile(ctx, s.getOutputPath(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
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index 0329685..8284ae3 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -39,8 +39,46 @@
 	ModuleSubDir(module blueprint.Module) string
 	ModuleType(module blueprint.Module) string
 
-	VisitAllModulesBlueprint(visit func(blueprint.Module))
-	VisitDirectDeps(module android.Module, visit func(android.Module))
+	VisitAllModules(visit func(blueprint.Module))
+	VisitDirectDeps(module blueprint.Module, visit func(blueprint.Module))
+}
+
+type CodegenContext struct {
+	config  android.Config
+	context android.Context
+	mode    CodegenMode
+}
+
+// CodegenMode is an enum to differentiate code-generation modes.
+type CodegenMode int
+
+const (
+	// Bp2Build: generate BUILD files with targets buildable by Bazel directly.
+	//
+	// This mode is used for the Soong->Bazel build definition conversion.
+	Bp2Build CodegenMode = iota
+
+	// QueryView: generate BUILD files with targets representing fully mutated
+	// Soong modules, representing the fully configured Soong module graph with
+	// variants and dependency endges.
+	//
+	// This mode is used for discovering and introspecting the existing Soong
+	// module graph.
+	QueryView
+)
+
+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, mode CodegenMode) CodegenContext {
+	return CodegenContext{
+		context: context,
+		config:  config,
+		mode:    mode,
+	}
 }
 
 // props is an unsorted map. This function ensures that
@@ -55,16 +93,54 @@
 	return attributes
 }
 
-func GenerateSoongModuleTargets(ctx bpToBuildContext) map[string][]BazelTarget {
+func GenerateSoongModuleTargets(ctx bpToBuildContext, codegenMode CodegenMode) map[string][]BazelTarget {
 	buildFileToTargets := make(map[string][]BazelTarget)
-	ctx.VisitAllModulesBlueprint(func(m blueprint.Module) {
+	ctx.VisitAllModules(func(m blueprint.Module) {
 		dir := ctx.ModuleDir(m)
-		t := generateSoongModuleTarget(ctx, m)
+		var t BazelTarget
+
+		switch codegenMode {
+		case Bp2Build:
+			if _, ok := m.(android.BazelTargetModule); !ok {
+				return
+			}
+			t = generateBazelTarget(ctx, m)
+		case QueryView:
+			t = generateSoongModuleTarget(ctx, m)
+		default:
+			panic(fmt.Errorf("Unknown code-generation mode: %s", codegenMode))
+		}
+
 		buildFileToTargets[ctx.ModuleDir(m)] = append(buildFileToTargets[dir], t)
 	})
 	return buildFileToTargets
 }
 
+func generateBazelTarget(ctx bpToBuildContext, m blueprint.Module) BazelTarget {
+	// extract the bazel attributes from the module.
+	props := getBuildProperties(ctx, m)
+
+	// extract the rule class name from the attributes. Since the string value
+	// will be string-quoted, remove the quotes here.
+	ruleClass := strings.Replace(props.Attrs["rule_class"], "\"", "", 2)
+	// Delete it from being generated in the BUILD file.
+	delete(props.Attrs, "rule_class")
+
+	// Return the Bazel target with rule class and attributes, ready to be
+	// code-generated.
+	attributes := propsToAttributes(props.Attrs)
+	targetName := targetNameForBp2Build(ctx, m)
+	return BazelTarget{
+		name: targetName,
+		content: fmt.Sprintf(
+			bazelTarget,
+			ruleClass,
+			targetName,
+			attributes,
+		),
+	}
+}
+
 // 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 {
@@ -75,7 +151,7 @@
 	// out the implications of that.
 	depLabels := map[string]bool{}
 	if aModule, ok := m.(android.Module); ok {
-		ctx.VisitDirectDeps(aModule, func(depModule android.Module) {
+		ctx.VisitDirectDeps(aModule, func(depModule blueprint.Module) {
 			depLabels[qualifiedTargetLabel(ctx, depModule)] = true
 		})
 	}
@@ -288,6 +364,10 @@
 	return strings.Repeat("    ", indent)
 }
 
+func targetNameForBp2Build(c bpToBuildContext, logicModule blueprint.Module) string {
+	return strings.Replace(c.ModuleName(logicModule), "__bp2build__", "", 1)
+}
+
 func targetNameWithVariant(c bpToBuildContext, logicModule blueprint.Module) string {
 	name := ""
 	if c.ModuleSubDir(logicModule) != "" {
diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go
index 8230ad8..be32530 100644
--- a/bp2build/build_conversion_test.go
+++ b/bp2build/build_conversion_test.go
@@ -16,6 +16,7 @@
 
 import (
 	"android/soong/android"
+	"android/soong/genrule"
 	"testing"
 )
 
@@ -31,10 +32,10 @@
 		`,
 			expectedBazelTarget: `soong_module(
     name = "foo",
-    module_name = "foo",
-    module_type = "custom",
-    module_variant = "",
-    module_deps = [
+    soong_module_name = "foo",
+    soong_module_type = "custom",
+    soong_module_variant = "",
+    soong_module_deps = [
     ],
 )`,
 		},
@@ -46,10 +47,10 @@
 		`,
 			expectedBazelTarget: `soong_module(
     name = "foo",
-    module_name = "foo",
-    module_type = "custom",
-    module_variant = "",
-    module_deps = [
+    soong_module_name = "foo",
+    soong_module_type = "custom",
+    soong_module_variant = "",
+    soong_module_deps = [
     ],
     ramdisk = True,
 )`,
@@ -62,10 +63,10 @@
 		`,
 			expectedBazelTarget: `soong_module(
     name = "foo",
-    module_name = "foo",
-    module_type = "custom",
-    module_variant = "",
-    module_deps = [
+    soong_module_name = "foo",
+    soong_module_type = "custom",
+    soong_module_variant = "",
+    soong_module_deps = [
     ],
     owner = "a_string_with\"quotes\"_and_\\backslashes\\\\",
 )`,
@@ -78,10 +79,10 @@
 		`,
 			expectedBazelTarget: `soong_module(
     name = "foo",
-    module_name = "foo",
-    module_type = "custom",
-    module_variant = "",
-    module_deps = [
+    soong_module_name = "foo",
+    soong_module_type = "custom",
+    soong_module_variant = "",
+    soong_module_deps = [
     ],
     required = [
         "bar",
@@ -96,10 +97,10 @@
 		`,
 			expectedBazelTarget: `soong_module(
     name = "foo",
-    module_name = "foo",
-    module_type = "custom",
-    module_variant = "",
-    module_deps = [
+    soong_module_name = "foo",
+    soong_module_type = "custom",
+    soong_module_variant = "",
+    soong_module_deps = [
     ],
     target_required = [
         "qux",
@@ -124,10 +125,10 @@
 		`,
 			expectedBazelTarget: `soong_module(
     name = "foo",
-    module_name = "foo",
-    module_type = "custom",
-    module_variant = "",
-    module_deps = [
+    soong_module_name = "foo",
+    soong_module_type = "custom",
+    soong_module_variant = "",
+    soong_module_deps = [
     ],
     dist = {
         "tag": ".foo",
@@ -162,10 +163,10 @@
 		`,
 			expectedBazelTarget: `soong_module(
     name = "foo",
-    module_name = "foo",
-    module_type = "custom",
-    module_variant = "",
-    module_deps = [
+    soong_module_name = "foo",
+    soong_module_type = "custom",
+    soong_module_variant = "",
+    soong_module_deps = [
     ],
     dists = [
         {
@@ -200,13 +201,9 @@
 		_, errs = ctx.PrepareBuildActions(config)
 		android.FailIfErrored(t, errs)
 
-		bp2BuildCtx := bp2buildBlueprintWrapContext{
-			bpCtx: ctx.Context.Context,
-		}
-
-		bazelTargets := GenerateSoongModuleTargets(&bp2BuildCtx)[dir]
-		if g, w := len(bazelTargets), 1; g != w {
-			t.Fatalf("Expected %d bazel target, got %d", w, g)
+		bazelTargets := GenerateSoongModuleTargets(ctx.Context.Context, QueryView)[dir]
+		if actualCount, expectedCount := len(bazelTargets), 1; actualCount != expectedCount {
+			t.Fatalf("Expected %d bazel target, got %d", expectedCount, actualCount)
 		}
 
 		actualBazelTarget := bazelTargets[0]
@@ -214,7 +211,171 @@
 			t.Errorf(
 				"Expected generated Bazel target to be '%s', got '%s'",
 				testCase.expectedBazelTarget,
-				actualBazelTarget,
+				actualBazelTarget.content,
+			)
+		}
+	}
+}
+
+func TestGenerateBazelTargetModules(t *testing.T) {
+	testCases := []struct {
+		bp                  string
+		expectedBazelTarget string
+	}{
+		{
+			bp: `custom {
+	name: "foo",
+    string_list_prop: ["a", "b"],
+    string_prop: "a",
+}`,
+			expectedBazelTarget: `custom(
+    name = "foo",
+    string_list_prop = [
+        "a",
+        "b",
+    ],
+    string_prop = "a",
+)`,
+		},
+	}
+
+	dir := "."
+	for _, testCase := range testCases {
+		config := android.TestConfig(buildDir, nil, testCase.bp, nil)
+		ctx := android.NewTestContext(config)
+		ctx.RegisterModuleType("custom", customModuleFactory)
+		ctx.RegisterBp2BuildMutator("custom", customBp2BuildMutator)
+		ctx.RegisterForBazelConversion()
+
+		_, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
+		android.FailIfErrored(t, errs)
+		_, errs = ctx.ResolveDependencies(config)
+		android.FailIfErrored(t, errs)
+
+		bazelTargets := GenerateSoongModuleTargets(ctx.Context.Context, Bp2Build)[dir]
+		if actualCount, expectedCount := len(bazelTargets), 1; actualCount != expectedCount {
+			t.Fatalf("Expected %d bazel target, got %d", expectedCount, actualCount)
+		}
+
+		actualBazelTarget := bazelTargets[0]
+		if actualBazelTarget.content != testCase.expectedBazelTarget {
+			t.Errorf(
+				"Expected generated Bazel target to be '%s', got '%s'",
+				testCase.expectedBazelTarget,
+				actualBazelTarget.content,
+			)
+		}
+	}
+}
+
+func TestModuleTypeBp2Build(t *testing.T) {
+	testCases := []struct {
+		moduleTypeUnderTest        string
+		moduleTypeUnderTestFactory android.ModuleFactory
+		bp                         string
+		expectedBazelTarget        string
+	}{
+		{
+			moduleTypeUnderTest:        "filegroup",
+			moduleTypeUnderTestFactory: android.FileGroupFactory,
+			bp: `filegroup {
+	name: "foo",
+	srcs: [],
+}`,
+			expectedBazelTarget: `filegroup(
+    name = "foo",
+    srcs = [
+    ],
+)`,
+		},
+		{
+			moduleTypeUnderTest:        "filegroup",
+			moduleTypeUnderTestFactory: android.FileGroupFactory,
+			bp: `filegroup {
+	name: "foo",
+	srcs: ["a", "b"],
+}`,
+			expectedBazelTarget: `filegroup(
+    name = "foo",
+    srcs = [
+        "a",
+        "b",
+    ],
+)`,
+		},
+		{
+			moduleTypeUnderTest:        "genrule",
+			moduleTypeUnderTestFactory: genrule.GenRuleFactory,
+			bp: `genrule {
+    name: "foo",
+    out: ["foo.out"],
+    srcs: ["foo.in"],
+    tool_files: [":foo.tool"],
+    cmd: "$(location :foo.tool) arg $(in) $(out)",
+}`,
+			expectedBazelTarget: `genrule(
+    name = "foo",
+    cmd = "$(location :foo.tool) arg $(SRCS) $(OUTS)",
+    outs = [
+        "foo.out",
+    ],
+    srcs = [
+        "foo.in",
+    ],
+    tools = [
+        ":foo.tool",
+    ],
+)`,
+		},
+		{
+			moduleTypeUnderTest:        "genrule",
+			moduleTypeUnderTestFactory: genrule.GenRuleFactory,
+			bp: `genrule {
+    name: "foo",
+    out: ["foo.out"],
+    srcs: ["foo.in"],
+    tools: [":foo.tool"],
+    cmd: "$(location :foo.tool) --out-dir=$(genDir) $(in)",
+}`,
+			expectedBazelTarget: `genrule(
+    name = "foo",
+    cmd = "$(location :foo.tool) --out-dir=$(GENDIR) $(SRCS)",
+    outs = [
+        "foo.out",
+    ],
+    srcs = [
+        "foo.in",
+    ],
+    tools = [
+        ":foo.tool",
+    ],
+)`,
+		},
+	}
+
+	dir := "."
+	for _, testCase := range testCases {
+		config := android.TestConfig(buildDir, nil, testCase.bp, nil)
+		ctx := android.NewTestContext(config)
+		ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
+		ctx.RegisterForBazelConversion()
+
+		_, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
+		android.FailIfErrored(t, errs)
+		_, errs = ctx.ResolveDependencies(config)
+		android.FailIfErrored(t, errs)
+
+		bazelTargets := GenerateSoongModuleTargets(ctx.Context.Context, Bp2Build)[dir]
+		if actualCount, expectedCount := len(bazelTargets), 1; actualCount != expectedCount {
+			t.Fatalf("Expected %d bazel target, got %d", expectedCount, actualCount)
+		}
+
+		actualBazelTarget := bazelTargets[0]
+		if actualBazelTarget.content != testCase.expectedBazelTarget {
+			t.Errorf(
+				"Expected generated Bazel target to be '%s', got '%s'",
+				testCase.expectedBazelTarget,
+				actualBazelTarget.content,
 			)
 		}
 	}
diff --git a/bp2build/bzl_conversion.go b/bp2build/bzl_conversion.go
index 04c4542..f2f6b01 100644
--- a/bp2build/bzl_conversion.go
+++ b/bp2build/bzl_conversion.go
@@ -109,9 +109,9 @@
 		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]),
+        "soong_module_name": attr.string(mandatory = True),
+        "soong_module_variant": attr.string(),
+        "soong_module_deps": attr.label_list(providers = [SoongModuleInfo]),
 `
 		attrs += getAttributes(factory)
 		attrs += "    },"
diff --git a/bp2build/bzl_conversion_test.go b/bp2build/bzl_conversion_test.go
index 8bea3f6..8cdee65 100644
--- a/bp2build/bzl_conversion_test.go
+++ b/bp2build/bzl_conversion_test.go
@@ -83,9 +83,9 @@
 custom = rule(
     implementation = _custom_impl,
     attrs = {
-        "module_name": attr.string(mandatory = True),
-        "module_variant": attr.string(),
-        "module_deps": attr.label_list(providers = [SoongModuleInfo]),
+        "soong_module_name": attr.string(mandatory = True),
+        "soong_module_variant": attr.string(),
+        "soong_module_deps": attr.label_list(providers = [SoongModuleInfo]),
         "bool_prop": attr.bool(),
         "bool_ptr_prop": attr.bool(),
         "int64_ptr_prop": attr.int(),
@@ -107,9 +107,9 @@
 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]),
+        "soong_module_name": attr.string(mandatory = True),
+        "soong_module_variant": attr.string(),
+        "soong_module_deps": attr.label_list(providers = [SoongModuleInfo]),
         "bool_prop": attr.bool(),
         "bool_ptr_prop": attr.bool(),
         "int64_ptr_prop": attr.int(),
@@ -131,9 +131,9 @@
 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]),
+        "soong_module_name": attr.string(mandatory = True),
+        "soong_module_variant": attr.string(),
+        "soong_module_deps": attr.label_list(providers = [SoongModuleInfo]),
         "bool_prop": attr.bool(),
         "bool_ptr_prop": attr.bool(),
         "int64_ptr_prop": attr.int(),
@@ -172,7 +172,7 @@
 			content: "irrelevant",
 		},
 	}
-	files := CreateBazelFiles(ruleShims, make(map[string][]BazelTarget))
+	files := CreateBazelFiles(ruleShims, make(map[string][]BazelTarget), QueryView)
 
 	var actualSoongModuleBzl BazelFile
 	for _, f := range files {
diff --git a/bp2build/conversion.go b/bp2build/conversion.go
index cdfb38b..2025ef7 100644
--- a/bp2build/conversion.go
+++ b/bp2build/conversion.go
@@ -17,7 +17,8 @@
 
 func CreateBazelFiles(
 	ruleShims map[string]RuleShim,
-	buildToTargets map[string][]BazelTarget) []BazelFile {
+	buildToTargets map[string][]BazelTarget,
+	mode CodegenMode) []BazelFile {
 	files := make([]BazelFile, 0, len(ruleShims)+len(buildToTargets)+numAdditionalFiles)
 
 	// Write top level files: WORKSPACE and BUILD. These files are empty.
@@ -26,22 +27,30 @@
 	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))
+	if mode == QueryView {
+		// These files are only used for queryview.
+		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, newFile(bazelRulesSubDir, "soong_module.bzl", generateSoongModuleBzl(ruleShims)))
 
-	files = append(files, createBuildFiles(buildToTargets)...)
+	files = append(files, createBuildFiles(buildToTargets, mode)...)
 
 	return files
 }
 
-func createBuildFiles(buildToTargets map[string][]BazelTarget) []BazelFile {
+func createBuildFiles(buildToTargets map[string][]BazelTarget, mode CodegenMode) []BazelFile {
 	files := make([]BazelFile, 0, len(buildToTargets))
 	for _, dir := range android.SortedStringKeys(buildToTargets) {
 		content := soongModuleLoad
+		if mode == Bp2Build {
+			// No need to load soong_module for bp2build BUILD files.
+			content = ""
+		}
 		targets := buildToTargets[dir]
 		sort.Slice(targets, func(i, j int) bool { return targets[i].name < targets[j].name })
 		for _, t := range targets {
diff --git a/bp2build/conversion_test.go b/bp2build/conversion_test.go
index a38fa6a..7f75b14 100644
--- a/bp2build/conversion_test.go
+++ b/bp2build/conversion_test.go
@@ -19,12 +19,44 @@
 	"testing"
 )
 
-func TestCreateBazelFiles_AddsTopLevelFiles(t *testing.T) {
-	files := CreateBazelFiles(map[string]RuleShim{}, map[string][]BazelTarget{})
-	expectedFilePaths := []struct {
-		dir      string
-		basename string
-	}{
+type filepath struct {
+	dir      string
+	basename string
+}
+
+func assertFilecountsAreEqual(t *testing.T, actual []BazelFile, expected []filepath) {
+	if a, e := len(actual), len(expected); a != e {
+		t.Errorf("Expected %d files, got %d", e, a)
+	}
+}
+
+func assertFileContent(t *testing.T, actual []BazelFile, expected []filepath) {
+	for i := range actual {
+		if g, w := actual[i], expected[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)
+		}
+	}
+}
+
+func sortFiles(files []BazelFile) {
+	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
+		}
+	})
+}
+
+func TestCreateBazelFiles_QueryView_AddsTopLevelFiles(t *testing.T) {
+	files := CreateBazelFiles(map[string]RuleShim{}, map[string][]BazelTarget{}, QueryView)
+	expectedFilePaths := []filepath{
 		{
 			dir:      "",
 			basename: "BUILD",
@@ -47,27 +79,29 @@
 		},
 	}
 
-	if g, w := len(files), len(expectedFilePaths); g != w {
-		t.Errorf("Expected %d files, got %d", w, g)
+	assertFilecountsAreEqual(t, files, expectedFilePaths)
+	sortFiles(files)
+	assertFileContent(t, files, expectedFilePaths)
+}
+
+func TestCreateBazelFiles_Bp2Build_AddsTopLevelFiles(t *testing.T) {
+	files := CreateBazelFiles(map[string]RuleShim{}, map[string][]BazelTarget{}, Bp2Build)
+	expectedFilePaths := []filepath{
+		{
+			dir:      "",
+			basename: "BUILD",
+		},
+		{
+			dir:      "",
+			basename: "WORKSPACE",
+		},
+		{
+			dir:      bazelRulesSubDir,
+			basename: "BUILD",
+		},
 	}
 
-	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)
-		}
-	}
+	assertFilecountsAreEqual(t, files, expectedFilePaths)
+	sortFiles(files)
+	assertFileContent(t, files, expectedFilePaths)
 }
diff --git a/bp2build/testing.go b/bp2build/testing.go
index 160412d..4c31d2d 100644
--- a/bp2build/testing.go
+++ b/bp2build/testing.go
@@ -2,8 +2,9 @@
 
 import (
 	"android/soong/android"
+	"android/soong/bazel"
 
-	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
 )
 
 type nestedProps struct {
@@ -103,34 +104,36 @@
 	return m
 }
 
-type bp2buildBlueprintWrapContext struct {
-	bpCtx *blueprint.Context
+type customBazelModuleAttributes struct {
+	Name             *string
+	String_prop      string
+	String_list_prop []string
 }
 
-func (ctx *bp2buildBlueprintWrapContext) ModuleName(module blueprint.Module) string {
-	return ctx.bpCtx.ModuleName(module)
+type customBazelModule struct {
+	android.BazelTargetModuleBase
+	customBazelModuleAttributes
 }
 
-func (ctx *bp2buildBlueprintWrapContext) ModuleDir(module blueprint.Module) string {
-	return ctx.bpCtx.ModuleDir(module)
+func customBazelModuleFactory() android.Module {
+	module := &customBazelModule{}
+	module.AddProperties(&module.customBazelModuleAttributes)
+	android.InitBazelTargetModule(module)
+	return module
 }
 
-func (ctx *bp2buildBlueprintWrapContext) ModuleSubDir(module blueprint.Module) string {
-	return ctx.bpCtx.ModuleSubDir(module)
-}
+func (m *customBazelModule) Name() string                                          { return m.BaseModuleName() }
+func (m *customBazelModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {}
 
-func (ctx *bp2buildBlueprintWrapContext) ModuleType(module blueprint.Module) string {
-	return ctx.bpCtx.ModuleType(module)
-}
-
-func (ctx *bp2buildBlueprintWrapContext) VisitAllModulesBlueprint(visit func(blueprint.Module)) {
-	ctx.bpCtx.VisitAllModules(visit)
-}
-
-func (ctx *bp2buildBlueprintWrapContext) VisitDirectDeps(module android.Module, visit func(android.Module)) {
-	ctx.bpCtx.VisitDirectDeps(module, func(m blueprint.Module) {
-		if aModule, ok := m.(android.Module); ok {
-			visit(aModule)
-		}
-	})
+func customBp2BuildMutator(ctx android.TopDownMutatorContext) {
+	if m, ok := ctx.Module().(*customModule); ok {
+		name := "__bp2build__" + m.Name()
+		ctx.CreateModule(customBazelModuleFactory, &customBazelModuleAttributes{
+			Name:             proptools.StringPtr(name),
+			String_prop:      m.props.String_prop,
+			String_list_prop: m.props.String_list_prop,
+		}, &bazel.BazelTargetModuleProperties{
+			Rule_class: "custom",
+		})
+	}
 }
diff --git a/cc/Android.bp b/cc/Android.bp
index 33f3db2..6ec7e0e 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -87,6 +87,7 @@
         "prebuilt_test.go",
         "proto_test.go",
         "test_data_test.go",
+        "vendor_snapshot_test.go",
     ],
     pluginFor: ["soong_build"],
 }
diff --git a/cc/cc.go b/cc/cc.go
index ca2bd4f..afa6bf9 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -256,11 +256,27 @@
 	// Deprecated. true is the default, false is invalid.
 	Clang *bool `android:"arch_variant"`
 
-	// Minimum sdk version supported when compiling against the ndk. Setting this property causes
-	// two variants to be built, one for the platform and one for apps.
+	// The API level that this module is built against. The APIs of this API level will be
+	// visible at build time, but use of any APIs newer than min_sdk_version will render the
+	// module unloadable on older devices.  In the future it will be possible to weakly-link new
+	// APIs, making the behavior match Java: such modules will load on older devices, but
+	// calling new APIs on devices that do not support them will result in a crash.
+	//
+	// This property has the same behavior as sdk_version does for Java modules. For those
+	// familiar with Android Gradle, the property behaves similarly to how compileSdkVersion
+	// does for Java code.
+	//
+	// In addition, setting this property causes two variants to be built, one for the platform
+	// and one for apps.
 	Sdk_version *string
 
-	// Minimum sdk version that the artifact should support when it runs as part of mainline modules(APEX).
+	// Minimum OS API level supported by this C or C++ module. This property becomes the value
+	// of the __ANDROID_API__ macro. When the C or C++ module is included in an APEX or an APK,
+	// this property is also used to ensure that the min_sdk_version of the containing module is
+	// not older (i.e. less) than this module's min_sdk_version. When not set, this property
+	// defaults to the value of sdk_version.  When this is set to "apex_inherit", this tracks
+	// min_sdk_version of the containing APEX. When the module
+	// is not built for an APEX, "apex_inherit" defaults to sdk_version.
 	Min_sdk_version *string
 
 	// If true, always create an sdk variant and don't create a platform variant.
@@ -374,6 +390,17 @@
 	// Nothing happens if BOARD_VNDK_VERSION isn't set in the BoardConfig.mk
 	Vendor_available *bool
 
+	// This is the same as the "vendor_available" except that the install path
+	// of the vendor variant is /odm or /vendor/odm.
+	// By replacing "vendor_available: true" with "odm_available: true", the
+	// module will install its vendor variant to the /odm partition or /vendor/odm.
+	// As the modules with "odm_available: true" still create the vendor variants,
+	// they can link to the other vendor modules as the vendor_available modules do.
+	// Also, the vendor modules can link to odm_available modules.
+	//
+	// It may not be used for VNDK modules.
+	Odm_available *bool
+
 	// whether this module should be allowed to be directly depended by other
 	// modules with `product_specific: true` or `product_available: true`.
 	// If set to true, an additional product variant will be built separately
@@ -408,9 +435,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
@@ -428,6 +468,8 @@
 	canUseSdk() bool
 	useSdk() bool
 	sdkVersion() string
+	minSdkVersion() string
+	isSdkVariant() bool
 	useVndk() bool
 	isNdk(config android.Config) bool
 	IsLlndk() bool
@@ -456,6 +498,7 @@
 	nativeCoverage() bool
 	directlyInAnyApex() bool
 	isPreventInstall() bool
+	isCfiAssemblySupportEnabled() bool
 }
 
 type ModuleContext interface {
@@ -1011,10 +1054,6 @@
 		c.AddProperties(feature.props()...)
 	}
 
-	c.Prefer32(func(ctx android.BaseModuleContext, base *android.ModuleBase, os android.OsType) bool {
-		// Windows builds always prefer 32-bit
-		return os == android.Windows
-	})
 	android.InitAndroidArchModule(c, c.hod, c.multilib)
 	android.InitApexModule(c)
 	android.InitSdkAwareModule(c)
@@ -1045,7 +1084,8 @@
 }
 
 func (c *Module) canUseSdk() bool {
-	return c.Os() == android.Android && !c.UseVndk() && !c.InRamdisk() && !c.InRecovery() && !c.InVendorRamdisk()
+	return c.Os() == android.Android && c.Target().NativeBridge == android.NativeBridgeDisabled &&
+		!c.UseVndk() && !c.InRamdisk() && !c.InRecovery() && !c.InVendorRamdisk()
 }
 
 func (c *Module) UseSdk() bool {
@@ -1070,7 +1110,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
@@ -1230,6 +1270,11 @@
 	return c.kytheFiles
 }
 
+func (c *Module) isCfiAssemblySupportEnabled() bool {
+	return c.sanitize != nil &&
+		Bool(c.sanitize.Properties.Sanitize.Config.Cfi_assembly_support)
+}
+
 type baseModuleContext struct {
 	android.BaseModuleContext
 	moduleContextImpl
@@ -1300,6 +1345,29 @@
 	return ""
 }
 
+func (ctx *moduleContextImpl) minSdkVersion() string {
+	ver := ctx.mod.MinSdkVersion()
+	if ver == "apex_inherit" && !ctx.isForPlatform() {
+		ver = ctx.apexSdkVersion().String()
+	}
+	if ver == "apex_inherit" || ver == "" {
+		ver = ctx.sdkVersion()
+	}
+	// Also make sure that minSdkVersion is not greater than sdkVersion, if they are both numbers
+	sdkVersionInt, err := strconv.Atoi(ctx.sdkVersion())
+	minSdkVersionInt, err2 := strconv.Atoi(ver)
+	if err == nil && err2 == nil {
+		if sdkVersionInt < minSdkVersionInt {
+			return strconv.Itoa(sdkVersionInt)
+		}
+	}
+	return ver
+}
+
+func (ctx *moduleContextImpl) isSdkVariant() bool {
+	return ctx.mod.IsSdkVariant()
+}
+
 func (ctx *moduleContextImpl) useVndk() bool {
 	return ctx.mod.UseVndk()
 }
@@ -1395,6 +1463,10 @@
 	return ctx.mod.Properties.PreventInstall
 }
 
+func (ctx *moduleContextImpl) isCfiAssemblySupportEnabled() bool {
+	return ctx.mod.isCfiAssemblySupportEnabled()
+}
+
 func newBaseModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *Module {
 	return &Module{
 		hod:      hod,
@@ -2323,10 +2395,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
@@ -2335,22 +2415,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)
 			}
 		}
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 3399e93..7288cc4 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) {
@@ -1118,874 +1110,6 @@
 	`)
 }
 
-func TestVendorSnapshotCapture(t *testing.T) {
-	bp := `
-	cc_library {
-		name: "libvndk",
-		vendor_available: true,
-		product_available: true,
-		vndk: {
-			enabled: true,
-		},
-		nocrt: true,
-	}
-
-	cc_library {
-		name: "libvendor",
-		vendor: true,
-		nocrt: true,
-	}
-
-	cc_library {
-		name: "libvendor_available",
-		vendor_available: true,
-		nocrt: true,
-	}
-
-	cc_library_headers {
-		name: "libvendor_headers",
-		vendor_available: true,
-		nocrt: true,
-	}
-
-	cc_binary {
-		name: "vendor_bin",
-		vendor: true,
-		nocrt: true,
-	}
-
-	cc_binary {
-		name: "vendor_available_bin",
-		vendor_available: true,
-		nocrt: true,
-	}
-
-	toolchain_library {
-		name: "libb",
-		vendor_available: true,
-		src: "libb.a",
-	}
-
-	cc_object {
-		name: "obj",
-		vendor_available: true,
-	}
-
-	cc_library {
-		name: "libllndk",
-		llndk_stubs: "libllndk.llndk",
-	}
-
-	llndk_library {
-		name: "libllndk.llndk",
-		symbol_file: "",
-	}
-`
-	config := TestConfig(buildDir, android.Android, nil, bp, nil)
-	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
-	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
-	ctx := testCcWithConfig(t, config)
-
-	// Check Vendor snapshot output.
-
-	snapshotDir := "vendor-snapshot"
-	snapshotVariantPath := filepath.Join(buildDir, snapshotDir, "arm64")
-	snapshotSingleton := ctx.SingletonForTests("vendor-snapshot")
-
-	var jsonFiles []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)
-
-		// For shared libraries, only non-VNDK vendor_available modules are captured
-		sharedVariant := fmt.Sprintf("android_vendor.VER_%s_%s_shared", archType, archVariant)
-		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
-		checkSnapshot(t, ctx, snapshotSingleton, "libvendor", "libvendor.so", sharedDir, sharedVariant)
-		checkSnapshot(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.so", sharedDir, sharedVariant)
-		jsonFiles = append(jsonFiles,
-			filepath.Join(sharedDir, "libvendor.so.json"),
-			filepath.Join(sharedDir, "libvendor_available.so.json"))
-
-		// LLNDK modules are not captured
-		checkSnapshotExclude(t, ctx, snapshotSingleton, "libllndk", "libllndk.so", sharedDir, sharedVariant)
-
-		// For static libraries, all vendor:true and vendor_available modules (including VNDK) are captured.
-		// Also cfi variants are captured, except for prebuilts like toolchain_library
-		staticVariant := fmt.Sprintf("android_vendor.VER_%s_%s_static", archType, archVariant)
-		staticCfiVariant := fmt.Sprintf("android_vendor.VER_%s_%s_static_cfi", archType, archVariant)
-		staticDir := filepath.Join(snapshotVariantPath, archDir, "static")
-		checkSnapshot(t, ctx, snapshotSingleton, "libb", "libb.a", staticDir, staticVariant)
-		checkSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.a", staticDir, staticVariant)
-		checkSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.cfi.a", staticDir, staticCfiVariant)
-		checkSnapshot(t, ctx, snapshotSingleton, "libvendor", "libvendor.a", staticDir, staticVariant)
-		checkSnapshot(t, ctx, snapshotSingleton, "libvendor", "libvendor.cfi.a", staticDir, staticCfiVariant)
-		checkSnapshot(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.a", staticDir, staticVariant)
-		checkSnapshot(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.cfi.a", staticDir, staticCfiVariant)
-		jsonFiles = append(jsonFiles,
-			filepath.Join(staticDir, "libb.a.json"),
-			filepath.Join(staticDir, "libvndk.a.json"),
-			filepath.Join(staticDir, "libvndk.cfi.a.json"),
-			filepath.Join(staticDir, "libvendor.a.json"),
-			filepath.Join(staticDir, "libvendor.cfi.a.json"),
-			filepath.Join(staticDir, "libvendor_available.a.json"),
-			filepath.Join(staticDir, "libvendor_available.cfi.a.json"))
-
-		// For binary executables, all vendor:true and vendor_available modules are captured.
-		if archType == "arm64" {
-			binaryVariant := fmt.Sprintf("android_vendor.VER_%s_%s", archType, archVariant)
-			binaryDir := filepath.Join(snapshotVariantPath, archDir, "binary")
-			checkSnapshot(t, ctx, snapshotSingleton, "vendor_bin", "vendor_bin", binaryDir, binaryVariant)
-			checkSnapshot(t, ctx, snapshotSingleton, "vendor_available_bin", "vendor_available_bin", binaryDir, binaryVariant)
-			jsonFiles = append(jsonFiles,
-				filepath.Join(binaryDir, "vendor_bin.json"),
-				filepath.Join(binaryDir, "vendor_available_bin.json"))
-		}
-
-		// For header libraries, all vendor:true and vendor_available modules are captured.
-		headerDir := filepath.Join(snapshotVariantPath, archDir, "header")
-		jsonFiles = append(jsonFiles, filepath.Join(headerDir, "libvendor_headers.json"))
-
-		// For object modules, all vendor:true and vendor_available modules are captured.
-		objectVariant := fmt.Sprintf("android_vendor.VER_%s_%s", archType, archVariant)
-		objectDir := filepath.Join(snapshotVariantPath, archDir, "object")
-		checkSnapshot(t, ctx, snapshotSingleton, "obj", "obj.o", objectDir, objectVariant)
-		jsonFiles = append(jsonFiles, filepath.Join(objectDir, "obj.o.json"))
-	}
-
-	for _, jsonFile := range jsonFiles {
-		// verify all json files exist
-		if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
-			t.Errorf("%q expected but not found", jsonFile)
-		}
-	}
-
-	// fake snapshot should have all outputs in the normal snapshot.
-	fakeSnapshotSingleton := ctx.SingletonForTests("vendor-fake-snapshot")
-	for _, output := range snapshotSingleton.AllOutputs() {
-		fakeOutput := strings.Replace(output, "/vendor-snapshot/", "/fake/vendor-snapshot/", 1)
-		if fakeSnapshotSingleton.MaybeOutput(fakeOutput).Rule == nil {
-			t.Errorf("%q expected but not found", fakeOutput)
-		}
-	}
-}
-
-func TestVendorSnapshotUse(t *testing.T) {
-	frameworkBp := `
-	cc_library {
-		name: "libvndk",
-		vendor_available: true,
-		product_available: true,
-		vndk: {
-			enabled: true,
-		},
-		nocrt: true,
-		compile_multilib: "64",
-	}
-
-	cc_library {
-		name: "libvendor",
-		vendor: true,
-		nocrt: true,
-		no_libcrt: true,
-		stl: "none",
-		system_shared_libs: [],
-		compile_multilib: "64",
-	}
-
-	cc_binary {
-		name: "bin",
-		vendor: true,
-		nocrt: true,
-		no_libcrt: true,
-		stl: "none",
-		system_shared_libs: [],
-		compile_multilib: "64",
-	}
-`
-
-	vndkBp := `
-	vndk_prebuilt_shared {
-		name: "libvndk",
-		version: "BOARD",
-		target_arch: "arm64",
-		vendor_available: true,
-		product_available: true,
-		vndk: {
-			enabled: true,
-		},
-		arch: {
-			arm64: {
-				srcs: ["libvndk.so"],
-				export_include_dirs: ["include/libvndk"],
-			},
-		},
-	}
-`
-
-	vendorProprietaryBp := `
-	cc_library {
-		name: "libvendor_without_snapshot",
-		vendor: true,
-		nocrt: true,
-		no_libcrt: true,
-		stl: "none",
-		system_shared_libs: [],
-		compile_multilib: "64",
-	}
-
-	cc_library_shared {
-		name: "libclient",
-		vendor: true,
-		nocrt: true,
-		no_libcrt: true,
-		stl: "none",
-		system_shared_libs: [],
-		shared_libs: ["libvndk"],
-		static_libs: ["libvendor", "libvendor_without_snapshot"],
-		compile_multilib: "64",
-		srcs: ["client.cpp"],
-	}
-
-	cc_binary {
-		name: "bin_without_snapshot",
-		vendor: true,
-		nocrt: true,
-		no_libcrt: true,
-		stl: "none",
-		system_shared_libs: [],
-		static_libs: ["libvndk"],
-		compile_multilib: "64",
-		srcs: ["bin.cpp"],
-	}
-
-	vendor_snapshot_static {
-		name: "libvndk",
-		version: "BOARD",
-		target_arch: "arm64",
-		vendor: true,
-		arch: {
-			arm64: {
-				src: "libvndk.a",
-				export_include_dirs: ["include/libvndk"],
-			},
-		},
-	}
-
-	vendor_snapshot_shared {
-		name: "libvendor",
-		version: "BOARD",
-		target_arch: "arm64",
-		vendor: true,
-		arch: {
-			arm64: {
-				src: "libvendor.so",
-				export_include_dirs: ["include/libvendor"],
-			},
-		},
-	}
-
-	vendor_snapshot_static {
-		name: "libvendor",
-		version: "BOARD",
-		target_arch: "arm64",
-		vendor: true,
-		arch: {
-			arm64: {
-				src: "libvendor.a",
-				export_include_dirs: ["include/libvendor"],
-			},
-		},
-	}
-
-	vendor_snapshot_binary {
-		name: "bin",
-		version: "BOARD",
-		target_arch: "arm64",
-		vendor: true,
-		arch: {
-			arm64: {
-				src: "bin",
-			},
-		},
-	}
-`
-	depsBp := GatherRequiredDepsForTest(android.Android)
-
-	mockFS := map[string][]byte{
-		"deps/Android.bp":              []byte(depsBp),
-		"framework/Android.bp":         []byte(frameworkBp),
-		"vendor/Android.bp":            []byte(vendorProprietaryBp),
-		"vendor/bin":                   nil,
-		"vendor/bin.cpp":               nil,
-		"vendor/client.cpp":            nil,
-		"vendor/include/libvndk/a.h":   nil,
-		"vendor/include/libvendor/b.h": nil,
-		"vendor/libvndk.a":             nil,
-		"vendor/libvendor.a":           nil,
-		"vendor/libvendor.so":          nil,
-		"vndk/Android.bp":              []byte(vndkBp),
-		"vndk/include/libvndk/a.h":     nil,
-		"vndk/libvndk.so":              nil,
-	}
-
-	config := TestConfig(buildDir, android.Android, nil, "", mockFS)
-	config.TestProductVariables.DeviceVndkVersion = StringPtr("BOARD")
-	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
-	ctx := CreateTestContext(config)
-	ctx.Register()
-
-	_, errs := ctx.ParseFileList(".", []string{"deps/Android.bp", "framework/Android.bp", "vendor/Android.bp", "vndk/Android.bp"})
-	android.FailIfErrored(t, errs)
-	_, errs = ctx.PrepareBuildActions(config)
-	android.FailIfErrored(t, errs)
-
-	sharedVariant := "android_vendor.BOARD_arm64_armv8-a_shared"
-	staticVariant := "android_vendor.BOARD_arm64_armv8-a_static"
-	binaryVariant := "android_vendor.BOARD_arm64_armv8-a"
-
-	// libclient uses libvndk.vndk.BOARD.arm64, libvendor.vendor_static.BOARD.arm64, libvendor_without_snapshot
-	libclientCcFlags := ctx.ModuleForTests("libclient", sharedVariant).Rule("cc").Args["cFlags"]
-	for _, includeFlags := range []string{
-		"-Ivndk/include/libvndk",     // libvndk
-		"-Ivendor/include/libvendor", // libvendor
-	} {
-		if !strings.Contains(libclientCcFlags, includeFlags) {
-			t.Errorf("flags for libclient must contain %#v, but was %#v.",
-				includeFlags, libclientCcFlags)
-		}
-	}
-
-	libclientLdFlags := ctx.ModuleForTests("libclient", sharedVariant).Rule("ld").Args["libFlags"]
-	for _, input := range [][]string{
-		[]string{sharedVariant, "libvndk.vndk.BOARD.arm64"},
-		[]string{staticVariant, "libvendor.vendor_static.BOARD.arm64"},
-		[]string{staticVariant, "libvendor_without_snapshot"},
-	} {
-		outputPaths := getOutputPaths(ctx, input[0] /* variant */, []string{input[1]} /* module name */)
-		if !strings.Contains(libclientLdFlags, outputPaths[0].String()) {
-			t.Errorf("libflags for libclient must contain %#v, but was %#v", outputPaths[0], libclientLdFlags)
-		}
-	}
-
-	// bin_without_snapshot uses libvndk.vendor_static.BOARD.arm64
-	binWithoutSnapshotCcFlags := ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Rule("cc").Args["cFlags"]
-	if !strings.Contains(binWithoutSnapshotCcFlags, "-Ivendor/include/libvndk") {
-		t.Errorf("flags for bin_without_snapshot must contain %#v, but was %#v.",
-			"-Ivendor/include/libvndk", binWithoutSnapshotCcFlags)
-	}
-
-	binWithoutSnapshotLdFlags := ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Rule("ld").Args["libFlags"]
-	libVndkStaticOutputPaths := getOutputPaths(ctx, staticVariant, []string{"libvndk.vendor_static.BOARD.arm64"})
-	if !strings.Contains(binWithoutSnapshotLdFlags, libVndkStaticOutputPaths[0].String()) {
-		t.Errorf("libflags for bin_without_snapshot must contain %#v, but was %#v",
-			libVndkStaticOutputPaths[0], binWithoutSnapshotLdFlags)
-	}
-
-	// libvendor.so is installed by libvendor.vendor_shared.BOARD.arm64
-	ctx.ModuleForTests("libvendor.vendor_shared.BOARD.arm64", sharedVariant).Output("libvendor.so")
-
-	// libvendor_without_snapshot.so is installed by libvendor_without_snapshot
-	ctx.ModuleForTests("libvendor_without_snapshot", sharedVariant).Output("libvendor_without_snapshot.so")
-
-	// bin is installed by bin.vendor_binary.BOARD.arm64
-	ctx.ModuleForTests("bin.vendor_binary.BOARD.arm64", binaryVariant).Output("bin")
-
-	// bin_without_snapshot is installed by bin_without_snapshot
-	ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Output("bin_without_snapshot")
-
-	// libvendor and bin don't have vendor.BOARD variant
-	libvendorVariants := ctx.ModuleVariantsForTests("libvendor")
-	if inList(sharedVariant, libvendorVariants) {
-		t.Errorf("libvendor must not have variant %#v, but it does", sharedVariant)
-	}
-
-	binVariants := ctx.ModuleVariantsForTests("bin")
-	if inList(binaryVariant, binVariants) {
-		t.Errorf("bin must not have variant %#v, but it does", sharedVariant)
-	}
-}
-
-func TestVendorSnapshotSanitizer(t *testing.T) {
-	bp := `
-	vendor_snapshot_static {
-		name: "libsnapshot",
-		vendor: true,
-		target_arch: "arm64",
-		version: "BOARD",
-		arch: {
-			arm64: {
-				src: "libsnapshot.a",
-				cfi: {
-					src: "libsnapshot.cfi.a",
-				}
-			},
-		},
-	}
-`
-	config := TestConfig(buildDir, android.Android, nil, bp, nil)
-	config.TestProductVariables.DeviceVndkVersion = StringPtr("BOARD")
-	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
-	ctx := testCcWithConfig(t, config)
-
-	// Check non-cfi and cfi variant.
-	staticVariant := "android_vendor.BOARD_arm64_armv8-a_static"
-	staticCfiVariant := "android_vendor.BOARD_arm64_armv8-a_static_cfi"
-
-	staticModule := ctx.ModuleForTests("libsnapshot.vendor_static.BOARD.arm64", staticVariant).Module().(*Module)
-	assertString(t, staticModule.outputFile.Path().Base(), "libsnapshot.a")
-
-	staticCfiModule := ctx.ModuleForTests("libsnapshot.vendor_static.BOARD.arm64", staticCfiVariant).Module().(*Module)
-	assertString(t, staticCfiModule.outputFile.Path().Base(), "libsnapshot.cfi.a")
-}
-
-func assertExcludeFromVendorSnapshotIs(t *testing.T, c *Module, expected bool) {
-	t.Helper()
-	if c.ExcludeFromVendorSnapshot() != expected {
-		t.Errorf("expected %q ExcludeFromVendorSnapshot to be %t", c.String(), expected)
-	}
-}
-
-func assertExcludeFromRecoverySnapshotIs(t *testing.T, c *Module, expected bool) {
-	t.Helper()
-	if c.ExcludeFromRecoverySnapshot() != expected {
-		t.Errorf("expected %q ExcludeFromRecoverySnapshot to be %t", c.String(), expected)
-	}
-}
-
-func TestVendorSnapshotExclude(t *testing.T) {
-
-	// This test verifies that the exclude_from_vendor_snapshot property
-	// makes its way from the Android.bp source file into the module data
-	// structure. It also verifies that modules are correctly included or
-	// excluded in the vendor snapshot based on their path (framework or
-	// vendor) and the exclude_from_vendor_snapshot property.
-
-	frameworkBp := `
-		cc_library_shared {
-			name: "libinclude",
-			srcs: ["src/include.cpp"],
-			vendor_available: true,
-		}
-		cc_library_shared {
-			name: "libexclude",
-			srcs: ["src/exclude.cpp"],
-			vendor: true,
-			exclude_from_vendor_snapshot: true,
-		}
-	`
-
-	vendorProprietaryBp := `
-		cc_library_shared {
-			name: "libvendor",
-			srcs: ["vendor.cpp"],
-			vendor: true,
-		}
-	`
-
-	depsBp := GatherRequiredDepsForTest(android.Android)
-
-	mockFS := map[string][]byte{
-		"deps/Android.bp":       []byte(depsBp),
-		"framework/Android.bp":  []byte(frameworkBp),
-		"framework/include.cpp": nil,
-		"framework/exclude.cpp": nil,
-		"device/Android.bp":     []byte(vendorProprietaryBp),
-		"device/vendor.cpp":     nil,
-	}
-
-	config := TestConfig(buildDir, android.Android, nil, "", mockFS)
-	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
-	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
-	ctx := CreateTestContext(config)
-	ctx.Register()
-
-	_, errs := ctx.ParseFileList(".", []string{"deps/Android.bp", "framework/Android.bp", "device/Android.bp"})
-	android.FailIfErrored(t, errs)
-	_, errs = ctx.PrepareBuildActions(config)
-	android.FailIfErrored(t, errs)
-
-	// Test an include and exclude framework module.
-	assertExcludeFromVendorSnapshotIs(t, ctx.ModuleForTests("libinclude", coreVariant).Module().(*Module), false)
-	assertExcludeFromVendorSnapshotIs(t, ctx.ModuleForTests("libinclude", vendorVariant).Module().(*Module), false)
-	assertExcludeFromVendorSnapshotIs(t, ctx.ModuleForTests("libexclude", vendorVariant).Module().(*Module), true)
-
-	// A vendor module is excluded, but by its path, not the
-	// exclude_from_vendor_snapshot property.
-	assertExcludeFromVendorSnapshotIs(t, ctx.ModuleForTests("libvendor", vendorVariant).Module().(*Module), false)
-
-	// Verify the content of the vendor snapshot.
-
-	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, "libinclude", "libinclude.so", sharedDir, sharedVariant)
-		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libinclude.so.json"))
-
-		// Excluded modules
-		checkSnapshotExclude(t, ctx, snapshotSingleton, "libexclude", "libexclude.so", sharedDir, sharedVariant)
-		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libexclude.so.json"))
-		checkSnapshotExclude(t, ctx, snapshotSingleton, "libvendor", "libvendor.so", sharedDir, sharedVariant)
-		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libvendor.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 TestVendorSnapshotExcludeInVendorProprietaryPathErrors(t *testing.T) {
-
-	// This test verifies that using the exclude_from_vendor_snapshot
-	// property on a module in a vendor proprietary path generates an
-	// error. These modules are already excluded, so we prohibit using the
-	// property in this way, which could add to confusion.
-
-	vendorProprietaryBp := `
-		cc_library_shared {
-			name: "libvendor",
-			srcs: ["vendor.cpp"],
-			vendor: true,
-			exclude_from_vendor_snapshot: true,
-		}
-	`
-
-	depsBp := GatherRequiredDepsForTest(android.Android)
-
-	mockFS := map[string][]byte{
-		"deps/Android.bp":   []byte(depsBp),
-		"device/Android.bp": []byte(vendorProprietaryBp),
-		"device/vendor.cpp": nil,
-	}
-
-	config := TestConfig(buildDir, android.Android, nil, "", mockFS)
-	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
-	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
-	ctx := CreateTestContext(config)
-	ctx.Register()
-
-	_, errs := ctx.ParseFileList(".", []string{"deps/Android.bp", "device/Android.bp"})
-	android.FailIfErrored(t, errs)
-
-	_, errs = ctx.PrepareBuildActions(config)
-	android.CheckErrorsAgainstExpectations(t, errs, []string{
-		`module "libvendor\{.+,image:vendor.+,arch:arm64_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
-		`module "libvendor\{.+,image:vendor.+,arch:arm_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
-		`module "libvendor\{.+,image:vendor.+,arch:arm64_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
-		`module "libvendor\{.+,image:vendor.+,arch:arm_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
-		`module "libvendor\{.+,image:vendor.+,arch:arm64_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
-		`module "libvendor\{.+,image:vendor.+,arch:arm_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
-	})
-}
-
-func TestVendorSnapshotExcludeWithVendorAvailable(t *testing.T) {
-
-	// This test verifies that using the exclude_from_vendor_snapshot
-	// property on a module that is vendor available generates an error. A
-	// vendor available module must be captured in the vendor snapshot and
-	// must not built from source when building the vendor image against
-	// the vendor snapshot.
-
-	frameworkBp := `
-		cc_library_shared {
-			name: "libinclude",
-			srcs: ["src/include.cpp"],
-			vendor_available: true,
-			exclude_from_vendor_snapshot: true,
-		}
-	`
-
-	depsBp := GatherRequiredDepsForTest(android.Android)
-
-	mockFS := map[string][]byte{
-		"deps/Android.bp":       []byte(depsBp),
-		"framework/Android.bp":  []byte(frameworkBp),
-		"framework/include.cpp": nil,
-	}
-
-	config := TestConfig(buildDir, android.Android, nil, "", mockFS)
-	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
-	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
-	ctx := CreateTestContext(config)
-	ctx.Register()
-
-	_, errs := ctx.ParseFileList(".", []string{"deps/Android.bp", "framework/Android.bp"})
-	android.FailIfErrored(t, errs)
-
-	_, errs = ctx.PrepareBuildActions(config)
-	android.CheckErrorsAgainstExpectations(t, errs, []string{
-		`module "libinclude\{.+,image:,arch:arm64_.+\}" may not use both "vendor_available: true" and "exclude_from_vendor_snapshot: true"`,
-		`module "libinclude\{.+,image:,arch:arm_.+\}" may not use both "vendor_available: true" and "exclude_from_vendor_snapshot: true"`,
-		`module "libinclude\{.+,image:vendor.+,arch:arm64_.+\}" may not use both "vendor_available: true" and "exclude_from_vendor_snapshot: true"`,
-		`module "libinclude\{.+,image:vendor.+,arch:arm_.+\}" may not use both "vendor_available: true" and "exclude_from_vendor_snapshot: true"`,
-		`module "libinclude\{.+,image:,arch:arm64_.+\}" may not use both "vendor_available: true" and "exclude_from_vendor_snapshot: true"`,
-		`module "libinclude\{.+,image:,arch:arm_.+\}" may not use both "vendor_available: true" and "exclude_from_vendor_snapshot: true"`,
-		`module "libinclude\{.+,image:vendor.+,arch:arm64_.+\}" may not use both "vendor_available: true" and "exclude_from_vendor_snapshot: true"`,
-		`module "libinclude\{.+,image:vendor.+,arch:arm_.+\}" may not use both "vendor_available: true" and "exclude_from_vendor_snapshot: true"`,
-	})
-}
-
-func TestRecoverySnapshotCapture(t *testing.T) {
-	bp := `
-	cc_library {
-		name: "libvndk",
-		vendor_available: true,
-		recovery_available: true,
-		product_available: true,
-		vndk: {
-			enabled: true,
-		},
-		nocrt: true,
-	}
-
-	cc_library {
-		name: "librecovery",
-		recovery: true,
-		nocrt: true,
-	}
-
-	cc_library {
-		name: "librecovery_available",
-		recovery_available: true,
-		nocrt: true,
-	}
-
-	cc_library_headers {
-		name: "librecovery_headers",
-		recovery_available: true,
-		nocrt: true,
-	}
-
-	cc_binary {
-		name: "recovery_bin",
-		recovery: true,
-		nocrt: true,
-	}
-
-	cc_binary {
-		name: "recovery_available_bin",
-		recovery_available: true,
-		nocrt: true,
-	}
-
-	toolchain_library {
-		name: "libb",
-		recovery_available: true,
-		src: "libb.a",
-	}
-
-	cc_object {
-		name: "obj",
-		recovery_available: true,
-	}
-`
-	config := TestConfig(buildDir, android.Android, nil, bp, nil)
-	config.TestProductVariables.RecoverySnapshotVersion = StringPtr("current")
-	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
-	ctx := testCcWithConfig(t, config)
-
-	// Check Recovery snapshot output.
-
-	snapshotDir := "recovery-snapshot"
-	snapshotVariantPath := filepath.Join(buildDir, snapshotDir, "arm64")
-	snapshotSingleton := ctx.SingletonForTests("recovery-snapshot")
-
-	var jsonFiles []string
-
-	for _, arch := range [][]string{
-		[]string{"arm64", "armv8-a"},
-	} {
-		archType := arch[0]
-		archVariant := arch[1]
-		archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
-
-		// For shared libraries, only recovery_available modules are captured.
-		sharedVariant := fmt.Sprintf("android_recovery_%s_%s_shared", archType, archVariant)
-		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
-		checkSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.so", sharedDir, sharedVariant)
-		checkSnapshot(t, ctx, snapshotSingleton, "librecovery", "librecovery.so", sharedDir, sharedVariant)
-		checkSnapshot(t, ctx, snapshotSingleton, "librecovery_available", "librecovery_available.so", sharedDir, sharedVariant)
-		jsonFiles = append(jsonFiles,
-			filepath.Join(sharedDir, "libvndk.so.json"),
-			filepath.Join(sharedDir, "librecovery.so.json"),
-			filepath.Join(sharedDir, "librecovery_available.so.json"))
-
-		// For static libraries, all recovery:true and recovery_available modules are captured.
-		staticVariant := fmt.Sprintf("android_recovery_%s_%s_static", archType, archVariant)
-		staticDir := filepath.Join(snapshotVariantPath, archDir, "static")
-		checkSnapshot(t, ctx, snapshotSingleton, "libb", "libb.a", staticDir, staticVariant)
-		checkSnapshot(t, ctx, snapshotSingleton, "librecovery", "librecovery.a", staticDir, staticVariant)
-		checkSnapshot(t, ctx, snapshotSingleton, "librecovery_available", "librecovery_available.a", staticDir, staticVariant)
-		jsonFiles = append(jsonFiles,
-			filepath.Join(staticDir, "libb.a.json"),
-			filepath.Join(staticDir, "librecovery.a.json"),
-			filepath.Join(staticDir, "librecovery_available.a.json"))
-
-		// For binary executables, all recovery:true and recovery_available modules are captured.
-		if archType == "arm64" {
-			binaryVariant := fmt.Sprintf("android_recovery_%s_%s", archType, archVariant)
-			binaryDir := filepath.Join(snapshotVariantPath, archDir, "binary")
-			checkSnapshot(t, ctx, snapshotSingleton, "recovery_bin", "recovery_bin", binaryDir, binaryVariant)
-			checkSnapshot(t, ctx, snapshotSingleton, "recovery_available_bin", "recovery_available_bin", binaryDir, binaryVariant)
-			jsonFiles = append(jsonFiles,
-				filepath.Join(binaryDir, "recovery_bin.json"),
-				filepath.Join(binaryDir, "recovery_available_bin.json"))
-		}
-
-		// For header libraries, all vendor:true and vendor_available modules are captured.
-		headerDir := filepath.Join(snapshotVariantPath, archDir, "header")
-		jsonFiles = append(jsonFiles, filepath.Join(headerDir, "librecovery_headers.json"))
-
-		// For object modules, all vendor:true and vendor_available modules are captured.
-		objectVariant := fmt.Sprintf("android_recovery_%s_%s", archType, archVariant)
-		objectDir := filepath.Join(snapshotVariantPath, archDir, "object")
-		checkSnapshot(t, ctx, snapshotSingleton, "obj", "obj.o", objectDir, objectVariant)
-		jsonFiles = append(jsonFiles, filepath.Join(objectDir, "obj.o.json"))
-	}
-
-	for _, jsonFile := range jsonFiles {
-		// verify all json files exist
-		if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
-			t.Errorf("%q expected but not found", jsonFile)
-		}
-	}
-}
-
-func TestRecoverySnapshotExclude(t *testing.T) {
-	// This test verifies that the exclude_from_recovery_snapshot property
-	// makes its way from the Android.bp source file into the module data
-	// structure. It also verifies that modules are correctly included or
-	// excluded in the recovery snapshot based on their path (framework or
-	// vendor) and the exclude_from_recovery_snapshot property.
-
-	frameworkBp := `
-		cc_library_shared {
-			name: "libinclude",
-			srcs: ["src/include.cpp"],
-                        recovery_available: true,
-		}
-		cc_library_shared {
-			name: "libexclude",
-			srcs: ["src/exclude.cpp"],
-			recovery: true,
-			exclude_from_recovery_snapshot: true,
-		}
-	`
-
-	vendorProprietaryBp := `
-		cc_library_shared {
-			name: "libvendor",
-			srcs: ["vendor.cpp"],
-			recovery: true,
-		}
-	`
-
-	depsBp := GatherRequiredDepsForTest(android.Android)
-
-	mockFS := map[string][]byte{
-		"deps/Android.bp":       []byte(depsBp),
-		"framework/Android.bp":  []byte(frameworkBp),
-		"framework/include.cpp": nil,
-		"framework/exclude.cpp": nil,
-		"device/Android.bp":     []byte(vendorProprietaryBp),
-		"device/vendor.cpp":     nil,
-	}
-
-	config := TestConfig(buildDir, android.Android, nil, "", mockFS)
-	config.TestProductVariables.RecoverySnapshotVersion = StringPtr("current")
-	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
-	ctx := CreateTestContext(config)
-	ctx.Register()
-
-	_, errs := ctx.ParseFileList(".", []string{"deps/Android.bp", "framework/Android.bp", "device/Android.bp"})
-	android.FailIfErrored(t, errs)
-	_, errs = ctx.PrepareBuildActions(config)
-	android.FailIfErrored(t, errs)
-
-	// Test an include and exclude framework module.
-	assertExcludeFromRecoverySnapshotIs(t, ctx.ModuleForTests("libinclude", coreVariant).Module().(*Module), false)
-	assertExcludeFromRecoverySnapshotIs(t, ctx.ModuleForTests("libinclude", recoveryVariant).Module().(*Module), false)
-	assertExcludeFromRecoverySnapshotIs(t, ctx.ModuleForTests("libexclude", recoveryVariant).Module().(*Module), true)
-
-	// A vendor module is excluded, but by its path, not the
-	// exclude_from_recovery_snapshot property.
-	assertExcludeFromRecoverySnapshotIs(t, ctx.ModuleForTests("libvendor", recoveryVariant).Module().(*Module), false)
-
-	// Verify the content of the recovery snapshot.
-
-	snapshotDir := "recovery-snapshot"
-	snapshotVariantPath := filepath.Join(buildDir, snapshotDir, "arm64")
-	snapshotSingleton := ctx.SingletonForTests("recovery-snapshot")
-
-	var includeJsonFiles []string
-	var excludeJsonFiles []string
-
-	for _, arch := range [][]string{
-		[]string{"arm64", "armv8-a"},
-	} {
-		archType := arch[0]
-		archVariant := arch[1]
-		archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
-
-		sharedVariant := fmt.Sprintf("android_recovery_%s_%s_shared", archType, archVariant)
-		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
-
-		// Included modules
-		checkSnapshot(t, ctx, snapshotSingleton, "libinclude", "libinclude.so", sharedDir, sharedVariant)
-		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libinclude.so.json"))
-
-		// Excluded modules
-		checkSnapshotExclude(t, ctx, snapshotSingleton, "libexclude", "libexclude.so", sharedDir, sharedVariant)
-		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libexclude.so.json"))
-		checkSnapshotExclude(t, ctx, snapshotSingleton, "libvendor", "libvendor.so", sharedDir, sharedVariant)
-		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libvendor.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 TestDoubleLoadableDepError(t *testing.T) {
 	// Check whether an error is emitted when a LLNDK depends on a non-double_loadable VNDK lib.
 	testCcError(t, "module \".*\" variant \".*\": link.* \".*\" which is not LL-NDK, VNDK-SP, .*double_loadable", `
@@ -2030,64 +1154,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 {
@@ -2112,6 +1178,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) {
@@ -3171,7 +2260,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")
@@ -3179,14 +2286,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"
 
@@ -4479,137 +3586,309 @@
 	}
 }
 
-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
-		}
-	}
+type MemtagNoteType int
 
-	t.Errorf("%q is not found in implicit deps of module %q", name, m.Module().(*Module).Name())
-}
+const (
+	None MemtagNoteType = iota + 1
+	Sync
+	Async
+)
 
-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 (t MemtagNoteType) str() string {
+	switch t {
+	case None:
+		return "none"
+	case Sync:
+		return "sync"
+	case Async:
+		return "async"
+	default:
+		panic("invalid note type")
 	}
 }
 
-func TestSanitizeMemtagHeap(t *testing.T) {
-	rootBp := `
-		cc_library_static {
-			name: "libstatic",
-			sanitize: { memtag_heap: true },
-		}
+func checkHasMemtagNote(t *testing.T, m android.TestingModule, expected MemtagNoteType) {
+	note_async := "note_memtag_heap_async"
+	note_sync := "note_memtag_heap_sync"
 
-		cc_library_shared {
-			name: "libshared",
-			sanitize: { memtag_heap: true },
+	found := None
+	implicits := m.Rule("ld").Implicits
+	for _, lib := range implicits {
+		if strings.Contains(lib.Rel(), note_async) {
+			found = Async
+			break
+		} else if strings.Contains(lib.Rel(), note_sync) {
+			found = Sync
+			break
 		}
+	}
 
-		cc_library {
-			name: "libboth",
-			sanitize: { memtag_heap: true },
-		}
+	if found != expected {
+		t.Errorf("Wrong Memtag note in target %q: found %q, expected %q", m.Module().(*Module).Name(), found.str(), expected.str())
+	}
+}
 
-		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 },
-		}
-
+func makeMemtagTestConfig(t *testing.T) android.Config {
+	templateBp := `
 		cc_test {
-			name: "test",
+			name: "%[1]s_test",
 			gtest: false,
 		}
 
 		cc_test {
-			name: "test_true",
-			gtest: false,
-			sanitize: { memtag_heap: true },
-		}
-
-		cc_test {
-			name: "test_false",
+			name: "%[1]s_test_false",
 			gtest: false,
 			sanitize: { memtag_heap: false },
 		}
 
 		cc_test {
-			name: "test_true_async",
+			name: "%[1]s_test_true",
+			gtest: false,
+			sanitize: { memtag_heap: true },
+		}
+
+		cc_test {
+			name: "%[1]s_test_true_nodiag",
 			gtest: false,
 			sanitize: { memtag_heap: true, diag: { memtag_heap: false }  },
 		}
 
-		`
+		cc_test {
+			name: "%[1]s_test_true_diag",
+			gtest: false,
+			sanitize: { memtag_heap: true, diag: { memtag_heap: true }  },
+		}
 
-	subdirAsyncBp := `
 		cc_binary {
-			name: "binary_async",
+			name: "%[1]s_binary",
+		}
+
+		cc_binary {
+			name: "%[1]s_binary_false",
+			sanitize: { memtag_heap: false },
+		}
+
+		cc_binary {
+			name: "%[1]s_binary_true",
+			sanitize: { memtag_heap: true },
+		}
+
+		cc_binary {
+			name: "%[1]s_binary_true_nodiag",
+			sanitize: { memtag_heap: true, diag: { memtag_heap: false }  },
+		}
+
+		cc_binary {
+			name: "%[1]s_binary_true_diag",
+			sanitize: { memtag_heap: true, diag: { memtag_heap: true }  },
 		}
 		`
-
-	subdirSyncBp := `
-		cc_binary {
-			name: "binary_sync",
-		}
-		`
+	subdirDefaultBp := fmt.Sprintf(templateBp, "default")
+	subdirExcludeBp := fmt.Sprintf(templateBp, "exclude")
+	subdirSyncBp := fmt.Sprintf(templateBp, "sync")
+	subdirAsyncBp := fmt.Sprintf(templateBp, "async")
 
 	mockFS := map[string][]byte{
-		"subdir_async/Android.bp": []byte(subdirAsyncBp),
-		"subdir_sync/Android.bp":  []byte(subdirSyncBp),
+		"subdir_default/Android.bp": []byte(subdirDefaultBp),
+		"subdir_exclude/Android.bp": []byte(subdirExcludeBp),
+		"subdir_sync/Android.bp":    []byte(subdirSyncBp),
+		"subdir_async/Android.bp":   []byte(subdirAsyncBp),
 	}
 
-	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"}
+	return TestConfig(buildDir, android.Android, nil, "", mockFS)
+}
+
+func TestSanitizeMemtagHeap(t *testing.T) {
+	variant := "android_arm64_armv8-a"
+
+	config := makeMemtagTestConfig(t)
+	config.TestProductVariables.MemtagHeapExcludePaths = []string{"subdir_exclude"}
 	config.TestProductVariables.MemtagHeapSyncIncludePaths = []string{"subdir_sync"}
+	config.TestProductVariables.MemtagHeapAsyncIncludePaths = []string{"subdir_async"}
 	ctx := CreateTestContext(config)
 	ctx.Register()
 
-	_, errs := ctx.ParseFileList(".", []string{"Android.bp", "subdir_sync/Android.bp", "subdir_async/Android.bp"})
+	_, errs := ctx.ParseFileList(".", []string{"Android.bp", "subdir_default/Android.bp", "subdir_exclude/Android.bp", "subdir_sync/Android.bp", "subdir_async/Android.bp"})
 	android.FailIfErrored(t, errs)
 	_, errs = ctx.PrepareBuildActions(config)
 	android.FailIfErrored(t, errs)
 
+	checkHasMemtagNote(t, ctx.ModuleForTests("default_test", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("default_test_false", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_nodiag", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_diag", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("default_binary", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_false", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_nodiag", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_diag", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_false", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_nodiag", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_diag", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_false", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_nodiag", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_diag", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("async_test", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("async_test_false", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_nodiag", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_diag", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("async_binary", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_false", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_nodiag", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_diag", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("sync_test", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_false", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_nodiag", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_diag", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_false", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_nodiag", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_diag", variant), Sync)
+}
+
+func TestSanitizeMemtagHeapWithSanitizeDevice(t *testing.T) {
 	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)
+	config := makeMemtagTestConfig(t)
+	config.TestProductVariables.MemtagHeapExcludePaths = []string{"subdir_exclude"}
+	config.TestProductVariables.MemtagHeapSyncIncludePaths = []string{"subdir_sync"}
+	config.TestProductVariables.MemtagHeapAsyncIncludePaths = []string{"subdir_async"}
+	config.TestProductVariables.SanitizeDevice = []string{"memtag_heap"}
+	ctx := CreateTestContext(config)
+	ctx.Register()
 
-	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)
+	_, errs := ctx.ParseFileList(".", []string{"Android.bp", "subdir_default/Android.bp", "subdir_exclude/Android.bp", "subdir_sync/Android.bp", "subdir_async/Android.bp"})
+	android.FailIfErrored(t, errs)
+	_, errs = ctx.PrepareBuildActions(config)
+	android.FailIfErrored(t, errs)
 
-	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)
+	checkHasMemtagNote(t, ctx.ModuleForTests("default_test", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("default_test_false", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_nodiag", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_diag", variant), Sync)
 
-	checkHasImplicitDep(t, ctx.ModuleForTests("binary_async", variant), note_async)
-	checkHasImplicitDep(t, ctx.ModuleForTests("binary_sync", variant), note_sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("default_binary", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_false", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_nodiag", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_diag", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_false", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_nodiag", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_diag", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_false", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_nodiag", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_diag", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("async_test", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("async_test_false", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_nodiag", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_diag", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("async_binary", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_false", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_nodiag", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_diag", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("sync_test", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_false", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_nodiag", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_diag", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_false", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_nodiag", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_diag", variant), Sync)
+}
+
+func TestSanitizeMemtagHeapWithSanitizeDeviceDiag(t *testing.T) {
+	variant := "android_arm64_armv8-a"
+
+	config := makeMemtagTestConfig(t)
+	config.TestProductVariables.MemtagHeapExcludePaths = []string{"subdir_exclude"}
+	config.TestProductVariables.MemtagHeapSyncIncludePaths = []string{"subdir_sync"}
+	config.TestProductVariables.MemtagHeapAsyncIncludePaths = []string{"subdir_async"}
+	config.TestProductVariables.SanitizeDevice = []string{"memtag_heap"}
+	config.TestProductVariables.SanitizeDeviceDiag = []string{"memtag_heap"}
+	ctx := CreateTestContext(config)
+	ctx.Register()
+
+	_, errs := ctx.ParseFileList(".", []string{"Android.bp", "subdir_default/Android.bp", "subdir_exclude/Android.bp", "subdir_sync/Android.bp", "subdir_async/Android.bp"})
+	android.FailIfErrored(t, errs)
+	_, errs = ctx.PrepareBuildActions(config)
+	android.FailIfErrored(t, errs)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("default_test", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("default_test_false", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_nodiag", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_diag", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("default_binary", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_false", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_nodiag", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_diag", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_false", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_nodiag", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_diag", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_false", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_nodiag", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_diag", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("async_test", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("async_test_false", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_nodiag", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_diag", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("async_binary", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_false", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_nodiag", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_diag", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("sync_test", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_false", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_nodiag", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_diag", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_false", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_nodiag", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_diag", variant), Sync)
 }
diff --git a/cc/compiler.go b/cc/compiler.go
index eb2b566..5f30d3d 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -404,7 +404,12 @@
 
 	target := "-target " + tc.ClangTriple()
 	if ctx.Os().Class == android.Device {
-		version := ctx.sdkVersion()
+		// When built for the non-updateble part of platform, minSdkVersion doesn't matter.
+		// It matters only when building we are building for modules that can be unbundled.
+		version := "current"
+		if !ctx.isForPlatform() || ctx.isSdkVariant() {
+			version = ctx.minSdkVersion()
+		}
 		if version == "" || version == "current" {
 			target += strconv.Itoa(android.FutureApiLevelInt)
 		} else {
diff --git a/cc/config/clang.go b/cc/config/clang.go
index 3282958..35dd10f 100644
--- a/cc/config/clang.go
+++ b/cc/config/clang.go
@@ -141,6 +141,13 @@
 		// Warnings from clang-10
 		// Nested and array designated initialization is nice to have.
 		"-Wno-c99-designator",
+
+		// Calls to the APIs that are newer than the min sdk version of the caller should be
+		// guarded with __builtin_available.
+		"-Wunguarded-availability",
+		// This macro allows the bionic versioning.h to indirectly determine whether the
+		// option -Wunguarded-availability is on or not.
+		"-D__ANDROID_UNGUARDED_AVAILABILITY__",
 	}, " "))
 
 	pctx.StaticVariable("ClangExtraCppflags", strings.Join([]string{
diff --git a/cc/config/global.go b/cc/config/global.go
index d47e0ce..ee41125 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -53,6 +53,7 @@
 		"-Werror=date-time",
 		"-Werror=pragma-pack",
 		"-Werror=pragma-pack-suspicious-include",
+		"-Werror=string-plus-int",
 		"-Werror=unreachable-code-loop-increment",
 	}
 
@@ -90,9 +91,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),
@@ -113,8 +117,13 @@
 	}
 
 	noOverrideGlobalCflags = []string{
+		"-Werror=bool-operation",
+		"-Werror=implicit-int-float-conversion",
+		"-Werror=int-in-bool-context",
 		"-Werror=int-to-pointer-cast",
 		"-Werror=pointer-to-int-cast",
+		"-Werror=string-compare",
+		"-Werror=xor-used-as-pow",
 		// http://b/161386391 for -Wno-void-pointer-to-enum-cast
 		"-Wno-void-pointer-to-enum-cast",
 		// http://b/161386391 for -Wno-void-pointer-to-int-cast
@@ -135,8 +144,8 @@
 
 	// prebuilts/clang default settings.
 	ClangDefaultBase         = "prebuilts/clang/host"
-	ClangDefaultVersion      = "clang-r407598"
-	ClangDefaultShortVersion = "12.0.1"
+	ClangDefaultVersion      = "clang-r407598b"
+	ClangDefaultShortVersion = "12.0.2"
 
 	// Directories with warnings from Android.bp files.
 	WarningAllowedProjects = []string{
diff --git a/cc/config/tidy.go b/cc/config/tidy.go
index 0df17bc..7cc9f43 100644
--- a/cc/config/tidy.go
+++ b/cc/config/tidy.go
@@ -20,30 +20,52 @@
 )
 
 func init() {
-	// Most Android source files are not clang-tidy clean yet.
-	// Default global tidy checks must exclude all checks that
-	// have found too many warnings.
+	// 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{
-			"*",
-			"-altera-*",
+		checks := strings.Join([]string{
+			"-*",
+			"abseil-*",
+			"android-*",
+			"bugprone-*",
+			"cert-*",
+			"clang-diagnostic-unused-command-line-argument",
+			"google-*",
+			"misc-*",
+			"performance-*",
+			"portability-*",
 			"-bugprone-narrowing-conversions",
-			"-cppcoreguidelines-*",
-			"-fuchsia-*",
-			"-google-readability-*",
+			"-google-readability*",
 			"-google-runtime-references",
-			"-hicpp-*",
-			"-llvm-*",
-			"-llvmlibc-*",
 			"-misc-no-recursion",
 			"-misc-non-private-member-variables-in-classes",
 			"-misc-unused-parameters",
-			"-modernize-*",
-			"-readability-*",
+			// 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.
+		if ctx.Config().IsEnvTrue("CLANG_ANALYZER_CHECKS") {
+			checks += ",clang-analyzer-*"
+		}
+		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 0aa6866..45c18c2 100644
--- a/cc/config/vndk.go
+++ b/cc/config/vndk.go
@@ -17,18 +17,47 @@
 // List of VNDK libraries that have different core variant and vendor variant.
 // For these libraries, the vendor variants must be installed even if the device
 // has VndkUseCoreVariant set.
+// TODO(b/150578172): clean up unstable and non-versioned aidl module
 var VndkMustUseVendorVariantList = []string{
 	"android.hardware.authsecret-unstable-ndk_platform",
+	"android.hardware.authsecret-ndk_platform",
+	"android.hardware.authsecret-V1-ndk_platform",
 	"android.hardware.automotive.occupant_awareness-ndk_platform",
+	"android.hardware.automotive.occupant_awareness-V1-ndk_platform",
+	"android.hardware.health.storage-V1-ndk_platform",
+	"android.hardware.health.storage-ndk_platform",
+	"android.hardware.health.storage-unstable-ndk_platform",
+	"android.hardware.light-V1-ndk_platform",
 	"android.hardware.light-ndk_platform",
+	"android.hardware.identity-V2-ndk_platform",
 	"android.hardware.identity-ndk_platform",
 	"android.hardware.nfc@1.2",
+	"android.hardware.memtrack-V1-ndk_platform",
+	"android.hardware.memtrack-ndk_platform",
 	"android.hardware.memtrack-unstable-ndk_platform",
+	"android.hardware.oemlock-V1-ndk_platform",
+	"android.hardware.oemlock-ndk_platform",
 	"android.hardware.oemlock-unstable-ndk_platform",
+	"android.hardware.power-V1-ndk_platform",
 	"android.hardware.power-ndk_platform",
+	"android.hardware.rebootescrow-V1-ndk_platform",
 	"android.hardware.rebootescrow-ndk_platform",
+	"android.hardware.security.keymint-V1-ndk_platform",
+	"android.hardware.security.keymint-ndk_platform",
 	"android.hardware.security.keymint-unstable-ndk_platform",
+	"android.hardware.security.secureclock-V1-ndk_platform",
+	"android.hardware.security.secureclock-unstable-ndk_platform",
+	"android.hardware.security.secureclock-ndk_platform",
+	"android.hardware.security.sharedsecret-V1-ndk_platform",
+	"android.hardware.security.sharedsecret-ndk_platform",
+	"android.hardware.security.sharedsecret-unstable-ndk_platform",
+	"android.hardware.vibrator-V1-ndk_platform",
 	"android.hardware.vibrator-ndk_platform",
+	"android.hardware.weaver-V1-ndk_platform",
+	"android.hardware.weaver-ndk_platform",
+	"android.hardware.weaver-unstable-ndk_platform",
+	"android.system.keystore2-V1-ndk_platform",
+	"android.system.keystore2-ndk_platform",
 	"android.system.keystore2-unstable-ndk_platform",
 	"libbinder",
 	"libcrypto",
diff --git a/cc/config/x86_darwin_host.go b/cc/config/x86_darwin_host.go
index d7ff580..1035df3 100644
--- a/cc/config/x86_darwin_host.go
+++ b/cc/config/x86_darwin_host.go
@@ -67,6 +67,7 @@
 		"10.14",
 		"10.15",
 		"11.0",
+		"11.1",
 	}
 
 	darwinAvailableLibraries = append(
diff --git a/cc/coverage.go b/cc/coverage.go
index acf98dd..5b5ccf2 100644
--- a/cc/coverage.go
+++ b/cc/coverage.go
@@ -58,6 +58,8 @@
 func getClangProfileLibraryName(ctx ModuleContextIntf) string {
 	if ctx.useSdk() {
 		return "libprofile-clang-extras_ndk"
+	} else if ctx.isCfiAssemblySupportEnabled() {
+		return "libprofile-clang-extras_cfi_support"
 	} else {
 		return "libprofile-clang-extras"
 	}
diff --git a/cc/genrule.go b/cc/genrule.go
index 1ce2169..ca4fda7 100644
--- a/cc/genrule.go
+++ b/cc/genrule.go
@@ -25,6 +25,7 @@
 
 type GenruleExtraProperties struct {
 	Vendor_available         *bool
+	Odm_available            *bool
 	Product_available        *bool
 	Ramdisk_available        *bool
 	Vendor_ramdisk_available *bool
@@ -63,7 +64,7 @@
 		return false
 	}
 
-	return Bool(g.Vendor_available) || Bool(g.Product_available) || !(ctx.SocSpecific() || ctx.DeviceSpecific())
+	return !(ctx.SocSpecific() || ctx.DeviceSpecific())
 }
 
 func (g *GenruleExtraProperties) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
@@ -92,7 +93,7 @@
 	}
 
 	var variants []string
-	if Bool(g.Vendor_available) || ctx.SocSpecific() || ctx.DeviceSpecific() {
+	if Bool(g.Vendor_available) || Bool(g.Odm_available) || ctx.SocSpecific() || ctx.DeviceSpecific() {
 		vndkVersion := ctx.DeviceConfig().VndkVersion()
 		// If vndkVersion is current, we can always use PlatformVndkVersion.
 		// If not, we assume modules under proprietary paths are compatible for
diff --git a/cc/image.go b/cc/image.go
index f89194f..231da7e 100644
--- a/cc/image.go
+++ b/cc/image.go
@@ -56,8 +56,14 @@
 
 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()
+	// module. As well as SoC specific modules, vendor variants must be installed to /vendor
+	// unless they have "odm_available: true".
+	return ctx.ModuleContext.SocSpecific() || (ctx.mod.InVendor() && !ctx.mod.VendorVariantToOdm())
+}
+
+func (ctx *moduleContext) DeviceSpecific() bool {
+	// Some vendor variants want to be installed to /odm by setting "odm_available: true".
+	return ctx.ModuleContext.DeviceSpecific() || (ctx.mod.InVendor() && ctx.mod.VendorVariantToOdm())
 }
 
 func (ctx *moduleContextImpl) inProduct() bool {
@@ -82,7 +88,13 @@
 
 // Returns true when this module is configured to have core and vendor variants.
 func (c *Module) HasVendorVariant() bool {
-	return Bool(c.VendorProperties.Vendor_available)
+	return Bool(c.VendorProperties.Vendor_available) || Bool(c.VendorProperties.Odm_available)
+}
+
+// Returns true when this module creates a vendor variant and wants to install the vendor variant
+// to the odm partition.
+func (c *Module) VendorVariantToOdm() bool {
+	return Bool(c.VendorProperties.Odm_available)
 }
 
 // Returns true when this module is configured to have core and product variants.
@@ -183,7 +195,18 @@
 	if Bool(m.VendorProperties.Vendor_available) {
 		if vendorSpecific {
 			mctx.PropertyErrorf("vendor_available",
-				"doesn't make sense at the same time as `vendor: true`, `proprietary: true`, or `device_specific:true`")
+				"doesn't make sense at the same time as `vendor: true`, `proprietary: true`, or `device_specific: true`")
+		}
+		if Bool(m.VendorProperties.Odm_available) {
+			mctx.PropertyErrorf("vendor_available",
+				"doesn't make sense at the same time as `odm_available: true`")
+		}
+	}
+
+	if Bool(m.VendorProperties.Odm_available) {
+		if vendorSpecific {
+			mctx.PropertyErrorf("odm_available",
+				"doesn't make sense at the same time as `vendor: true`, `proprietary: true`, or `device_specific: true`")
 		}
 	}
 
diff --git a/cc/library.go b/cc/library.go
index bc6ff69..29a3c69 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -449,24 +449,39 @@
 			}
 			continue
 		}
-		exts := headerExts
-		// Glob all files under this special directory, because of C++ headers.
-		if strings.HasPrefix(dir, "external/libcxx/include") {
-			exts = []string{""}
+		glob, err := ctx.GlobWithDeps(dir+"/**/*", nil)
+		if err != nil {
+			ctx.ModuleErrorf("glob failed: %#v", err)
+			return
 		}
-		for _, ext := range exts {
-			glob, err := ctx.GlobWithDeps(dir+"/**/*"+ext, nil)
-			if err != nil {
-				ctx.ModuleErrorf("glob failed: %#v", err)
-				return
-			}
-			for _, header := range glob {
-				if strings.HasSuffix(header, "/") {
+		isLibcxx := strings.HasPrefix(dir, "external/libcxx/include")
+		j := 0
+		for i, header := range glob {
+			if isLibcxx {
+				// Glob all files under this special directory, because of C++ headers with no
+				// extension.
+				if !strings.HasSuffix(header, "/") {
 					continue
 				}
-				ret = append(ret, android.PathForSource(ctx, header))
+			} else {
+				// Filter out only the files with extensions that are headers.
+				found := false
+				for _, ext := range headerExts {
+					if strings.HasSuffix(header, ext) {
+						found = true
+						break
+					}
+				}
+				if !found {
+					continue
+				}
 			}
+			if i != j {
+				glob[j] = glob[i]
+			}
+			j++
 		}
+		glob = glob[:j]
 	}
 
 	// Collect generated headers
@@ -1318,17 +1333,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/library_sdk_member.go b/cc/library_sdk_member.go
index 3f2cf49..1402991 100644
--- a/cc/library_sdk_member.go
+++ b/cc/library_sdk_member.go
@@ -130,6 +130,10 @@
 		pbm.AddProperty("vendor_available", true)
 	}
 
+	if proptools.Bool(ccModule.VendorProperties.Odm_available) {
+		pbm.AddProperty("odm_available", true)
+	}
+
 	if proptools.Bool(ccModule.VendorProperties.Product_available) {
 		pbm.AddProperty("product_available", true)
 	}
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index a5c43fe..10de889 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -354,6 +354,5 @@
 func NdkLibraryFactory() android.Module {
 	module := newStubLibrary()
 	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibBoth)
-	module.ModuleBase.EnableNativeBridgeSupportByDefault()
 	return module
 }
diff --git a/cc/ndk_prebuilt.go b/cc/ndk_prebuilt.go
index 793ab37..8d522d0 100644
--- a/cc/ndk_prebuilt.go
+++ b/cc/ndk_prebuilt.go
@@ -70,7 +70,6 @@
 // ./prebuilts/ndk/current/platforms/android-<sdk_version>/arch-$(HOST_ARCH)/usr/lib/<NAME>.o.
 func NdkPrebuiltObjectFactory() android.Module {
 	module := newBaseModule(android.DeviceSupported, android.MultilibBoth)
-	module.ModuleBase.EnableNativeBridgeSupportByDefault()
 	module.linker = &ndkPrebuiltObjectLinker{
 		objectLinker: objectLinker{
 			baseLinker: NewBaseLinker(nil),
@@ -150,7 +149,6 @@
 	module.Properties.AlwaysSdk = true
 	module.Properties.Sdk_version = StringPtr("current")
 	module.stl.Properties.Stl = StringPtr("none")
-	module.ModuleBase.EnableNativeBridgeSupportByDefault()
 	return module.Init()
 }
 
diff --git a/cc/sanitize.go b/cc/sanitize.go
index af17490..8eeb355 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -267,6 +267,12 @@
 		return
 	}
 
+	// cc_test targets default to SYNC MemTag unless explicitly set to ASYNC (via diag: {memtag_heap}).
+	if ctx.testBinary() && s.Memtag_heap == nil {
+		s.Memtag_heap = boolPtr(true)
+		s.Diag.Memtag_heap = boolPtr(true)
+	}
+
 	var globalSanitizers []string
 	var globalSanitizersDiag []string
 
@@ -358,27 +364,29 @@
 			s.Diag.Cfi = boolPtr(true)
 		}
 
+		if found, globalSanitizersDiag = removeFromList("memtag_heap", globalSanitizersDiag); found &&
+			s.Diag.Memtag_heap == nil && Bool(s.Memtag_heap) {
+			s.Diag.Memtag_heap = boolPtr(true)
+		}
+
 		if len(globalSanitizersDiag) > 0 {
 			ctx.ModuleErrorf("unknown global sanitizer diagnostics option %s", globalSanitizersDiag[0])
 		}
 	}
 
-	// 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.Arch().ArchType == android.Arm64 {
 		if ctx.Config().MemtagHeapSyncEnabledForPath(ctx.ModuleDir()) {
-			s.Memtag_heap = boolPtr(true)
-			s.Diag.Memtag_heap = boolPtr(true)
+			if s.Memtag_heap == nil {
+				s.Memtag_heap = boolPtr(true)
+			}
+			if s.Diag.Memtag_heap == nil {
+				s.Diag.Memtag_heap = boolPtr(true)
+			}
 		} else if ctx.Config().MemtagHeapAsyncEnabledForPath(ctx.ModuleDir()) {
-			s.Memtag_heap = boolPtr(true)
-			s.Diag.Memtag_heap = boolPtr(false)
+			if s.Memtag_heap == nil {
+				s.Memtag_heap = boolPtr(true)
+			}
 		}
 	}
 
diff --git a/cc/sdk.go b/cc/sdk.go
index ec57f06..2c3fec3 100644
--- a/cc/sdk.go
+++ b/cc/sdk.go
@@ -38,18 +38,31 @@
 			ctx.CreateVariations("sdk")
 		} else if m.UseSdk() || m.SplitPerApiLevel() {
 			modules := ctx.CreateVariations("", "sdk")
+
+			// Clear the sdk_version property for the platform (non-SDK) variant so later code
+			// doesn't get confused by it.
 			modules[0].(*Module).Properties.Sdk_version = nil
+
+			// Mark the SDK variant.
 			modules[1].(*Module).Properties.IsSdkVariant = true
 
 			if ctx.Config().UnbundledBuildApps() {
+				// For an unbundled apps build, hide the platform variant from Make.
 				modules[0].(*Module).Properties.HideFromMake = true
 				modules[0].(*Module).Properties.PreventInstall = true
 			} else {
+				// For a platform build, mark the SDK variant so that it gets a ".sdk" suffix when
+				// exposed to Make.
 				modules[1].(*Module).Properties.SdkAndPlatformVariantVisibleToMake = true
 				modules[1].(*Module).Properties.PreventInstall = true
 			}
 			ctx.AliasVariation("")
 		} else {
+			if m, ok := ctx.Module().(*Module); ok {
+				// Clear the sdk_version property for modules that don't have an SDK variant so
+				// later code doesn't get confused by it.
+				m.Properties.Sdk_version = nil
+			}
 			ctx.CreateVariations("")
 			ctx.AliasVariation("")
 		}
diff --git a/cc/snapshot_prebuilt.go b/cc/snapshot_prebuilt.go
index 8979846..2003e03 100644
--- a/cc/snapshot_prebuilt.go
+++ b/cc/snapshot_prebuilt.go
@@ -40,10 +40,10 @@
 	// evalution of a function that may be not be defined.
 	inImage(m *Module) func() bool
 
-	// Returns the value of the "available" property for a given module for
-	// and snapshot, e.g., "vendor_available", "recovery_available", etc.
-	// or nil if the property is not defined.
-	available(m *Module) *bool
+	// Returns true if the module is private and must not be included in the
+	// snapshot. For example VNDK-private modules must return true for the
+	// vendor snapshots. But false for the recovery snapshots.
+	private(m *Module) bool
 
 	// Returns true if a dir under source tree is an SoC-owned proprietary
 	// directory, such as device/, vendor/, etc.
@@ -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{}
@@ -107,8 +112,8 @@
 	return m.InVendor
 }
 
-func (vendorSnapshotImage) available(m *Module) *bool {
-	return m.VendorProperties.Vendor_available
+func (vendorSnapshotImage) private(m *Module) bool {
+	return m.IsVndkPrivate()
 }
 
 func (vendorSnapshotImage) isProprietaryPath(dir string) 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)
@@ -212,8 +227,9 @@
 	return m.InRecovery
 }
 
-func (recoverySnapshotImage) available(m *Module) *bool {
-	return m.Properties.Recovery_available
+// recovery snapshot does not have private libraries.
+func (recoverySnapshotImage) private(m *Module) bool {
+	return false
 }
 
 func (recoverySnapshotImage) isProprietaryPath(dir string) bool {
@@ -275,6 +291,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
 
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/testing.go b/cc/testing.go
index 903f76c..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,
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 0a89e47..7346aac 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -23,8 +23,6 @@
 	"sort"
 	"strings"
 
-	"github.com/google/blueprint/proptools"
-
 	"android/soong/android"
 )
 
@@ -177,15 +175,15 @@
 
 func isRecoveryProprietaryModule(ctx android.BaseModuleContext) bool {
 
-	// Any module in a vendor proprietary path is a vendor proprietary
+	// Any module in a recovery proprietary path is a recovery proprietary
 	// module.
 	if isRecoveryProprietaryPath(ctx.ModuleDir()) {
 		return true
 	}
 
-	// However if the module is not in a vendor proprietary path, it may
-	// still be a vendor proprietary module. This happens for cc modules
-	// that are excluded from the vendor snapshot, and it means that the
+	// However if the module is not in a recovery proprietary path, it may
+	// still be a recovery proprietary module. This happens for cc modules
+	// that are excluded from the recovery snapshot, and it means that the
 	// vendor has assumed control of the framework-provided module.
 
 	if c, ok := ctx.Module().(*Module); ok {
@@ -198,7 +196,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,6 +239,10 @@
 	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 {
@@ -260,7 +262,7 @@
 			}
 		}
 		if l.static() {
-			return m.outputFile.Valid() && proptools.BoolDefault(image.available(m), true)
+			return m.outputFile.Valid() && !image.private(m)
 		}
 		if l.shared() {
 			if !m.outputFile.Valid() {
@@ -278,7 +280,7 @@
 
 	// Binaries and Objects
 	if m.binary() || m.object() {
-		return m.outputFile.Valid() && proptools.BoolDefault(image.available(m), true)
+		return m.outputFile.Valid()
 	}
 
 	return false
@@ -522,20 +524,9 @@
 				ctx.Errorf("module %q in vendor proprietary path %q may not use \"exclude_from_vendor_snapshot: true\"", m.String(), moduleDir)
 				return
 			}
-			if Bool(c.image.available(m)) {
-				// Error: may not combine "vendor_available:
-				// true" with "exclude_from_vendor_snapshot:
-				// true".
-				ctx.Errorf(
-					"module %q may not use both \""+
-						c.name+
-						"_available: true\" and \"exclude_from_vendor_snapshot: true\"",
-					m.String())
-				return
-			}
 		}
 
-		if !isSnapshotAware(m, inProprietaryPath, apexInfo, c.image) {
+		if !isSnapshotAware(ctx.DeviceConfig(), m, inProprietaryPath, apexInfo, c.image) {
 			return
 		}
 
diff --git a/cc/vendor_snapshot_test.go b/cc/vendor_snapshot_test.go
new file mode 100644
index 0000000..cce28b0
--- /dev/null
+++ b/cc/vendor_snapshot_test.go
@@ -0,0 +1,1006 @@
+// 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 cc
+
+import (
+	"android/soong/android"
+	"fmt"
+	"path/filepath"
+	"reflect"
+	"strings"
+	"testing"
+)
+
+func TestVendorSnapshotCapture(t *testing.T) {
+	bp := `
+	cc_library {
+		name: "libvndk",
+		vendor_available: true,
+		product_available: true,
+		vndk: {
+			enabled: true,
+		},
+		nocrt: true,
+	}
+
+	cc_library {
+		name: "libvendor",
+		vendor: true,
+		nocrt: true,
+	}
+
+	cc_library {
+		name: "libvendor_available",
+		vendor_available: true,
+		nocrt: true,
+	}
+
+	cc_library_headers {
+		name: "libvendor_headers",
+		vendor_available: true,
+		nocrt: true,
+	}
+
+	cc_binary {
+		name: "vendor_bin",
+		vendor: true,
+		nocrt: true,
+	}
+
+	cc_binary {
+		name: "vendor_available_bin",
+		vendor_available: true,
+		nocrt: true,
+	}
+
+	toolchain_library {
+		name: "libb",
+		vendor_available: true,
+		src: "libb.a",
+	}
+
+	cc_object {
+		name: "obj",
+		vendor_available: true,
+	}
+
+	cc_library {
+		name: "libllndk",
+		llndk_stubs: "libllndk.llndk",
+	}
+
+	llndk_library {
+		name: "libllndk.llndk",
+		symbol_file: "",
+	}
+`
+	config := TestConfig(buildDir, android.Android, nil, bp, nil)
+	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
+	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+	ctx := testCcWithConfig(t, config)
+
+	// Check Vendor snapshot output.
+
+	snapshotDir := "vendor-snapshot"
+	snapshotVariantPath := filepath.Join(buildDir, snapshotDir, "arm64")
+	snapshotSingleton := ctx.SingletonForTests("vendor-snapshot")
+
+	var jsonFiles []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)
+
+		// For shared libraries, only non-VNDK vendor_available modules are captured
+		sharedVariant := fmt.Sprintf("android_vendor.VER_%s_%s_shared", archType, archVariant)
+		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
+		checkSnapshot(t, ctx, snapshotSingleton, "libvendor", "libvendor.so", sharedDir, sharedVariant)
+		checkSnapshot(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.so", sharedDir, sharedVariant)
+		jsonFiles = append(jsonFiles,
+			filepath.Join(sharedDir, "libvendor.so.json"),
+			filepath.Join(sharedDir, "libvendor_available.so.json"))
+
+		// LLNDK modules are not captured
+		checkSnapshotExclude(t, ctx, snapshotSingleton, "libllndk", "libllndk.so", sharedDir, sharedVariant)
+
+		// For static libraries, all vendor:true and vendor_available modules (including VNDK) are captured.
+		// Also cfi variants are captured, except for prebuilts like toolchain_library
+		staticVariant := fmt.Sprintf("android_vendor.VER_%s_%s_static", archType, archVariant)
+		staticCfiVariant := fmt.Sprintf("android_vendor.VER_%s_%s_static_cfi", archType, archVariant)
+		staticDir := filepath.Join(snapshotVariantPath, archDir, "static")
+		checkSnapshot(t, ctx, snapshotSingleton, "libb", "libb.a", staticDir, staticVariant)
+		checkSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.a", staticDir, staticVariant)
+		checkSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.cfi.a", staticDir, staticCfiVariant)
+		checkSnapshot(t, ctx, snapshotSingleton, "libvendor", "libvendor.a", staticDir, staticVariant)
+		checkSnapshot(t, ctx, snapshotSingleton, "libvendor", "libvendor.cfi.a", staticDir, staticCfiVariant)
+		checkSnapshot(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.a", staticDir, staticVariant)
+		checkSnapshot(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.cfi.a", staticDir, staticCfiVariant)
+		jsonFiles = append(jsonFiles,
+			filepath.Join(staticDir, "libb.a.json"),
+			filepath.Join(staticDir, "libvndk.a.json"),
+			filepath.Join(staticDir, "libvndk.cfi.a.json"),
+			filepath.Join(staticDir, "libvendor.a.json"),
+			filepath.Join(staticDir, "libvendor.cfi.a.json"),
+			filepath.Join(staticDir, "libvendor_available.a.json"),
+			filepath.Join(staticDir, "libvendor_available.cfi.a.json"))
+
+		// For binary executables, all vendor:true and vendor_available modules are captured.
+		if archType == "arm64" {
+			binaryVariant := fmt.Sprintf("android_vendor.VER_%s_%s", archType, archVariant)
+			binaryDir := filepath.Join(snapshotVariantPath, archDir, "binary")
+			checkSnapshot(t, ctx, snapshotSingleton, "vendor_bin", "vendor_bin", binaryDir, binaryVariant)
+			checkSnapshot(t, ctx, snapshotSingleton, "vendor_available_bin", "vendor_available_bin", binaryDir, binaryVariant)
+			jsonFiles = append(jsonFiles,
+				filepath.Join(binaryDir, "vendor_bin.json"),
+				filepath.Join(binaryDir, "vendor_available_bin.json"))
+		}
+
+		// For header libraries, all vendor:true and vendor_available modules are captured.
+		headerDir := filepath.Join(snapshotVariantPath, archDir, "header")
+		jsonFiles = append(jsonFiles, filepath.Join(headerDir, "libvendor_headers.json"))
+
+		// For object modules, all vendor:true and vendor_available modules are captured.
+		objectVariant := fmt.Sprintf("android_vendor.VER_%s_%s", archType, archVariant)
+		objectDir := filepath.Join(snapshotVariantPath, archDir, "object")
+		checkSnapshot(t, ctx, snapshotSingleton, "obj", "obj.o", objectDir, objectVariant)
+		jsonFiles = append(jsonFiles, filepath.Join(objectDir, "obj.o.json"))
+	}
+
+	for _, jsonFile := range jsonFiles {
+		// verify all json files exist
+		if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
+			t.Errorf("%q expected but not found", jsonFile)
+		}
+	}
+
+	// fake snapshot should have all outputs in the normal snapshot.
+	fakeSnapshotSingleton := ctx.SingletonForTests("vendor-fake-snapshot")
+	for _, output := range snapshotSingleton.AllOutputs() {
+		fakeOutput := strings.Replace(output, "/vendor-snapshot/", "/fake/vendor-snapshot/", 1)
+		if fakeSnapshotSingleton.MaybeOutput(fakeOutput).Rule == nil {
+			t.Errorf("%q expected but not found", fakeOutput)
+		}
+	}
+}
+
+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 {
+		name: "libvndk",
+		vendor_available: true,
+		product_available: true,
+		vndk: {
+			enabled: true,
+		},
+		nocrt: true,
+		compile_multilib: "64",
+	}
+
+	cc_library {
+		name: "libvendor",
+		vendor: true,
+		nocrt: true,
+		no_libcrt: true,
+		stl: "none",
+		system_shared_libs: [],
+		compile_multilib: "64",
+	}
+
+	cc_library {
+		name: "libvendor_available",
+		vendor_available: true,
+		nocrt: true,
+		no_libcrt: true,
+		stl: "none",
+		system_shared_libs: [],
+		compile_multilib: "64",
+	}
+
+	cc_binary {
+		name: "bin",
+		vendor: true,
+		nocrt: true,
+		no_libcrt: true,
+		stl: "none",
+		system_shared_libs: [],
+		compile_multilib: "64",
+	}
+`
+
+	vndkBp := `
+	vndk_prebuilt_shared {
+		name: "libvndk",
+		version: "BOARD",
+		target_arch: "arm64",
+		vendor_available: true,
+		product_available: true,
+		vndk: {
+			enabled: true,
+		},
+		arch: {
+			arm64: {
+				srcs: ["libvndk.so"],
+				export_include_dirs: ["include/libvndk"],
+			},
+		},
+	}
+`
+
+	vendorProprietaryBp := `
+	cc_library {
+		name: "libvendor_without_snapshot",
+		vendor: true,
+		nocrt: true,
+		no_libcrt: true,
+		stl: "none",
+		system_shared_libs: [],
+		compile_multilib: "64",
+	}
+
+	cc_library_shared {
+		name: "libclient",
+		vendor: true,
+		nocrt: true,
+		no_libcrt: true,
+		stl: "none",
+		system_shared_libs: [],
+		shared_libs: ["libvndk", "libvendor_available"],
+		static_libs: ["libvendor", "libvendor_without_snapshot"],
+		compile_multilib: "64",
+		srcs: ["client.cpp"],
+	}
+
+	cc_binary {
+		name: "bin_without_snapshot",
+		vendor: true,
+		nocrt: true,
+		no_libcrt: true,
+		stl: "none",
+		system_shared_libs: [],
+		static_libs: ["libvndk"],
+		compile_multilib: "64",
+		srcs: ["bin.cpp"],
+	}
+
+	vendor_snapshot_static {
+		name: "libvndk",
+		version: "BOARD",
+		target_arch: "arm64",
+		vendor: true,
+		arch: {
+			arm64: {
+				src: "libvndk.a",
+				export_include_dirs: ["include/libvndk"],
+			},
+		},
+	}
+
+	vendor_snapshot_shared {
+		name: "libvendor",
+		version: "BOARD",
+		target_arch: "arm64",
+		vendor: true,
+		arch: {
+			arm64: {
+				src: "libvendor.so",
+				export_include_dirs: ["include/libvendor"],
+			},
+		},
+	}
+
+	vendor_snapshot_static {
+		name: "libvendor",
+		version: "BOARD",
+		target_arch: "arm64",
+		vendor: true,
+		arch: {
+			arm64: {
+				src: "libvendor.a",
+				export_include_dirs: ["include/libvendor"],
+			},
+		},
+	}
+
+	vendor_snapshot_shared {
+		name: "libvendor_available",
+		version: "BOARD",
+		target_arch: "arm64",
+		vendor: true,
+		arch: {
+			arm64: {
+				src: "libvendor_available.so",
+				export_include_dirs: ["include/libvendor"],
+			},
+		},
+	}
+
+	vendor_snapshot_static {
+		name: "libvendor_available",
+		version: "BOARD",
+		target_arch: "arm64",
+		vendor: true,
+		arch: {
+			arm64: {
+				src: "libvendor_available.a",
+				export_include_dirs: ["include/libvendor"],
+			},
+		},
+	}
+
+	vendor_snapshot_binary {
+		name: "bin",
+		version: "BOARD",
+		target_arch: "arm64",
+		vendor: true,
+		arch: {
+			arm64: {
+				src: "bin",
+			},
+		},
+	}
+`
+	depsBp := GatherRequiredDepsForTest(android.Android)
+
+	mockFS := map[string][]byte{
+		"deps/Android.bp":              []byte(depsBp),
+		"framework/Android.bp":         []byte(frameworkBp),
+		"vendor/Android.bp":            []byte(vendorProprietaryBp),
+		"vendor/bin":                   nil,
+		"vendor/bin.cpp":               nil,
+		"vendor/client.cpp":            nil,
+		"vendor/include/libvndk/a.h":   nil,
+		"vendor/include/libvendor/b.h": nil,
+		"vendor/libvndk.a":             nil,
+		"vendor/libvendor.a":           nil,
+		"vendor/libvendor.so":          nil,
+		"vndk/Android.bp":              []byte(vndkBp),
+		"vndk/include/libvndk/a.h":     nil,
+		"vndk/libvndk.so":              nil,
+	}
+
+	config := TestConfig(buildDir, android.Android, nil, "", mockFS)
+	config.TestProductVariables.DeviceVndkVersion = StringPtr("BOARD")
+	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+	ctx := CreateTestContext(config)
+	ctx.Register()
+
+	_, errs := ctx.ParseFileList(".", []string{"deps/Android.bp", "framework/Android.bp", "vendor/Android.bp", "vndk/Android.bp"})
+	android.FailIfErrored(t, errs)
+	_, errs = ctx.PrepareBuildActions(config)
+	android.FailIfErrored(t, errs)
+
+	sharedVariant := "android_vendor.BOARD_arm64_armv8-a_shared"
+	staticVariant := "android_vendor.BOARD_arm64_armv8-a_static"
+	binaryVariant := "android_vendor.BOARD_arm64_armv8-a"
+
+	// libclient uses libvndk.vndk.BOARD.arm64, libvendor.vendor_static.BOARD.arm64, libvendor_without_snapshot
+	libclientCcFlags := ctx.ModuleForTests("libclient", sharedVariant).Rule("cc").Args["cFlags"]
+	for _, includeFlags := range []string{
+		"-Ivndk/include/libvndk",     // libvndk
+		"-Ivendor/include/libvendor", // libvendor
+	} {
+		if !strings.Contains(libclientCcFlags, includeFlags) {
+			t.Errorf("flags for libclient must contain %#v, but was %#v.",
+				includeFlags, libclientCcFlags)
+		}
+	}
+
+	libclientLdFlags := ctx.ModuleForTests("libclient", sharedVariant).Rule("ld").Args["libFlags"]
+	for _, input := range [][]string{
+		[]string{sharedVariant, "libvndk.vndk.BOARD.arm64"},
+		[]string{staticVariant, "libvendor.vendor_static.BOARD.arm64"},
+		[]string{staticVariant, "libvendor_without_snapshot"},
+	} {
+		outputPaths := getOutputPaths(ctx, input[0] /* variant */, []string{input[1]} /* module name */)
+		if !strings.Contains(libclientLdFlags, outputPaths[0].String()) {
+			t.Errorf("libflags for libclient must contain %#v, but was %#v", outputPaths[0], libclientLdFlags)
+		}
+	}
+
+	libclientAndroidMkSharedLibs := ctx.ModuleForTests("libclient", sharedVariant).Module().(*Module).Properties.AndroidMkSharedLibs
+	if g, w := libclientAndroidMkSharedLibs, []string{"libvndk.vendor", "libvendor_available.vendor"}; !reflect.DeepEqual(g, w) {
+		t.Errorf("wanted libclient AndroidMkSharedLibs %q, got %q", w, g)
+	}
+
+	libclientAndroidMkStaticLibs := ctx.ModuleForTests("libclient", sharedVariant).Module().(*Module).Properties.AndroidMkStaticLibs
+	if g, w := libclientAndroidMkStaticLibs, []string{"libvendor", "libvendor_without_snapshot"}; !reflect.DeepEqual(g, w) {
+		t.Errorf("wanted libclient AndroidMkStaticLibs %q, got %q", w, g)
+	}
+
+	// bin_without_snapshot uses libvndk.vendor_static.BOARD.arm64
+	binWithoutSnapshotCcFlags := ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Rule("cc").Args["cFlags"]
+	if !strings.Contains(binWithoutSnapshotCcFlags, "-Ivendor/include/libvndk") {
+		t.Errorf("flags for bin_without_snapshot must contain %#v, but was %#v.",
+			"-Ivendor/include/libvndk", binWithoutSnapshotCcFlags)
+	}
+
+	binWithoutSnapshotLdFlags := ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Rule("ld").Args["libFlags"]
+	libVndkStaticOutputPaths := getOutputPaths(ctx, staticVariant, []string{"libvndk.vendor_static.BOARD.arm64"})
+	if !strings.Contains(binWithoutSnapshotLdFlags, libVndkStaticOutputPaths[0].String()) {
+		t.Errorf("libflags for bin_without_snapshot must contain %#v, but was %#v",
+			libVndkStaticOutputPaths[0], binWithoutSnapshotLdFlags)
+	}
+
+	// libvendor.so is installed by libvendor.vendor_shared.BOARD.arm64
+	ctx.ModuleForTests("libvendor.vendor_shared.BOARD.arm64", sharedVariant).Output("libvendor.so")
+
+	// libvendor_available.so is installed by libvendor_available.vendor_shared.BOARD.arm64
+	ctx.ModuleForTests("libvendor_available.vendor_shared.BOARD.arm64", sharedVariant).Output("libvendor_available.so")
+
+	// libvendor_without_snapshot.so is installed by libvendor_without_snapshot
+	ctx.ModuleForTests("libvendor_without_snapshot", sharedVariant).Output("libvendor_without_snapshot.so")
+
+	// bin is installed by bin.vendor_binary.BOARD.arm64
+	ctx.ModuleForTests("bin.vendor_binary.BOARD.arm64", binaryVariant).Output("bin")
+
+	// bin_without_snapshot is installed by bin_without_snapshot
+	ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Output("bin_without_snapshot")
+
+	// libvendor, libvendor_available and bin don't have vendor.BOARD variant
+	libvendorVariants := ctx.ModuleVariantsForTests("libvendor")
+	if inList(sharedVariant, libvendorVariants) {
+		t.Errorf("libvendor must not have variant %#v, but it does", sharedVariant)
+	}
+
+	libvendorAvailableVariants := ctx.ModuleVariantsForTests("libvendor_available")
+	if inList(sharedVariant, libvendorAvailableVariants) {
+		t.Errorf("libvendor_available must not have variant %#v, but it does", sharedVariant)
+	}
+
+	binVariants := ctx.ModuleVariantsForTests("bin")
+	if inList(binaryVariant, binVariants) {
+		t.Errorf("bin must not have variant %#v, but it does", sharedVariant)
+	}
+}
+
+func TestVendorSnapshotSanitizer(t *testing.T) {
+	bp := `
+	vendor_snapshot_static {
+		name: "libsnapshot",
+		vendor: true,
+		target_arch: "arm64",
+		version: "BOARD",
+		arch: {
+			arm64: {
+				src: "libsnapshot.a",
+				cfi: {
+					src: "libsnapshot.cfi.a",
+				}
+			},
+		},
+	}
+`
+	config := TestConfig(buildDir, android.Android, nil, bp, nil)
+	config.TestProductVariables.DeviceVndkVersion = StringPtr("BOARD")
+	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+	ctx := testCcWithConfig(t, config)
+
+	// Check non-cfi and cfi variant.
+	staticVariant := "android_vendor.BOARD_arm64_armv8-a_static"
+	staticCfiVariant := "android_vendor.BOARD_arm64_armv8-a_static_cfi"
+
+	staticModule := ctx.ModuleForTests("libsnapshot.vendor_static.BOARD.arm64", staticVariant).Module().(*Module)
+	assertString(t, staticModule.outputFile.Path().Base(), "libsnapshot.a")
+
+	staticCfiModule := ctx.ModuleForTests("libsnapshot.vendor_static.BOARD.arm64", staticCfiVariant).Module().(*Module)
+	assertString(t, staticCfiModule.outputFile.Path().Base(), "libsnapshot.cfi.a")
+}
+
+func assertExcludeFromVendorSnapshotIs(t *testing.T, ctx *android.TestContext, name string, expected bool) {
+	t.Helper()
+	m := ctx.ModuleForTests(name, vendorVariant).Module().(*Module)
+	if m.ExcludeFromVendorSnapshot() != expected {
+		t.Errorf("expected %q ExcludeFromVendorSnapshot to be %t", m.String(), expected)
+	}
+}
+
+func assertExcludeFromRecoverySnapshotIs(t *testing.T, ctx *android.TestContext, name string, expected bool) {
+	t.Helper()
+	m := ctx.ModuleForTests(name, recoveryVariant).Module().(*Module)
+	if m.ExcludeFromRecoverySnapshot() != expected {
+		t.Errorf("expected %q ExcludeFromRecoverySnapshot to be %t", m.String(), expected)
+	}
+}
+
+func TestVendorSnapshotExclude(t *testing.T) {
+
+	// This test verifies that the exclude_from_vendor_snapshot property
+	// makes its way from the Android.bp source file into the module data
+	// structure. It also verifies that modules are correctly included or
+	// excluded in the vendor snapshot based on their path (framework or
+	// vendor) and the exclude_from_vendor_snapshot property.
+
+	frameworkBp := `
+		cc_library_shared {
+			name: "libinclude",
+			srcs: ["src/include.cpp"],
+			vendor_available: true,
+		}
+		cc_library_shared {
+			name: "libexclude",
+			srcs: ["src/exclude.cpp"],
+			vendor: true,
+			exclude_from_vendor_snapshot: true,
+		}
+		cc_library_shared {
+			name: "libavailable_exclude",
+			srcs: ["src/exclude.cpp"],
+			vendor_available: true,
+			exclude_from_vendor_snapshot: true,
+		}
+	`
+
+	vendorProprietaryBp := `
+		cc_library_shared {
+			name: "libvendor",
+			srcs: ["vendor.cpp"],
+			vendor: true,
+		}
+	`
+
+	depsBp := GatherRequiredDepsForTest(android.Android)
+
+	mockFS := map[string][]byte{
+		"deps/Android.bp":       []byte(depsBp),
+		"framework/Android.bp":  []byte(frameworkBp),
+		"framework/include.cpp": nil,
+		"framework/exclude.cpp": nil,
+		"device/Android.bp":     []byte(vendorProprietaryBp),
+		"device/vendor.cpp":     nil,
+	}
+
+	config := TestConfig(buildDir, android.Android, nil, "", mockFS)
+	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
+	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+	ctx := CreateTestContext(config)
+	ctx.Register()
+
+	_, errs := ctx.ParseFileList(".", []string{"deps/Android.bp", "framework/Android.bp", "device/Android.bp"})
+	android.FailIfErrored(t, errs)
+	_, errs = ctx.PrepareBuildActions(config)
+	android.FailIfErrored(t, errs)
+
+	// Test an include and exclude framework module.
+	assertExcludeFromVendorSnapshotIs(t, ctx, "libinclude", false)
+	assertExcludeFromVendorSnapshotIs(t, ctx, "libexclude", true)
+	assertExcludeFromVendorSnapshotIs(t, ctx, "libavailable_exclude", true)
+
+	// A vendor module is excluded, but by its path, not the
+	// exclude_from_vendor_snapshot property.
+	assertExcludeFromVendorSnapshotIs(t, ctx, "libvendor", false)
+
+	// Verify the content of the vendor snapshot.
+
+	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, "libinclude", "libinclude.so", sharedDir, sharedVariant)
+		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libinclude.so.json"))
+
+		// Excluded modules
+		checkSnapshotExclude(t, ctx, snapshotSingleton, "libexclude", "libexclude.so", sharedDir, sharedVariant)
+		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libexclude.so.json"))
+		checkSnapshotExclude(t, ctx, snapshotSingleton, "libvendor", "libvendor.so", sharedDir, sharedVariant)
+		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libvendor.so.json"))
+		checkSnapshotExclude(t, ctx, snapshotSingleton, "libavailable_exclude", "libavailable_exclude.so", sharedDir, sharedVariant)
+		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libavailable_exclude.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 TestVendorSnapshotExcludeInVendorProprietaryPathErrors(t *testing.T) {
+
+	// This test verifies that using the exclude_from_vendor_snapshot
+	// property on a module in a vendor proprietary path generates an
+	// error. These modules are already excluded, so we prohibit using the
+	// property in this way, which could add to confusion.
+
+	vendorProprietaryBp := `
+		cc_library_shared {
+			name: "libvendor",
+			srcs: ["vendor.cpp"],
+			vendor: true,
+			exclude_from_vendor_snapshot: true,
+		}
+	`
+
+	depsBp := GatherRequiredDepsForTest(android.Android)
+
+	mockFS := map[string][]byte{
+		"deps/Android.bp":   []byte(depsBp),
+		"device/Android.bp": []byte(vendorProprietaryBp),
+		"device/vendor.cpp": nil,
+	}
+
+	config := TestConfig(buildDir, android.Android, nil, "", mockFS)
+	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
+	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+	ctx := CreateTestContext(config)
+	ctx.Register()
+
+	_, errs := ctx.ParseFileList(".", []string{"deps/Android.bp", "device/Android.bp"})
+	android.FailIfErrored(t, errs)
+
+	_, errs = ctx.PrepareBuildActions(config)
+	android.CheckErrorsAgainstExpectations(t, errs, []string{
+		`module "libvendor\{.+,image:vendor.+,arch:arm64_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
+		`module "libvendor\{.+,image:vendor.+,arch:arm_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
+		`module "libvendor\{.+,image:vendor.+,arch:arm64_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
+		`module "libvendor\{.+,image:vendor.+,arch:arm_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
+		`module "libvendor\{.+,image:vendor.+,arch:arm64_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
+		`module "libvendor\{.+,image:vendor.+,arch:arm_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
+	})
+}
+
+func TestRecoverySnapshotCapture(t *testing.T) {
+	bp := `
+	cc_library {
+		name: "libvndk",
+		vendor_available: true,
+		recovery_available: true,
+		product_available: true,
+		vndk: {
+			enabled: true,
+		},
+		nocrt: true,
+	}
+
+	cc_library {
+		name: "librecovery",
+		recovery: true,
+		nocrt: true,
+	}
+
+	cc_library {
+		name: "librecovery_available",
+		recovery_available: true,
+		nocrt: true,
+	}
+
+	cc_library_headers {
+		name: "librecovery_headers",
+		recovery_available: true,
+		nocrt: true,
+	}
+
+	cc_binary {
+		name: "recovery_bin",
+		recovery: true,
+		nocrt: true,
+	}
+
+	cc_binary {
+		name: "recovery_available_bin",
+		recovery_available: true,
+		nocrt: true,
+	}
+
+	toolchain_library {
+		name: "libb",
+		recovery_available: true,
+		src: "libb.a",
+	}
+
+	cc_object {
+		name: "obj",
+		recovery_available: true,
+	}
+`
+	config := TestConfig(buildDir, android.Android, nil, bp, nil)
+	config.TestProductVariables.RecoverySnapshotVersion = StringPtr("current")
+	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+	ctx := testCcWithConfig(t, config)
+
+	// Check Recovery snapshot output.
+
+	snapshotDir := "recovery-snapshot"
+	snapshotVariantPath := filepath.Join(buildDir, snapshotDir, "arm64")
+	snapshotSingleton := ctx.SingletonForTests("recovery-snapshot")
+
+	var jsonFiles []string
+
+	for _, arch := range [][]string{
+		[]string{"arm64", "armv8-a"},
+	} {
+		archType := arch[0]
+		archVariant := arch[1]
+		archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
+
+		// For shared libraries, only recovery_available modules are captured.
+		sharedVariant := fmt.Sprintf("android_recovery_%s_%s_shared", archType, archVariant)
+		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
+		checkSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.so", sharedDir, sharedVariant)
+		checkSnapshot(t, ctx, snapshotSingleton, "librecovery", "librecovery.so", sharedDir, sharedVariant)
+		checkSnapshot(t, ctx, snapshotSingleton, "librecovery_available", "librecovery_available.so", sharedDir, sharedVariant)
+		jsonFiles = append(jsonFiles,
+			filepath.Join(sharedDir, "libvndk.so.json"),
+			filepath.Join(sharedDir, "librecovery.so.json"),
+			filepath.Join(sharedDir, "librecovery_available.so.json"))
+
+		// For static libraries, all recovery:true and recovery_available modules are captured.
+		staticVariant := fmt.Sprintf("android_recovery_%s_%s_static", archType, archVariant)
+		staticDir := filepath.Join(snapshotVariantPath, archDir, "static")
+		checkSnapshot(t, ctx, snapshotSingleton, "libb", "libb.a", staticDir, staticVariant)
+		checkSnapshot(t, ctx, snapshotSingleton, "librecovery", "librecovery.a", staticDir, staticVariant)
+		checkSnapshot(t, ctx, snapshotSingleton, "librecovery_available", "librecovery_available.a", staticDir, staticVariant)
+		jsonFiles = append(jsonFiles,
+			filepath.Join(staticDir, "libb.a.json"),
+			filepath.Join(staticDir, "librecovery.a.json"),
+			filepath.Join(staticDir, "librecovery_available.a.json"))
+
+		// For binary executables, all recovery:true and recovery_available modules are captured.
+		if archType == "arm64" {
+			binaryVariant := fmt.Sprintf("android_recovery_%s_%s", archType, archVariant)
+			binaryDir := filepath.Join(snapshotVariantPath, archDir, "binary")
+			checkSnapshot(t, ctx, snapshotSingleton, "recovery_bin", "recovery_bin", binaryDir, binaryVariant)
+			checkSnapshot(t, ctx, snapshotSingleton, "recovery_available_bin", "recovery_available_bin", binaryDir, binaryVariant)
+			jsonFiles = append(jsonFiles,
+				filepath.Join(binaryDir, "recovery_bin.json"),
+				filepath.Join(binaryDir, "recovery_available_bin.json"))
+		}
+
+		// For header libraries, all vendor:true and vendor_available modules are captured.
+		headerDir := filepath.Join(snapshotVariantPath, archDir, "header")
+		jsonFiles = append(jsonFiles, filepath.Join(headerDir, "librecovery_headers.json"))
+
+		// For object modules, all vendor:true and vendor_available modules are captured.
+		objectVariant := fmt.Sprintf("android_recovery_%s_%s", archType, archVariant)
+		objectDir := filepath.Join(snapshotVariantPath, archDir, "object")
+		checkSnapshot(t, ctx, snapshotSingleton, "obj", "obj.o", objectDir, objectVariant)
+		jsonFiles = append(jsonFiles, filepath.Join(objectDir, "obj.o.json"))
+	}
+
+	for _, jsonFile := range jsonFiles {
+		// verify all json files exist
+		if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
+			t.Errorf("%q expected but not found", jsonFile)
+		}
+	}
+}
+
+func TestRecoverySnapshotExclude(t *testing.T) {
+	// This test verifies that the exclude_from_recovery_snapshot property
+	// makes its way from the Android.bp source file into the module data
+	// structure. It also verifies that modules are correctly included or
+	// excluded in the recovery snapshot based on their path (framework or
+	// vendor) and the exclude_from_recovery_snapshot property.
+
+	frameworkBp := `
+		cc_library_shared {
+			name: "libinclude",
+			srcs: ["src/include.cpp"],
+			recovery_available: true,
+		}
+		cc_library_shared {
+			name: "libexclude",
+			srcs: ["src/exclude.cpp"],
+			recovery: true,
+			exclude_from_recovery_snapshot: true,
+		}
+		cc_library_shared {
+			name: "libavailable_exclude",
+			srcs: ["src/exclude.cpp"],
+			recovery_available: true,
+			exclude_from_recovery_snapshot: true,
+		}
+	`
+
+	vendorProprietaryBp := `
+		cc_library_shared {
+			name: "librecovery",
+			srcs: ["recovery.cpp"],
+			recovery: true,
+		}
+	`
+
+	depsBp := GatherRequiredDepsForTest(android.Android)
+
+	mockFS := map[string][]byte{
+		"deps/Android.bp":       []byte(depsBp),
+		"framework/Android.bp":  []byte(frameworkBp),
+		"framework/include.cpp": nil,
+		"framework/exclude.cpp": nil,
+		"device/Android.bp":     []byte(vendorProprietaryBp),
+		"device/recovery.cpp":   nil,
+	}
+
+	config := TestConfig(buildDir, android.Android, nil, "", mockFS)
+	config.TestProductVariables.RecoverySnapshotVersion = StringPtr("current")
+	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+	ctx := CreateTestContext(config)
+	ctx.Register()
+
+	_, errs := ctx.ParseFileList(".", []string{"deps/Android.bp", "framework/Android.bp", "device/Android.bp"})
+	android.FailIfErrored(t, errs)
+	_, errs = ctx.PrepareBuildActions(config)
+	android.FailIfErrored(t, errs)
+
+	// Test an include and exclude framework module.
+	assertExcludeFromRecoverySnapshotIs(t, ctx, "libinclude", false)
+	assertExcludeFromRecoverySnapshotIs(t, ctx, "libexclude", true)
+	assertExcludeFromRecoverySnapshotIs(t, ctx, "libavailable_exclude", true)
+
+	// A recovery module is excluded, but by its path, not the
+	// exclude_from_recovery_snapshot property.
+	assertExcludeFromRecoverySnapshotIs(t, ctx, "librecovery", false)
+
+	// Verify the content of the recovery snapshot.
+
+	snapshotDir := "recovery-snapshot"
+	snapshotVariantPath := filepath.Join(buildDir, snapshotDir, "arm64")
+	snapshotSingleton := ctx.SingletonForTests("recovery-snapshot")
+
+	var includeJsonFiles []string
+	var excludeJsonFiles []string
+
+	for _, arch := range [][]string{
+		[]string{"arm64", "armv8-a"},
+	} {
+		archType := arch[0]
+		archVariant := arch[1]
+		archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
+
+		sharedVariant := fmt.Sprintf("android_recovery_%s_%s_shared", archType, archVariant)
+		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
+
+		// Included modules
+		checkSnapshot(t, ctx, snapshotSingleton, "libinclude", "libinclude.so", sharedDir, sharedVariant)
+		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libinclude.so.json"))
+
+		// Excluded modules
+		checkSnapshotExclude(t, ctx, snapshotSingleton, "libexclude", "libexclude.so", sharedDir, sharedVariant)
+		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libexclude.so.json"))
+		checkSnapshotExclude(t, ctx, snapshotSingleton, "librecovery", "librecovery.so", sharedDir, sharedVariant)
+		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "librecovery.so.json"))
+		checkSnapshotExclude(t, ctx, snapshotSingleton, "libavailable_exclude", "libavailable_exclude.so", sharedDir, sharedVariant)
+		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libavailable_exclude.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)
+		}
+	}
+}
diff --git a/cc/vndk.go b/cc/vndk.go
index daae1f7..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
 	}
 }
 
@@ -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
@@ -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/main.go b/cmd/soong_build/main.go
index 907bed3..8cd4f97 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.Bp2Build)
+	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 3657ea8..5d61d0c 100644
--- a/cmd/soong_build/queryview.go
+++ b/cmd/soong_build/queryview.go
@@ -20,50 +20,13 @@
 	"io/ioutil"
 	"os"
 	"path/filepath"
-
-	"github.com/google/blueprint"
 )
 
-type queryviewContext struct {
-	bpCtx *blueprint.Context
-}
-
-func (ctx *queryviewContext) ModuleName(module blueprint.Module) string {
-	return ctx.bpCtx.ModuleName(module)
-}
-
-func (ctx *queryviewContext) ModuleDir(module blueprint.Module) string {
-	return ctx.bpCtx.ModuleDir(module)
-}
-
-func (ctx *queryviewContext) ModuleSubDir(module blueprint.Module) string {
-	return ctx.bpCtx.ModuleSubDir(module)
-}
-
-func (ctx *queryviewContext) ModuleType(module blueprint.Module) string {
-	return ctx.bpCtx.ModuleType(module)
-}
-
-func (ctx *queryviewContext) VisitAllModulesBlueprint(visit func(blueprint.Module)) {
-	ctx.bpCtx.VisitAllModules(visit)
-}
-
-func (ctx *queryviewContext) VisitDirectDeps(module android.Module, visit func(android.Module)) {
-	ctx.bpCtx.VisitDirectDeps(module, func(m blueprint.Module) {
-		if aModule, ok := m.(android.Module); ok {
-			visit(aModule)
-		}
-	})
-}
-
 func createBazelQueryView(ctx *android.Context, bazelQueryViewDir string) error {
-	qvCtx := queryviewContext{
-		bpCtx: ctx.Context,
-	}
 	ruleShims := bp2build.CreateRuleShims(android.ModuleTypeFactories())
-	buildToTargets := bp2build.GenerateSoongModuleTargets(&qvCtx)
+	buildToTargets := bp2build.GenerateSoongModuleTargets(*ctx, bp2build.QueryView)
 
-	filesToWrite := bp2build.CreateBazelFiles(ruleShims, buildToTargets)
+	filesToWrite := bp2build.CreateBazelFiles(ruleShims, buildToTargets, bp2build.QueryView)
 	for _, f := range filesToWrite {
 		if err := writeReadOnlyFile(bazelQueryViewDir, f); err != nil {
 			return err
diff --git a/dexpreopt/class_loader_context.go b/dexpreopt/class_loader_context.go
index 532d8fc..bf610ef 100644
--- a/dexpreopt/class_loader_context.go
+++ b/dexpreopt/class_loader_context.go
@@ -488,25 +488,26 @@
 	return clcHost, clcTarget, paths
 }
 
-// JSON representation of <uses-library> paths on host and on device.
-type jsonLibraryPath struct {
-	Host   string
-	Device string
+// Class loader contexts that come from Make via JSON dexpreopt.config. JSON CLC representation is
+// slightly different: it uses a map of library names to their CLC (instead of a list of structs
+// that including the name, as in the Soong CLC representation). The difference is insubstantial, it
+// is caused only by the language differerences between Go and JSON.
+type jsonClassLoaderContext struct {
+	Host        string
+	Device      string
+	Subcontexts map[string]*jsonClassLoaderContext
 }
 
-// Class loader contexts that come from Make (via JSON dexpreopt.config) files have simpler
-// structure than Soong class loader contexts: they are flat maps from a <uses-library> name to its
-// on-host and on-device paths. There are no nested subcontexts. It is a limitation of the current
-// Make implementation.
-type jsonClassLoaderContext map[string]jsonLibraryPath
+// A map of <uses-library> name to its on-host and on-device build paths and CLC.
+type jsonClassLoaderContexts map[string]*jsonClassLoaderContext
 
-// A map from SDK version (represented with a JSON string) to JSON class loader context.
-type jsonClassLoaderContextMap map[string]jsonClassLoaderContext
+// A map from SDK version (represented with a JSON string) to JSON CLCs.
+type jsonClassLoaderContextMap map[string]map[string]*jsonClassLoaderContext
 
-// Convert JSON class loader context map to ClassLoaderContextMap.
+// Convert JSON CLC map to Soong represenation.
 func fromJsonClassLoaderContext(ctx android.PathContext, jClcMap jsonClassLoaderContextMap) ClassLoaderContextMap {
 	clcMap := make(ClassLoaderContextMap)
-	for sdkVerStr, clc := range jClcMap {
+	for sdkVerStr, clcs := range jClcMap {
 		sdkVer, ok := strconv.Atoi(sdkVerStr)
 		if ok != nil {
 			if sdkVerStr == "any" {
@@ -515,14 +516,21 @@
 				android.ReportPathErrorf(ctx, "failed to parse SDK version in dexpreopt.config: '%s'", sdkVerStr)
 			}
 		}
-		for lib, path := range clc {
-			clcMap[sdkVer] = append(clcMap[sdkVer], &ClassLoaderContext{
-				Name:        lib,
-				Host:        constructPath(ctx, path.Host),
-				Device:      path.Device,
-				Subcontexts: nil,
-			})
-		}
+		clcMap[sdkVer] = fromJsonClassLoaderContextRec(ctx, clcs)
 	}
 	return clcMap
 }
+
+// Recursive helper for fromJsonClassLoaderContext.
+func fromJsonClassLoaderContextRec(ctx android.PathContext, jClcs map[string]*jsonClassLoaderContext) []*ClassLoaderContext {
+	clcs := make([]*ClassLoaderContext, 0, len(jClcs))
+	for lib, clc := range jClcs {
+		clcs = append(clcs, &ClassLoaderContext{
+			Name:        lib,
+			Host:        constructPath(ctx, clc.Host),
+			Device:      clc.Device,
+			Subcontexts: fromJsonClassLoaderContextRec(ctx, clc.Subcontexts),
+		})
+	}
+	return clcs
+}
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
index f52ecb4..867ece6 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
 
@@ -79,12 +80,10 @@
 	CpuVariant             map[android.ArchType]string // cpu variant for each architecture
 	InstructionSetFeatures map[android.ArchType]string // instruction set for each architecture
 
-	// Only used for boot image
-	DirtyImageObjects android.OptionalPath // path to a dirty-image-objects file
-	BootImageProfiles android.Paths        // path to a boot-image-profile.txt file
-	BootFlags         string               // extra flags to pass to dex2oat for the boot image
-	Dex2oatImageXmx   string               // max heap size for dex2oat for the boot image
-	Dex2oatImageXms   string               // initial heap size for dex2oat for the boot image
+	BootImageProfiles android.Paths // path to a boot-image-profile.txt file
+	BootFlags         string        // extra flags to pass to dex2oat for the boot image
+	Dex2oatImageXmx   string        // max heap size for dex2oat for the boot image
+	Dex2oatImageXms   string        // initial heap size for dex2oat for the boot image
 }
 
 // GlobalSoongConfig contains the global config that is generated from Soong,
@@ -178,7 +177,6 @@
 
 		// Copies of entries in GlobalConfig that are not constructable without extra parameters.  They will be
 		// used to construct the real value manually below.
-		DirtyImageObjects string
 		BootImageProfiles []string
 	}
 
@@ -189,7 +187,6 @@
 	}
 
 	// Construct paths that require a PathContext.
-	config.GlobalConfig.DirtyImageObjects = android.OptionalPathForPath(constructPath(ctx, config.DirtyImageObjects))
 	config.GlobalConfig.BootImageProfiles = constructPaths(ctx, config.BootImageProfiles)
 
 	return config.GlobalConfig, nil
@@ -234,8 +231,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 +303,7 @@
 	}
 }
 
-var dex2oatDepTag = struct {
+var Dex2oatDepTag = struct {
 	blueprint.BaseDependencyTag
 }{}
 
@@ -316,7 +314,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 +330,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 {
@@ -365,13 +363,6 @@
 // createGlobalSoongConfig creates a GlobalSoongConfig from the current context.
 // Should not be used in dexpreopt_gen.
 func createGlobalSoongConfig(ctx android.ModuleContext) *GlobalSoongConfig {
-	if ctx.Config().TestProductVariables != nil {
-		// If we're called in a test there'll be a confusing error from the path
-		// functions below that gets reported without a stack trace, so let's panic
-		// properly with a more helpful message.
-		panic("This should not be called from tests. Please call GlobalSoongConfigForTests somewhere in the test setup.")
-	}
-
 	return &GlobalSoongConfig{
 		Profman:          ctx.Config().HostToolPath(ctx, "profman"),
 		Dex2oat:          dex2oatPathFromDep(ctx),
@@ -385,14 +376,13 @@
 
 // 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
-// GlobalSoongConfigForTests also needs to be rethought).
+// and then possibly remove this cache altogether.
 var globalSoongConfigOnceKey = android.NewOnceKey("DexpreoptGlobalSoongConfig")
 
 // GetGlobalSoongConfig creates a GlobalSoongConfig the first time it's called,
@@ -545,7 +535,6 @@
 		EmptyDirectory:                     "empty_dir",
 		CpuVariant:                         nil,
 		InstructionSetFeatures:             nil,
-		DirtyImageObjects:                  android.OptionalPath{},
 		BootImageProfiles:                  nil,
 		BootFlags:                          "",
 		Dex2oatImageXmx:                    "",
@@ -553,18 +542,14 @@
 	}
 }
 
-func GlobalSoongConfigForTests(config android.Config) *GlobalSoongConfig {
-	// Install the test GlobalSoongConfig in the Once cache so that later calls to
-	// Get(Cached)GlobalSoongConfig returns it without trying to create a real one.
-	return config.Once(globalSoongConfigOnceKey, func() interface{} {
-		return &GlobalSoongConfig{
-			Profman:          android.PathForTesting("profman"),
-			Dex2oat:          android.PathForTesting("dex2oat"),
-			Aapt:             android.PathForTesting("aapt"),
-			SoongZip:         android.PathForTesting("soong_zip"),
-			Zip2zip:          android.PathForTesting("zip2zip"),
-			ManifestCheck:    android.PathForTesting("manifest_check"),
-			ConstructContext: android.PathForTesting("construct_context"),
-		}
-	}).(*GlobalSoongConfig)
+func globalSoongConfigForTests() *GlobalSoongConfig {
+	return &GlobalSoongConfig{
+		Profman:          android.PathForTesting("profman"),
+		Dex2oat:          android.PathForTesting("dex2oat"),
+		Aapt:             android.PathForTesting("aapt"),
+		SoongZip:         android.PathForTesting("soong_zip"),
+		Zip2zip:          android.PathForTesting("zip2zip"),
+		ManifestCheck:    android.PathForTesting("manifest_check"),
+		ConstructContext: android.PathForTesting("construct_context"),
+	}
 }
diff --git a/dexpreopt/dexpreopt_test.go b/dexpreopt/dexpreopt_test.go
index 59278fd..af73d0c 100644
--- a/dexpreopt/dexpreopt_test.go
+++ b/dexpreopt/dexpreopt_test.go
@@ -61,7 +61,7 @@
 func TestDexPreopt(t *testing.T) {
 	config := android.TestConfig("out", nil, "", nil)
 	ctx := android.BuilderContextForTesting(config)
-	globalSoong := GlobalSoongConfigForTests(config)
+	globalSoong := globalSoongConfigForTests()
 	global := GlobalConfigForTests(ctx)
 	module := testSystemModuleConfig(ctx, "test")
 
@@ -83,7 +83,7 @@
 func TestDexPreoptSystemOther(t *testing.T) {
 	config := android.TestConfig("out", nil, "", nil)
 	ctx := android.BuilderContextForTesting(config)
-	globalSoong := GlobalSoongConfigForTests(config)
+	globalSoong := globalSoongConfigForTests()
 	global := GlobalConfigForTests(ctx)
 	systemModule := testSystemModuleConfig(ctx, "Stest")
 	systemProductModule := testSystemProductModuleConfig(ctx, "SPtest")
@@ -143,7 +143,7 @@
 func TestDexPreoptProfile(t *testing.T) {
 	config := android.TestConfig("out", nil, "", nil)
 	ctx := android.BuilderContextForTesting(config)
-	globalSoong := GlobalSoongConfigForTests(config)
+	globalSoong := globalSoongConfigForTests()
 	global := GlobalConfigForTests(ctx)
 	module := testSystemModuleConfig(ctx, "test")
 
diff --git a/dexpreopt/testing.go b/dexpreopt/testing.go
index b572eb3..bccbfc1 100644
--- a/dexpreopt/testing.go
+++ b/dexpreopt/testing.go
@@ -34,7 +34,7 @@
 	return module
 }
 
-func RegisterToolModulesForTest(ctx *android.TestContext) {
+func RegisterToolModulesForTest(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("dummy_tool_binary", dummyToolBinaryFactory)
 }
 
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index 2b3fbae..5ef4a90 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -20,6 +20,7 @@
 	"android/soong/android"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
 )
 
 func init() {
@@ -30,10 +31,24 @@
 	android.ModuleBase
 	android.PackagingBase
 
+	properties filesystemProperties
+
 	output     android.OutputPath
 	installDir android.InstallPath
 }
 
+type filesystemProperties struct {
+	// When set to true, sign the image with avbtool. Default is false.
+	Use_avb *bool
+
+	// Path to the private key that avbtool will use to sign this filesystem image.
+	// TODO(jiyong): allow apex_key to be specified here
+	Avb_private_key *string `android:"path"`
+
+	// Hash and signing algorithm for avbtool. Default is SHA256_RSA4096.
+	Avb_algorithm *string
+}
+
 // android_filesystem packages a set of modules and their transitive dependencies into a filesystem
 // image. The filesystem images are expected to be mounted in the target device, which means the
 // modules in the filesystem image are built for the target device (i.e. Android, not Linux host).
@@ -41,6 +56,7 @@
 // partitions like system.img. For example, cc_library modules are placed under ./lib[64] directory.
 func filesystemFactory() android.Module {
 	module := &filesystem{}
+	module.AddProperties(&module.properties)
 	android.InitPackageModule(module)
 	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
 	return module
@@ -72,21 +88,12 @@
 		FlagWithArg("-d ", rootDir.String()). // zipsync wipes this. No need to clear.
 		Input(zipFile)
 
-	mkuserimg := ctx.Config().HostToolPath(ctx, "mkuserimg_mke2fs")
-	propFile := android.PathForModuleOut(ctx, "prop").OutputPath
-	// TODO(jiyong): support more filesystem types other than ext4
-	propsText := fmt.Sprintf(`mount_point=system\n`+
-		`fs_type=ext4\n`+
-		`use_dynamic_partition_size=true\n`+
-		`ext_mkuserimg=%s\n`, mkuserimg.String())
-	builder.Command().Text("echo").Flag("-e").Flag(`"` + propsText + `"`).
-		Text(">").Output(propFile).
-		Implicit(mkuserimg)
-
+	propFile, toolDeps := f.buildPropFile(ctx)
 	f.output = android.PathForModuleOut(ctx, f.installFileName()).OutputPath
 	builder.Command().BuiltTool("build_image").
 		Text(rootDir.String()). // input directory
 		Input(propFile).
+		Implicits(toolDeps).
 		Output(f.output).
 		Text(rootDir.String()) // directory where to find fs_config_files|dirs
 
@@ -97,6 +104,56 @@
 	ctx.InstallFile(f.installDir, f.installFileName(), f.output)
 }
 
+func (f *filesystem) buildPropFile(ctx android.ModuleContext) (propFile android.OutputPath, toolDeps android.Paths) {
+	type prop struct {
+		name  string
+		value string
+	}
+
+	var props []prop
+	var deps android.Paths
+	addStr := func(name string, value string) {
+		props = append(props, prop{name, value})
+	}
+	addPath := func(name string, path android.Path) {
+		props = append(props, prop{name, path.String()})
+		deps = append(deps, path)
+	}
+
+	// TODO(jiyong): support more filesystem types other than ext4
+	addStr("fs_type", "ext4")
+	addStr("mount_point", "system")
+	addStr("use_dynamic_partition_size", "true")
+	addPath("ext_mkuserimg", ctx.Config().HostToolPath(ctx, "mkuserimg_mke2fs"))
+	// b/177813163 deps of the host tools have to be added. Remove this.
+	for _, t := range []string{"mke2fs", "e2fsdroid", "tune2fs"} {
+		deps = append(deps, ctx.Config().HostToolPath(ctx, t))
+	}
+
+	if proptools.Bool(f.properties.Use_avb) {
+		addStr("avb_hashtree_enable", "true")
+		addPath("avb_avbtool", ctx.Config().HostToolPath(ctx, "avbtool"))
+		algorithm := proptools.StringDefault(f.properties.Avb_algorithm, "SHA256_RSA4096")
+		addStr("avb_algorithm", algorithm)
+		key := android.PathForModuleSrc(ctx, proptools.String(f.properties.Avb_private_key))
+		addPath("avb_key_path", key)
+		addStr("avb_add_hashtree_footer_args", "--do_not_generate_fec")
+		addStr("partition_name", f.Name())
+	}
+
+	propFile = android.PathForModuleOut(ctx, "prop").OutputPath
+	builder := android.NewRuleBuilder(pctx, ctx)
+	builder.Command().Text("rm").Flag("-rf").Output(propFile)
+	for _, p := range props {
+		builder.Command().
+			Text("echo").Flag("-e").
+			Flag(`"` + p.name + "=" + p.value + `"`).
+			Text(">>").Output(propFile)
+	}
+	builder.Build("build_filesystem_prop", fmt.Sprintf("Creating filesystem props for %s", f.BaseModuleName()))
+	return propFile, deps
+}
+
 var _ android.AndroidMkEntriesProvider = (*filesystem)(nil)
 
 // Implements android.AndroidMkEntriesProvider
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 8df32f2..1f47dec 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -46,6 +46,8 @@
 	ctx.FinalDepsMutators(func(ctx android.RegisterMutatorsContext) {
 		ctx.BottomUp("genrule_tool_deps", toolDepsMutator).Parallel()
 	})
+
+	android.RegisterBp2BuildMutator("genrule", bp2buildMutator)
 }
 
 var (
@@ -772,6 +774,70 @@
 	Out []string `android:"arch_variant"`
 }
 
+type bazelGenruleAttributes struct {
+	Name  *string
+	Srcs  []string
+	Outs  []string
+	Tools []string
+	Cmd   string
+}
+
+type bazelGenrule struct {
+	android.BazelTargetModuleBase
+	bazelGenruleAttributes
+}
+
+func BazelGenruleFactory() android.Module {
+	module := &bazelGenrule{}
+	module.AddProperties(&module.bazelGenruleAttributes)
+	android.InitBazelTargetModule(module)
+	return module
+}
+
+func bp2buildMutator(ctx android.TopDownMutatorContext) {
+	if m, ok := ctx.Module().(*Module); ok {
+		name := "__bp2build__" + m.Name()
+		// Replace in and out variables with $< and $@
+		var cmd string
+		if m.properties.Cmd != nil {
+			cmd = strings.Replace(*m.properties.Cmd, "$(in)", "$(SRCS)", -1)
+			cmd = strings.Replace(cmd, "$(out)", "$(OUTS)", -1)
+			cmd = strings.Replace(cmd, "$(genDir)", "$(GENDIR)", -1)
+		}
+
+		// The Out prop is not in an immediately accessible field
+		// in the Module struct, so use GetProperties and cast it
+		// to the known struct prop.
+		var outs []string
+		for _, propIntf := range m.GetProperties() {
+			if props, ok := propIntf.(*genRuleProperties); ok {
+				outs = props.Out
+				break
+			}
+		}
+
+		// Bazel only has the "tools" attribute.
+		tools := append(m.properties.Tools, m.properties.Tool_files...)
+
+		// Create the BazelTargetModule.
+		ctx.CreateModule(BazelGenruleFactory, &bazelGenruleAttributes{
+			Name:  proptools.StringPtr(name),
+			Srcs:  m.properties.Srcs,
+			Outs:  outs,
+			Cmd:   cmd,
+			Tools: tools,
+		}, &bazel.BazelTargetModuleProperties{
+			Rule_class: "genrule",
+		})
+	}
+}
+
+func (m *bazelGenrule) Name() string {
+	return m.BaseModuleName()
+}
+
+func (m *bazelGenrule) GenerateAndroidBuildActions(ctx android.ModuleContext) {}
+
 var Bool = proptools.Bool
 var String = proptools.String
 
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/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index 062005b..004cbbb 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -25,6 +25,18 @@
 	"github.com/google/blueprint/proptools"
 )
 
+// =================================================================================================
+// WIP - see http://b/177892522 for details
+//
+// The build support for boot images is currently being migrated away from singleton to modules so
+// the documentation may not be strictly accurate. Rather than update the documentation at every
+// step which will create a lot of churn the changes that have been made will be listed here and the
+// documentation will be updated once it is closer to the final result.
+//
+// Changes:
+// 1) dex_bootjars is now a singleton module and not a plain singleton.
+// =================================================================================================
+
 // This comment describes:
 //   1. ART boot images in general (their types, structure, file layout, etc.)
 //   2. build system support for boot images
@@ -124,7 +136,7 @@
 // The primary ART boot image needs to be compiled with one dex2oat invocation that depends on DEX
 // jars for the core libraries. Framework boot image extension needs to be compiled with one dex2oat
 // invocation that depends on the primary ART boot image and all bootclasspath DEX jars except the
-// Core libraries.
+// core libraries as they are already part of the primary ART boot image.
 //
 // 2.1. Libraries that go in the boot images
 // -----------------------------------------
@@ -339,20 +351,24 @@
 	return append(imageLocations, dexpreopt.PathToLocation(image.images, image.target.Arch.ArchType))
 }
 
-func dexpreoptBootJarsFactory() android.Singleton {
-	return &dexpreoptBootJars{}
+func dexpreoptBootJarsFactory() android.SingletonModule {
+	m := &dexpreoptBootJars{}
+	android.InitAndroidModule(m)
+	return m
 }
 
 func RegisterDexpreoptBootJarsComponents(ctx android.RegistrationContext) {
-	ctx.RegisterSingletonType("dex_bootjars", dexpreoptBootJarsFactory)
+	ctx.RegisterSingletonModuleType("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.
+// Singleton module for generating boot image build rules.
 type dexpreoptBootJars struct {
+	android.SingletonModuleBase
+
 	// Default boot image config (currently always the Framework boot image extension). It should be
 	// noted that JIT-Zygote builds use ART APEX image instead of the Framework boot image extension,
 	// but the switch is handled not here, but in the makefiles (triggered with
@@ -371,7 +387,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.
@@ -385,9 +401,16 @@
 	return files
 }
 
+// Provide paths to boot images for use by modules that depend upon them.
+//
+// The build rules are created in GenerateSingletonBuildActions().
+func (d *dexpreoptBootJars) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	// Placeholder for now.
+}
+
 // Generate build rules for boot images.
-func (d *dexpreoptBootJars) GenerateBuildActions(ctx android.SingletonContext) {
-	if skipDexpreoptBootJars(ctx) {
+func (d *dexpreoptBootJars) GenerateSingletonBuildActions(ctx android.SingletonContext) {
+	if SkipDexpreoptBootJars(ctx) {
 		return
 	}
 	if dexpreopt.GetCachedGlobalSoongConfig(ctx) == nil {
@@ -422,16 +445,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)
@@ -622,8 +648,10 @@
 		cmd.FlagWithInput("--profile-file=", profile)
 	}
 
-	if global.DirtyImageObjects.Valid() {
-		cmd.FlagWithInput("--dirty-image-objects=", global.DirtyImageObjects.Path())
+	dirtyImageFile := "frameworks/base/config/dirty-image-objects"
+	dirtyImagePath := android.ExistentPathForSource(ctx, dirtyImageFile)
+	if dirtyImagePath.Valid() {
+		cmd.FlagWithInput("--dirty-image-objects=", dirtyImagePath.Path())
 	}
 
 	if image.extends != nil {
@@ -724,7 +752,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/dexpreopt_bootjars_test.go b/java/dexpreopt_bootjars_test.go
index 95fe5e1..48bc244 100644
--- a/java/dexpreopt_bootjars_test.go
+++ b/java/dexpreopt_bootjars_test.go
@@ -52,7 +52,6 @@
 	dexpreopt.SetTestGlobalConfig(config, dexpreoptConfig)
 
 	ctx := testContext(config)
-	RegisterDexpreoptBootJarsComponents(ctx)
 	run(t, ctx, config)
 
 	dexpreoptBootJars := ctx.SingletonForTests("dex_bootjars")
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 f684a00..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
 	}
 }
 
diff --git a/java/java_test.go b/java/java_test.go
index a2466f9..e7776c3 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -59,35 +59,16 @@
 }
 
 func testConfig(env map[string]string, bp string, fs map[string][]byte) android.Config {
-	bp += dexpreopt.BpToolModulesForTest()
-
-	config := TestConfig(buildDir, env, bp, fs)
-
-	// Set up the global Once cache used for dexpreopt.GlobalSoongConfig, so that
-	// it doesn't create a real one, which would fail.
-	_ = dexpreopt.GlobalSoongConfigForTests(config)
-
-	return config
+	return TestConfig(buildDir, env, bp, fs)
 }
 
 func testContext(config android.Config) *android.TestContext {
 
 	ctx := android.NewTestArchContext(config)
-	RegisterJavaBuildComponents(ctx)
-	RegisterAppBuildComponents(ctx)
-	RegisterAppImportBuildComponents(ctx)
-	RegisterAppSetBuildComponents(ctx)
-	RegisterAARBuildComponents(ctx)
-	RegisterGenRuleBuildComponents(ctx)
-	RegisterRuntimeResourceOverlayBuildComponents(ctx)
-	RegisterSystemModulesBuildComponents(ctx)
+	RegisterRequiredBuildComponentsForTest(ctx)
 	ctx.RegisterModuleType("java_plugin", PluginFactory)
 	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
 	ctx.RegisterModuleType("python_binary_host", python.PythonBinaryHostFactory)
-	RegisterDocsBuildComponents(ctx)
-	RegisterStubsBuildComponents(ctx)
-	RegisterPrebuiltApisBuildComponents(ctx)
-	RegisterSdkLibraryBuildComponents(ctx)
 	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
 	ctx.PreArchMutators(android.RegisterComponentsMutator)
 
@@ -101,8 +82,6 @@
 	// Register module types and mutators from cc needed for JNI testing
 	cc.RegisterRequiredBuildComponentsForTest(ctx)
 
-	dexpreopt.RegisterToolModulesForTest(ctx)
-
 	ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
 		ctx.TopDown("propagate_rro_enforcement", propagateRROEnforcementMutator).Parallel()
 	})
@@ -867,83 +846,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 +897,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/java/prebuilt_apis.go b/java/prebuilt_apis.go
index bcc6cc0..0ffbaaa 100644
--- a/java/prebuilt_apis.go
+++ b/java/prebuilt_apis.go
@@ -15,7 +15,7 @@
 package java
 
 import (
-	"sort"
+	"strconv"
 	"strings"
 
 	"github.com/google/blueprint/proptools"
@@ -173,39 +173,38 @@
 	// construct a map to find out the latest api file path
 	// for each (<module>, <scope>) pair.
 	type latestApiInfo struct {
-		module string
-		scope  string
-		apiver string
-		path   string
+		module  string
+		scope   string
+		version int
+		path    string
 	}
-	m := make(map[string]latestApiInfo)
 
+	// Create filegroups for all (<module>, <scope, <version>) triplets,
+	// and a "latest" filegroup variant for each (<module>, <scope>) pair
+	m := make(map[string]latestApiInfo)
 	for _, f := range files {
-		// create a filegroup for each api txt file
 		localPath := strings.TrimPrefix(f, mydir)
 		module, apiver, scope := parseApiFilePath(mctx, localPath)
 		createFilegroup(mctx, module, scope, apiver, localPath)
 
-		// find the latest apiver
+		version, err := strconv.Atoi(apiver)
+		if err != nil {
+			mctx.ModuleErrorf("Found finalized API files in non-numeric dir %v", apiver)
+			return
+		}
+
 		key := module + "." + scope
 		info, ok := m[key]
 		if !ok {
-			m[key] = latestApiInfo{module, scope, apiver, localPath}
-		} else if len(apiver) > len(info.apiver) || (len(apiver) == len(info.apiver) &&
-			strings.Compare(apiver, info.apiver) > 0) {
-			info.apiver = apiver
+			m[key] = latestApiInfo{module, scope, version, localPath}
+		} else if version > info.version {
+			info.version = version
 			info.path = localPath
 			m[key] = info
 		}
 	}
-	// create filegroups for the latest version of (<module>, <scope>) pairs
-	// sort the keys in order to make build.ninja stable
-	keys := make([]string, 0, len(m))
-	for k := range m {
-		keys = append(keys, k)
-	}
-	sort.Strings(keys)
-	for _, k := range keys {
+	// Sort the keys in order to make build.ninja stable
+	for _, k := range android.SortedStringKeys(m) {
 		info := m[k]
 		createFilegroup(mctx, info.module, info.scope, "latest", info.path)
 	}
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 1a655a6..638740f 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -1048,6 +1048,30 @@
 
 // Add other dependencies as normal.
 func (module *SdkLibrary) DepsMutator(ctx android.BottomUpMutatorContext) {
+	var missingApiModules []string
+	for _, apiScope := range module.getGeneratedApiScopes(ctx) {
+		if apiScope.unstable {
+			continue
+		}
+		if m := android.SrcIsModule(module.latestApiFilegroupName(apiScope)); !ctx.OtherModuleExists(m) {
+			missingApiModules = append(missingApiModules, m)
+		}
+		if m := android.SrcIsModule(module.latestRemovedApiFilegroupName(apiScope)); !ctx.OtherModuleExists(m) {
+			missingApiModules = append(missingApiModules, m)
+		}
+	}
+	if len(missingApiModules) != 0 && !module.sdkLibraryProperties.Unsafe_ignore_missing_latest_api {
+		m := module.Name() + " is missing tracking files for previously released library versions.\n"
+		m += "You need to do one of the following:\n"
+		m += "- Add `unsafe_ignore_missing_latest_api: true` to your blueprint (to disable compat tracking)\n"
+		m += "- Add a set of prebuilt txt files representing the last released version of this library for compat checking.\n"
+		m += "  (the current set of API files can be used as a seed for this compatibility tracking\n"
+		m += "\n"
+		m += "The following filegroup modules are missing:\n  "
+		m += strings.Join(missingApiModules, "\n  ") + "\n"
+		m += "Please see the documentation of the prebuilt_apis module type (and a usage example in prebuilts/sdk) for a convenient way to generate these."
+		ctx.ModuleErrorf(m)
+	}
 	if module.requiresRuntimeImplementationLibrary() {
 		// Only add the deps for the library if it is actually going to be built.
 		module.Library.deps(ctx)
diff --git a/java/testing.go b/java/testing.go
index fc4e477..0b1f2d1 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -86,8 +86,11 @@
 		for _, lib := range sdkLibs {
 			for _, scope := range []string{"public", "system", "module-lib", "system-server", "test"} {
 				fs[fmt.Sprintf("prebuilts/sdk/%s/%s/%s.jar", level, scope, lib)] = nil
-				fs[fmt.Sprintf("prebuilts/sdk/%s/%s/api/%s.txt", level, scope, lib)] = nil
-				fs[fmt.Sprintf("prebuilts/sdk/%s/%s/api/%s-removed.txt", level, scope, lib)] = nil
+				// No finalized API files for "current"
+				if level != "current" {
+					fs[fmt.Sprintf("prebuilts/sdk/%s/%s/api/%s.txt", level, scope, lib)] = nil
+					fs[fmt.Sprintf("prebuilts/sdk/%s/%s/api/%s-removed.txt", level, scope, lib)] = nil
+				}
 			}
 		}
 		fs[fmt.Sprintf("prebuilts/sdk/%s/public/framework.aidl", level)] = nil
@@ -95,6 +98,32 @@
 	return fs
 }
 
+// Register build components provided by this package that are needed by tests.
+//
+// In particular this must register all the components that are used in the `Android.bp` snippet
+// returned by GatherRequiredDepsForTest()
+func RegisterRequiredBuildComponentsForTest(ctx android.RegistrationContext) {
+	RegisterAARBuildComponents(ctx)
+	RegisterAppBuildComponents(ctx)
+	RegisterAppImportBuildComponents(ctx)
+	RegisterAppSetBuildComponents(ctx)
+	RegisterDexpreoptBootJarsComponents(ctx)
+	RegisterDocsBuildComponents(ctx)
+	RegisterGenRuleBuildComponents(ctx)
+	RegisterJavaBuildComponents(ctx)
+	RegisterPrebuiltApisBuildComponents(ctx)
+	RegisterRuntimeResourceOverlayBuildComponents(ctx)
+	RegisterSdkLibraryBuildComponents(ctx)
+	RegisterStubsBuildComponents(ctx)
+	RegisterSystemModulesBuildComponents(ctx)
+
+	// Make sure that any tool related module types needed by dexpreopt have been registered.
+	dexpreopt.RegisterToolModulesForTest(ctx)
+}
+
+// Gather the module definitions needed by tests that depend upon code from this package.
+//
+// Returns an `Android.bp` snippet that defines the modules that are needed by this package.
 func GatherRequiredDepsForTest() string {
 	var bp string
 
@@ -181,6 +210,16 @@
 		`, extra)
 	}
 
+	// Make sure that any tools needed for dexpreopting are defined.
+	bp += dexpreopt.BpToolModulesForTest()
+
+	// Make sure that the dex_bootjars singleton module is instantiated for the tests.
+	bp += `
+		dex_bootjars {
+			name: "dex_bootjars",
+		}
+`
+
 	return bp
 }
 
diff --git a/licenses/Android.bp b/licenses/Android.bp
index 217792f..e87bdcf 100644
--- a/licenses/Android.bp
+++ b/licenses/Android.bp
@@ -14,1069 +14,1216 @@
 // limitations under the License.
 
 package {
-	default_visibility: ["//visibility:public"],
-	default_applicable_licenses: ["Android-Apache-2.0"],
+    default_visibility: ["//visibility:public"],
+    default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
 license {
-	name: "Android-Apache-2.0",
-	license_kinds: ["SPDX-license-identifier-Apache-2.0"],
-	copyright_notice: "Copyright (C) The Android Open Source Project",
-	license_text: ["LICENSE"],
+    name: "Android-Apache-2.0",
+    license_kinds: ["SPDX-license-identifier-Apache-2.0"],
+    copyright_notice: "Copyright (C) The Android Open Source Project",
+    license_text: ["LICENSE"],
 }
 
 license_kind {
-	name: "SPDX-license-identifier-AFL-1.1",
-	conditions: ["by_exception_only"],
-	url: "https://spdx.org/licenses/AFL-1.1.html",
+    name: "SPDX-license-identifier-0BSD",
+    conditions: ["unencumbered"],
+    url: "https://spdx.org/licenses/0BSD",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-AFL-1.2",
-	conditions: ["by_exception_only"],
-	url: "https://spdx.org/licenses/AFL-1.2.html",
+    name: "SPDX-license-identifier-AFL-1.1",
+    conditions: ["by_exception_only"],
+    url: "https://spdx.org/licenses/AFL-1.1.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-AFL-2.0",
-	conditions: ["by_exception_only"],
-	url: "https://spdx.org/licenses/AFL-2.0.html",
+    name: "SPDX-license-identifier-AFL-1.2",
+    conditions: ["by_exception_only"],
+    url: "https://spdx.org/licenses/AFL-1.2.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-AFL-2.1",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/AFL-2.1.html",
+    name: "SPDX-license-identifier-AFL-2.0",
+    conditions: ["by_exception_only"],
+    url: "https://spdx.org/licenses/AFL-2.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-AFL-3.0",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/AFL-3.0.html",
+    name: "SPDX-license-identifier-AFL-2.1",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/AFL-2.1.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-AGPL",
-	conditions: ["by_exception_only", "not_allowed"],
-	url: "https://spdx.org/licenses/AGPL.html",
+    name: "SPDX-license-identifier-AFL-3.0",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/AFL-3.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-AGPL-1.0",
-	conditions: ["by_exception_only", "not_allowed"],
-	url: "https://spdx.org/licenses/AGPL-1.0.html",
+    name: "SPDX-license-identifier-AGPL",
+    conditions: [
+        "by_exception_only",
+        "not_allowed",
+    ],
+    url: "https://spdx.org/licenses/AGPL.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-AGPL-1.0-only",
-	conditions: ["by_exception_only", "not_allowed"],
-	url: "https://spdx.org/licenses/AGPL-1.0-only.html",
+    name: "SPDX-license-identifier-AGPL-1.0",
+    conditions: [
+        "by_exception_only",
+        "not_allowed",
+    ],
+    url: "https://spdx.org/licenses/AGPL-1.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-AGPL-1.0-or-later",
-	conditions: ["by_exception_only", "not_allowed"],
-	url: "https://spdx.org/licenses/AGPL-1.0-or-later.html",
+    name: "SPDX-license-identifier-AGPL-1.0-only",
+    conditions: [
+        "by_exception_only",
+        "not_allowed",
+    ],
+    url: "https://spdx.org/licenses/AGPL-1.0-only.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-AGPL-3.0",
-	conditions: ["by_exception_only", "not_allowed"],
-	url: "https://spdx.org/licenses/AGPL-3.0.html",
+    name: "SPDX-license-identifier-AGPL-1.0-or-later",
+    conditions: [
+        "by_exception_only",
+        "not_allowed",
+    ],
+    url: "https://spdx.org/licenses/AGPL-1.0-or-later.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-AGPL-3.0-only",
-	conditions: ["by_exception_only", "not_allowed"],
-	url: "https://spdx.org/licenses/AGPL-3.0-only.html",
+    name: "SPDX-license-identifier-AGPL-3.0",
+    conditions: [
+        "by_exception_only",
+        "not_allowed",
+    ],
+    url: "https://spdx.org/licenses/AGPL-3.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-AGPL-3.0-or-later",
-	conditions: ["by_exception_only", "not_allowed"],
-	url: "https://spdx.org/licenses/AGPL-3.0-or-later.html",
+    name: "SPDX-license-identifier-AGPL-3.0-only",
+    conditions: [
+        "by_exception_only",
+        "not_allowed",
+    ],
+    url: "https://spdx.org/licenses/AGPL-3.0-only.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-Apache",
-	conditions: ["notice"],
+    name: "SPDX-license-identifier-AGPL-3.0-or-later",
+    conditions: [
+        "by_exception_only",
+        "not_allowed",
+    ],
+    url: "https://spdx.org/licenses/AGPL-3.0-or-later.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-Apache-1.0",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/Apache-1.0.html",
+    name: "SPDX-license-identifier-Apache",
+    conditions: ["notice"],
 }
 
 license_kind {
-	name: "SPDX-license-identifier-Apache-1.1",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/Apache-1.1.html",
+    name: "SPDX-license-identifier-Apache-1.0",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/Apache-1.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-Apache-2.0",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/Apache-2.0.html",
+    name: "SPDX-license-identifier-Apache-1.1",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/Apache-1.1.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-Artistic",
-	conditions: ["notice"],
+    name: "SPDX-license-identifier-Apache-2.0",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/Apache-2.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-Artistic-1.0",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/Artistic-1.0.html",
+    name: "SPDX-license-identifier-Artistic",
+    conditions: ["notice"],
 }
 
 license_kind {
-	name: "SPDX-license-identifier-Artistic-1.0-Perl",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/Artistic-1.0-Perl.html",
+    name: "SPDX-license-identifier-Artistic-1.0",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/Artistic-1.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-Artistic-1.0-cl8",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/Artistic-1.0-cl8.html",
+    name: "SPDX-license-identifier-Artistic-1.0-Perl",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/Artistic-1.0-Perl.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-Artistic-2.0",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/Artistic-2.0.html",
+    name: "SPDX-license-identifier-Artistic-1.0-cl8",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/Artistic-1.0-cl8.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-BSD",
-	conditions: ["notice"],
+    name: "SPDX-license-identifier-Artistic-2.0",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/Artistic-2.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-BSD-1-Clause",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/BSD-1-Clause.html",
+    name: "SPDX-license-identifier-BSD",
+    conditions: ["notice"],
 }
 
 license_kind {
-	name: "SPDX-license-identifier-BSD-2-Clause",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/BSD-2-Clause.html",
+    name: "SPDX-license-identifier-BSD-1-Clause",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/BSD-1-Clause.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-BSD-2-Clause-FreeBSD",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/BSD-2-Clause-FreeBSD.html",
+    name: "SPDX-license-identifier-BSD-2-Clause",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/BSD-2-Clause.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-BSD-2-Clause-NetBSD",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/BSD-2-Clause-NetBSD.html",
+    name: "SPDX-license-identifier-BSD-2-Clause-FreeBSD",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/BSD-2-Clause-FreeBSD.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-BSD-2-Clause-Patent",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/BSD-2-Clause-Patent.html",
+    name: "SPDX-license-identifier-BSD-2-Clause-NetBSD",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/BSD-2-Clause-NetBSD.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-BSD-3-Clause",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/BSD-3-Clause.html",
+    name: "SPDX-license-identifier-BSD-2-Clause-Patent",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/BSD-2-Clause-Patent.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-BSD-3-Clause-Attribution",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/BSD-3-Clause-Attribution.html",
+    name: "SPDX-license-identifier-BSD-3-Clause",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/BSD-3-Clause.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-BSD-3-Clause-Clear",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/BSD-3-Clause-Clear.html",
+    name: "SPDX-license-identifier-BSD-3-Clause-Attribution",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/BSD-3-Clause-Attribution.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-BSD-3-Clause-LBNL",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/BSD-3-Clause-LBNL.html",
+    name: "SPDX-license-identifier-BSD-3-Clause-Clear",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/BSD-3-Clause-Clear.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-BSD-3-Clause-No-Nuclear-License",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/BSD-3-Clause-No-Nuclear-License.html",
+    name: "SPDX-license-identifier-BSD-3-Clause-LBNL",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/BSD-3-Clause-LBNL.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-BSD-3-Clause-No-Nuclear-License-2014",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/BSD-3-Clause-No-Nuclear-License-2014.html",
+    name: "SPDX-license-identifier-BSD-3-Clause-No-Nuclear-License",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/BSD-3-Clause-No-Nuclear-License.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-BSD-3-Clause-No-Nuclear-Warranty",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/BSD-3-Clause-No-Nuclear-Warranty.html",
+    name: "SPDX-license-identifier-BSD-3-Clause-No-Nuclear-License-2014",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/BSD-3-Clause-No-Nuclear-License-2014.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-BSD-3-Clause-Open-MPI",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/BSD-3-Clause-Open-MPI.html",
+    name: "SPDX-license-identifier-BSD-3-Clause-No-Nuclear-Warranty",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/BSD-3-Clause-No-Nuclear-Warranty.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-BSD-4-Clause",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/BSD-4-Clause.html",
+    name: "SPDX-license-identifier-BSD-3-Clause-Open-MPI",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/BSD-3-Clause-Open-MPI.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-BSD-4-Clause-UC",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/BSD-4-Clause-UC.html",
+    name: "SPDX-license-identifier-BSD-4-Clause",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/BSD-4-Clause.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-BSD-Protection",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/BSD-Protection.html",
+    name: "SPDX-license-identifier-BSD-4-Clause-UC",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/BSD-4-Clause-UC.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-BSD-Source-Code",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/BSD-Source-Code.html",
+    name: "SPDX-license-identifier-BSD-Protection",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/BSD-Protection.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-BSL-1.0",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/BSL-1.0.html",
+    name: "SPDX-license-identifier-BSD-Source-Code",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/BSD-Source-Code.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-Beerware",
-	conditions: ["by_exception_only", "not_allowed"],
-	url: "https://spdx.org/licenses/Beerware.html",
+    name: "SPDX-license-identifier-BSL-1.0",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/BSL-1.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-CC-BY",
-	conditions: ["notice"],
+    name: "SPDX-license-identifier-Beerware",
+    conditions: [
+        "by_exception_only",
+        "not_allowed",
+    ],
+    url: "https://spdx.org/licenses/Beerware.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-CC-BY-1.0",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/CC-BY-1.0.html",
+    name: "SPDX-license-identifier-CC-BY",
+    conditions: ["notice"],
 }
 
 license_kind {
-	name: "SPDX-license-identifier-CC-BY-2.0",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/CC-BY-2.0.html",
+    name: "SPDX-license-identifier-CC-BY-1.0",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/CC-BY-1.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-CC-BY-2.5",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/CC-BY-2.5.html",
+    name: "SPDX-license-identifier-CC-BY-2.0",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/CC-BY-2.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-CC-BY-3.0",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/CC-BY-3.0.html",
+    name: "SPDX-license-identifier-CC-BY-2.5",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/CC-BY-2.5.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-CC-BY-4.0",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/CC-BY-4.0.html",
+    name: "SPDX-license-identifier-CC-BY-3.0",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/CC-BY-3.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-CC-BY-NC",
-	conditions: ["by_exception_only", "not_allowed"],
+    name: "SPDX-license-identifier-CC-BY-4.0",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/CC-BY-4.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-CC-BY-NC-1.0",
-	conditions: ["by_exception_only", "not_allowed"],
-	url: "https://spdx.org/licenses/CC-BY-NC-1.0.html",
+    name: "SPDX-license-identifier-CC-BY-NC",
+    conditions: [
+        "by_exception_only",
+        "not_allowed",
+    ],
 }
 
 license_kind {
-	name: "SPDX-license-identifier-CC-BY-NC-2.0",
-	conditions: ["by_exception_only", "not_allowed"],
-	url: "https://spdx.org/licenses/CC-BY-NC-2.0.html",
+    name: "SPDX-license-identifier-CC-BY-NC-1.0",
+    conditions: [
+        "by_exception_only",
+        "not_allowed",
+    ],
+    url: "https://spdx.org/licenses/CC-BY-NC-1.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-CC-BY-NC-2.5",
-	conditions: ["by_exception_only", "not_allowed"],
-	url: "https://spdx.org/licenses/CC-BY-NC-2.5.html",
+    name: "SPDX-license-identifier-CC-BY-NC-2.0",
+    conditions: [
+        "by_exception_only",
+        "not_allowed",
+    ],
+    url: "https://spdx.org/licenses/CC-BY-NC-2.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-CC-BY-NC-3.0",
-	conditions: ["by_exception_only", "not_allowed"],
-	url: "https://spdx.org/licenses/CC-BY-NC-3.0.html",
+    name: "SPDX-license-identifier-CC-BY-NC-2.5",
+    conditions: [
+        "by_exception_only",
+        "not_allowed",
+    ],
+    url: "https://spdx.org/licenses/CC-BY-NC-2.5.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-CC-BY-NC-4.0",
-	conditions: ["by_exception_only", "not_allowed"],
-	url: "https://spdx.org/licenses/CC-BY-NC-4.0.html",
+    name: "SPDX-license-identifier-CC-BY-NC-3.0",
+    conditions: [
+        "by_exception_only",
+        "not_allowed",
+    ],
+    url: "https://spdx.org/licenses/CC-BY-NC-3.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-CC-BY-NC-ND-1.0",
-	conditions: ["by_exception_only", "not_allowed"],
-	url: "https://spdx.org/licenses/CC-BY-NC-ND-1.0.html",
+    name: "SPDX-license-identifier-CC-BY-NC-4.0",
+    conditions: [
+        "by_exception_only",
+        "not_allowed",
+    ],
+    url: "https://spdx.org/licenses/CC-BY-NC-4.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-CC-BY-NC-ND-2.0",
-	conditions: ["by_exception_only", "not_allowed"],
-	url: "https://spdx.org/licenses/CC-BY-NC-ND-2.0.html",
+    name: "SPDX-license-identifier-CC-BY-NC-ND-1.0",
+    conditions: [
+        "by_exception_only",
+        "not_allowed",
+    ],
+    url: "https://spdx.org/licenses/CC-BY-NC-ND-1.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-CC-BY-NC-ND-2.5",
-	conditions: ["by_exception_only", "not_allowed"],
-	url: "https://spdx.org/licenses/CC-BY-NC-ND-2.5.html",
+    name: "SPDX-license-identifier-CC-BY-NC-ND-2.0",
+    conditions: [
+        "by_exception_only",
+        "not_allowed",
+    ],
+    url: "https://spdx.org/licenses/CC-BY-NC-ND-2.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-CC-BY-NC-ND-3.0",
-	conditions: ["by_exception_only", "not_allowed"],
-	url: "https://spdx.org/licenses/CC-BY-NC-ND-3.0.html",
+    name: "SPDX-license-identifier-CC-BY-NC-ND-2.5",
+    conditions: [
+        "by_exception_only",
+        "not_allowed",
+    ],
+    url: "https://spdx.org/licenses/CC-BY-NC-ND-2.5.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-CC-BY-NC-ND-4.0",
-	conditions: ["by_exception_only", "not_allowed"],
-	url: "https://spdx.org/licenses/CC-BY-NC-ND-4.0.html",
+    name: "SPDX-license-identifier-CC-BY-NC-ND-3.0",
+    conditions: [
+        "by_exception_only",
+        "not_allowed",
+    ],
+    url: "https://spdx.org/licenses/CC-BY-NC-ND-3.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-CC-BY-NC-SA-1.0",
-	conditions: ["by_exception_only", "not_allowed"],
-	url: "https://spdx.org/licenses/CC-BY-NC-SA-1.0.html",
+    name: "SPDX-license-identifier-CC-BY-NC-ND-4.0",
+    conditions: [
+        "by_exception_only",
+        "not_allowed",
+    ],
+    url: "https://spdx.org/licenses/CC-BY-NC-ND-4.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-CC-BY-NC-SA-2.0",
-	conditions: ["by_exception_only", "not_allowed"],
-	url: "https://spdx.org/licenses/CC-BY-NC-SA-2.0.html",
+    name: "SPDX-license-identifier-CC-BY-NC-SA-1.0",
+    conditions: [
+        "by_exception_only",
+        "not_allowed",
+    ],
+    url: "https://spdx.org/licenses/CC-BY-NC-SA-1.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-CC-BY-NC-SA-2.5",
-	conditions: ["by_exception_only", "not_allowed"],
-	url: "https://spdx.org/licenses/CC-BY-NC-SA-2.5.html",
+    name: "SPDX-license-identifier-CC-BY-NC-SA-2.0",
+    conditions: [
+        "by_exception_only",
+        "not_allowed",
+    ],
+    url: "https://spdx.org/licenses/CC-BY-NC-SA-2.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-CC-BY-NC-SA-3.0",
-	conditions: ["by_exception_only", "not_allowed"],
-	url: "https://spdx.org/licenses/CC-BY-NC-SA-3.0.html",
+    name: "SPDX-license-identifier-CC-BY-NC-SA-2.5",
+    conditions: [
+        "by_exception_only",
+        "not_allowed",
+    ],
+    url: "https://spdx.org/licenses/CC-BY-NC-SA-2.5.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-CC-BY-NC-SA-4.0",
-	conditions: ["by_exception_only", "not_allowed"],
-	url: "https://spdx.org/licenses/CC-BY-NC-SA-4.0.html",
+    name: "SPDX-license-identifier-CC-BY-NC-SA-3.0",
+    conditions: [
+        "by_exception_only",
+        "not_allowed",
+    ],
+    url: "https://spdx.org/licenses/CC-BY-NC-SA-3.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-CC-BY-ND",
-	conditions: ["restricted"],
+    name: "SPDX-license-identifier-CC-BY-NC-SA-4.0",
+    conditions: [
+        "by_exception_only",
+        "not_allowed",
+    ],
+    url: "https://spdx.org/licenses/CC-BY-NC-SA-4.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-CC-BY-ND-1.0",
-	conditions: ["restricted"],
-	url: "https://spdx.org/licenses/CC-BY-ND-1.0.html",
+    name: "SPDX-license-identifier-CC-BY-ND",
+    conditions: ["restricted"],
 }
 
 license_kind {
-	name: "SPDX-license-identifier-CC-BY-ND-2.0",
-	conditions: ["restricted"],
-	url: "https://spdx.org/licenses/CC-BY-ND-2.0.html",
+    name: "SPDX-license-identifier-CC-BY-ND-1.0",
+    conditions: ["restricted"],
+    url: "https://spdx.org/licenses/CC-BY-ND-1.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-CC-BY-ND-2.5",
-	conditions: ["restricted"],
-	url: "https://spdx.org/licenses/CC-BY-ND-2.5.html",
+    name: "SPDX-license-identifier-CC-BY-ND-2.0",
+    conditions: ["restricted"],
+    url: "https://spdx.org/licenses/CC-BY-ND-2.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-CC-BY-ND-3.0",
-	conditions: ["restricted"],
-	url: "https://spdx.org/licenses/CC-BY-ND-3.0.html",
+    name: "SPDX-license-identifier-CC-BY-ND-2.5",
+    conditions: ["restricted"],
+    url: "https://spdx.org/licenses/CC-BY-ND-2.5.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-CC-BY-ND-4.0",
-	conditions: ["restricted"],
-	url: "https://spdx.org/licenses/CC-BY-ND-4.0.html",
+    name: "SPDX-license-identifier-CC-BY-ND-3.0",
+    conditions: ["restricted"],
+    url: "https://spdx.org/licenses/CC-BY-ND-3.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-CC-BY-SA",
-	conditions: ["restricted"],
+    name: "SPDX-license-identifier-CC-BY-ND-4.0",
+    conditions: ["restricted"],
+    url: "https://spdx.org/licenses/CC-BY-ND-4.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-CC-BY-SA-1.0",
-	conditions: ["restricted"],
-	url: "https://spdx.org/licenses/CC-BY-SA-1.0.html",
+    name: "SPDX-license-identifier-CC-BY-SA",
+    conditions: ["restricted"],
 }
 
 license_kind {
-	name: "SPDX-license-identifier-CC-BY-SA-2.0",
-	conditions: ["restricted"],
-	url: "https://spdx.org/licenses/CC-BY-SA-2.0.html",
+    name: "SPDX-license-identifier-CC-BY-SA-1.0",
+    conditions: ["restricted"],
+    url: "https://spdx.org/licenses/CC-BY-SA-1.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-CC-BY-SA-2.5",
-	conditions: ["restricted"],
-	url: "https://spdx.org/licenses/CC-BY-SA-2.5.html",
+    name: "SPDX-license-identifier-CC-BY-SA-2.0",
+    conditions: ["restricted"],
+    url: "https://spdx.org/licenses/CC-BY-SA-2.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-CC-BY-SA-3.0",
-	conditions: ["restricted"],
-	url: "https://spdx.org/licenses/CC-BY-SA-3.0.html",
+    name: "SPDX-license-identifier-CC-BY-SA-2.5",
+    conditions: ["restricted"],
+    url: "https://spdx.org/licenses/CC-BY-SA-2.5.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-CC-BY-SA-4.0",
-	conditions: ["restricted"],
-	url: "https://spdx.org/licenses/CC-BY-SA-4.0.html",
+    name: "SPDX-license-identifier-CC-BY-SA-3.0",
+    conditions: ["restricted"],
+    url: "https://spdx.org/licenses/CC-BY-SA-3.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-CC-BY-SA-ND",
-	conditions: ["restricted"],
+    name: "SPDX-license-identifier-CC-BY-SA-4.0",
+    conditions: ["restricted"],
+    url: "https://spdx.org/licenses/CC-BY-SA-4.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-CC0-1.0",
-	conditions: ["unencumbered"],
-	url: "https://spdx.org/licenses/CC0-1.0.html",
+    name: "SPDX-license-identifier-CC-BY-SA-ND",
+    conditions: ["restricted"],
 }
 
 license_kind {
-	name: "SPDX-license-identifier-CPAL-1.0",
-	conditions: ["by_exception_only", "not_allowed"],
-	url: "https://spdx.org/licenses/CPAL-1.0.html",
+    name: "SPDX-license-identifier-CC0-1.0",
+    conditions: ["unencumbered"],
+    url: "https://spdx.org/licenses/CC0-1.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-CPL-1.0",
-	conditions: ["reciprocal"],
-	url: "https://spdx.org/licenses/CPL-1.0.html",
+    name: "SPDX-license-identifier-CPAL-1.0",
+    conditions: [
+        "by_exception_only",
+        "not_allowed",
+    ],
+    url: "https://spdx.org/licenses/CPAL-1.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-EPL",
-	conditions: ["reciprocal"],
+    name: "SPDX-license-identifier-CPL-1.0",
+    conditions: ["reciprocal"],
+    url: "https://spdx.org/licenses/CPL-1.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-EPL-1.0",
-	conditions: ["reciprocal"],
-	url: "https://spdx.org/licenses/EPL-1.0.html",
+    name: "SPDX-license-identifier-EPL",
+    conditions: ["reciprocal"],
+}
+
+license_kind {
+    name: "SPDX-license-identifier-EPL-1.0",
+    conditions: ["reciprocal"],
+    url: "https://spdx.org/licenses/EPL-1.0.html",
+}
+
+license_kind {
+    name: "SPDX-license-identifier-EPL-2.0",
+    conditions: ["reciprocal"],
+    url: "https://spdx.org/licenses/EPL-2.0.html",
+}
+
+license_kind {
+    name: "SPDX-license-identifier-EUPL",
+    conditions: [
+        "by_exception_only",
+        "not_allowed",
+    ],
 }
 
 license_kind {
-	name: "SPDX-license-identifier-EPL-2.0",
-	conditions: ["reciprocal"],
-	url: "https://spdx.org/licenses/EPL-2.0.html",
+    name: "SPDX-license-identifier-EUPL-1.0",
+    conditions: [
+        "by_exception_only",
+        "not_allowed",
+    ],
+    url: "https://spdx.org/licenses/EUPL-1.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-EUPL",
-	conditions: ["by_exception_only", "not_allowed"],
+    name: "SPDX-license-identifier-EUPL-1.1",
+    conditions: [
+        "by_exception_only",
+        "not_allowed",
+    ],
+    url: "https://spdx.org/licenses/EUPL-1.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-EUPL-1.0",
-	conditions: ["by_exception_only", "not_allowed"],
-	url: "https://spdx.org/licenses/EUPL-1.0.html",
+    name: "SPDX-license-identifier-EUPL-1.2",
+    conditions: [
+        "by_exception_only",
+        "not_allowed",
+    ],
+    url: "https://spdx.org/licenses/EUPL-1.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-EUPL-1.1",
-	conditions: ["by_exception_only", "not_allowed"],
-	url: "https://spdx.org/licenses/EUPL-1.0.html",
+    name: "SPDX-license-identifier-FSFAP",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/FSFAP",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-EUPL-1.2",
-	conditions: ["by_exception_only", "not_allowed"],
-	url: "https://spdx.org/licenses/EUPL-1.0.html",
+    name: "SPDX-license-identifier-FTL",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/FTL.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-FTL",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/FTL.html",
+    name: "SPDX-license-identifier-GFDL",
+    conditions: ["by_exception_only"],
 }
 
 license_kind {
-	name: "SPDX-license-identifier-GPL",
-	conditions: ["restricted"],
+    name: "SPDX-license-identifier-GPL",
+    conditions: ["restricted"],
 }
 
 license_kind {
-	name: "SPDX-license-identifier-GPL-1.0",
-	conditions: ["restricted"],
-	url: "https://spdx.org/licenses/GPL-1.0.html",
+    name: "SPDX-license-identifier-GPL-1.0",
+    conditions: ["restricted"],
+    url: "https://spdx.org/licenses/GPL-1.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-GPL-1.0+",
-	conditions: ["restricted"],
-	url: "https://spdx.org/licenses/GPL-1.0+.html",
+    name: "SPDX-license-identifier-GPL-1.0+",
+    conditions: ["restricted"],
+    url: "https://spdx.org/licenses/GPL-1.0+.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-GPL-1.0-only",
-	conditions: ["restricted"],
-	url: "https://spdx.org/licenses/GPL-1.0-only.html",
+    name: "SPDX-license-identifier-GPL-1.0-only",
+    conditions: ["restricted"],
+    url: "https://spdx.org/licenses/GPL-1.0-only.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-GPL-1.0-or-later",
-	conditions: ["restricted"],
-	url: "https://spdx.org/licenses/GPL-1.0-or-later.html",
+    name: "SPDX-license-identifier-GPL-1.0-or-later",
+    conditions: ["restricted"],
+    url: "https://spdx.org/licenses/GPL-1.0-or-later.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-GPL-2.0",
-	conditions: ["restricted"],
-	url: "https://spdx.org/licenses/GPL-2.0.html",
+    name: "SPDX-license-identifier-GPL-2.0",
+    conditions: ["restricted"],
+    url: "https://spdx.org/licenses/GPL-2.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-GPL-2.0+",
-	conditions: ["restricted"],
-	url: "https://spdx.org/licenses/GPL-2.0+.html",
+    name: "SPDX-license-identifier-GPL-2.0+",
+    conditions: ["restricted"],
+    url: "https://spdx.org/licenses/GPL-2.0+.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-GPL-2.0-only",
-	conditions: ["restricted"],
-	url: "https://spdx.org/licenses/GPL-2.0-only.html",
+    name: "SPDX-license-identifier-GPL-2.0-only",
+    conditions: ["restricted"],
+    url: "https://spdx.org/licenses/GPL-2.0-only.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-GPL-2.0-or-later",
-	conditions: ["restricted"],
-	url: "https://spdx.org/licenses/GPL-2.0-or-later.html",
+    name: "SPDX-license-identifier-GPL-2.0-or-later",
+    conditions: ["restricted"],
+    url: "https://spdx.org/licenses/GPL-2.0-or-later.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-GPL-2.0-with-GCC-exception",
-	conditions: ["restricted"],
-	url: "https://spdx.org/licenses/GPL-2.0-with-GCC-exception.html",
+    name: "SPDX-license-identifier-GPL-2.0-with-GCC-exception",
+    conditions: ["restricted"],
+    url: "https://spdx.org/licenses/GPL-2.0-with-GCC-exception.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-GPL-2.0-with-autoconf-exception",
-	conditions: ["restricted"],
-	url: "https://spdx.org/licenses/GPL-2.0-with-autoconf-exception.html",
+    name: "SPDX-license-identifier-GPL-2.0-with-autoconf-exception",
+    conditions: ["restricted"],
+    url: "https://spdx.org/licenses/GPL-2.0-with-autoconf-exception.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-GPL-2.0-with-bison-exception",
-	conditions: ["restricted"],
-	url: "https://spdx.org/licenses/GPL-2.0-with-bison-exception.html",
+    name: "SPDX-license-identifier-GPL-2.0-with-bison-exception",
+    conditions: ["restricted"],
+    url: "https://spdx.org/licenses/GPL-2.0-with-bison-exception.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-GPL-2.0-with-classpath-exception",
-	conditions: ["restricted"],
-	url: "https://spdx.org/licenses/GPL-2.0-with-classpath-exception.html",
+    name: "SPDX-license-identifier-GPL-2.0-with-classpath-exception",
+    conditions: ["restricted"],
+    url: "https://spdx.org/licenses/GPL-2.0-with-classpath-exception.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-GPL-2.0-with-font-exception",
-	conditions: ["restricted"],
-	url: "https://spdx.org/licenses/GPL-2.0-with-font-exception.html",
+    name: "SPDX-license-identifier-GPL-2.0-with-font-exception",
+    conditions: ["restricted"],
+    url: "https://spdx.org/licenses/GPL-2.0-with-font-exception.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-GPL-3.0",
-	conditions: ["restricted"],
-	url: "https://spdx.org/licenses/GPL-3.0.html",
+    name: "SPDX-license-identifier-GPL-3.0",
+    conditions: ["restricted"],
+    url: "https://spdx.org/licenses/GPL-3.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-GPL-3.0+",
-	conditions: ["restricted"],
-	url: "https://spdx.org/licenses/GPL-3.0+.html",
+    name: "SPDX-license-identifier-GPL-3.0+",
+    conditions: ["restricted"],
+    url: "https://spdx.org/licenses/GPL-3.0+.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-GPL-3.0-only",
-	conditions: ["restricted"],
-	url: "https://spdx.org/licenses/GPL-3.0-only.html",
+    name: "SPDX-license-identifier-GPL-3.0-only",
+    conditions: ["restricted"],
+    url: "https://spdx.org/licenses/GPL-3.0-only.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-GPL-3.0-or-later",
-	conditions: ["restricted"],
-	url: "https://spdx.org/licenses/GPL-3.0-or-later.html",
+    name: "SPDX-license-identifier-GPL-3.0-or-later",
+    conditions: ["restricted"],
+    url: "https://spdx.org/licenses/GPL-3.0-or-later.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-GPL-3.0-with-GCC-exception",
-	conditions: ["restricted"],
-	url: "https://spdx.org/licenses/GPL-3.0-with-GCC-exception.html",
+    name: "SPDX-license-identifier-GPL-3.0-with-GCC-exception",
+    conditions: ["restricted"],
+    url: "https://spdx.org/licenses/GPL-3.0-with-GCC-exception.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-GPL-3.0-with-autoconf-exception",
-	conditions: ["restricted"],
-	url: "https://spdx.org/licenses/GPL-3.0-with-autoconf-exception.html",
+    name: "SPDX-license-identifier-GPL-3.0-with-autoconf-exception",
+    conditions: ["restricted"],
+    url: "https://spdx.org/licenses/GPL-3.0-with-autoconf-exception.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-GPL-with-classpath-exception",
-	conditions: ["restricted"],
+    name: "SPDX-license-identifier-GPL-with-classpath-exception",
+    conditions: ["restricted"],
 }
 
 license_kind {
-	name: "SPDX-license-identifier-HPND",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/HPND.html",
+    name: "SPDX-license-identifier-HPND",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/HPND.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-ICU",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/ICU.html",
+    name: "SPDX-license-identifier-ICU",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/ICU.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-ISC",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/ISC.html",
+    name: "SPDX-license-identifier-ISC",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/ISC.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-JSON",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/JSON.html",
+    name: "SPDX-license-identifier-JSON",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/JSON.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-LGPL",
-	conditions: ["restricted"],
+    name: "SPDX-license-identifier-LGPL",
+    conditions: ["restricted"],
 }
 
 license_kind {
-	name: "SPDX-license-identifier-LGPL-2.0",
-	conditions: ["restricted"],
-	url: "https://spdx.org/licenses/LGPL-2.0.html",
+    name: "SPDX-license-identifier-LGPL-2.0",
+    conditions: ["restricted"],
+    url: "https://spdx.org/licenses/LGPL-2.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-LGPL-2.0+",
-	conditions: ["restricted"],
-	url: "https://spdx.org/licenses/LGPL-2.0+.html",
+    name: "SPDX-license-identifier-LGPL-2.0+",
+    conditions: ["restricted"],
+    url: "https://spdx.org/licenses/LGPL-2.0+.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-LGPL-2.0-only",
-	conditions: ["restricted"],
-	url: "https://spdx.org/licenses/LGPL-2.0-only.html",
+    name: "SPDX-license-identifier-LGPL-2.0-only",
+    conditions: ["restricted"],
+    url: "https://spdx.org/licenses/LGPL-2.0-only.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-LGPL-2.0-or-later",
-	conditions: ["restricted"],
-	url: "https://spdx.org/licenses/LGPL-2.0-or-later.html",
+    name: "SPDX-license-identifier-LGPL-2.0-or-later",
+    conditions: ["restricted"],
+    url: "https://spdx.org/licenses/LGPL-2.0-or-later.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-LGPL-2.1",
-	conditions: ["restricted"],
-	url: "https://spdx.org/licenses/LGPL-2.1.html",
+    name: "SPDX-license-identifier-LGPL-2.1",
+    conditions: ["restricted"],
+    url: "https://spdx.org/licenses/LGPL-2.1.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-LGPL-2.1+",
-	conditions: ["restricted"],
-	url: "https://spdx.org/licenses/LGPL-2.1+.html",
+    name: "SPDX-license-identifier-LGPL-2.1+",
+    conditions: ["restricted"],
+    url: "https://spdx.org/licenses/LGPL-2.1+.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-LGPL-2.1-only",
-	conditions: ["restricted"],
-	url: "https://spdx.org/licenses/LGPL-2.1-only.html",
+    name: "SPDX-license-identifier-LGPL-2.1-only",
+    conditions: ["restricted"],
+    url: "https://spdx.org/licenses/LGPL-2.1-only.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-LGPL-2.1-or-later",
-	conditions: ["restricted"],
-	url: "https://spdx.org/licenses/LGPL-2.1-or-later.html",
+    name: "SPDX-license-identifier-LGPL-2.1-or-later",
+    conditions: ["restricted"],
+    url: "https://spdx.org/licenses/LGPL-2.1-or-later.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-LGPL-3.0",
-	conditions: ["restricted"],
-	url: "https://spdx.org/licenses/LGPL-3.0.html",
+    name: "SPDX-license-identifier-LGPL-3.0",
+    conditions: ["restricted"],
+    url: "https://spdx.org/licenses/LGPL-3.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-LGPL-3.0+",
-	conditions: ["restricted"],
-	url: "https://spdx.org/licenses/LGPL-3.0+.html",
+    name: "SPDX-license-identifier-LGPL-3.0+",
+    conditions: ["restricted"],
+    url: "https://spdx.org/licenses/LGPL-3.0+.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-LGPL-3.0-only",
-	conditions: ["restricted"],
-	url: "https://spdx.org/licenses/LGPL-3.0-only.html",
+    name: "SPDX-license-identifier-LGPL-3.0-only",
+    conditions: ["restricted"],
+    url: "https://spdx.org/licenses/LGPL-3.0-only.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-LGPL-3.0-or-later",
-	conditions: ["restricted"],
-	url: "https://spdx.org/licenses/LGPL-3.0-or-later.html",
+    name: "SPDX-license-identifier-LGPL-3.0-or-later",
+    conditions: ["restricted"],
+    url: "https://spdx.org/licenses/LGPL-3.0-or-later.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-LGPLLR",
-	conditions: ["restricted"],
-	url: "https://spdx.org/licenses/LGPLLR.html",
+    name: "SPDX-license-identifier-LGPLLR",
+    conditions: ["restricted"],
+    url: "https://spdx.org/licenses/LGPLLR.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-LPL-1.02",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/LPL-1.02.html",
+    name: "SPDX-license-identifier-LPL-1.02",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/LPL-1.02.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-MIT",
-	conditions: ["notice"],
+    name: "SPDX-license-identifier-MIT",
+    conditions: ["notice"],
 }
 
 license_kind {
-	name: "SPDX-license-identifier-MIT-0",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/MIT-0.html",
+    name: "SPDX-license-identifier-MIT-0",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/MIT-0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-MIT-CMU",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/MIT-CMU.html",
+    name: "SPDX-license-identifier-MIT-CMU",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/MIT-CMU.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-MIT-advertising",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/MIT-advertising.html",
+    name: "SPDX-license-identifier-MIT-advertising",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/MIT-advertising.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-MIT-enna",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/MIT-enna.html",
+    name: "SPDX-license-identifier-MIT-enna",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/MIT-enna.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-MIT-feh",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/MIT-feh.html",
+    name: "SPDX-license-identifier-MIT-feh",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/MIT-feh.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-MITNFA",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/MITNFA.html",
+    name: "SPDX-license-identifier-MITNFA",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/MITNFA.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-MPL",
-	conditions: ["reciprocal"],
+    name: "SPDX-license-identifier-MPL",
+    conditions: ["reciprocal"],
 }
 
 license_kind {
-	name: "SPDX-license-identifier-MPL-1.0",
-	conditions: ["reciprocal"],
-	url: "https://spdx.org/licenses/MPL-1.0.html",
+    name: "SPDX-license-identifier-MPL-1.0",
+    conditions: ["reciprocal"],
+    url: "https://spdx.org/licenses/MPL-1.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-MPL-1.1",
-	conditions: ["reciprocal"],
-	url: "https://spdx.org/licenses/MPL-1.1.html",
+    name: "SPDX-license-identifier-MPL-1.1",
+    conditions: ["reciprocal"],
+    url: "https://spdx.org/licenses/MPL-1.1.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-MPL-2.0",
-	conditions: ["reciprocal"],
-	url: "https://spdx.org/licenses/MPL-2.0.html",
+    name: "SPDX-license-identifier-MPL-2.0",
+    conditions: ["reciprocal"],
+    url: "https://spdx.org/licenses/MPL-2.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-MPL-2.0-no-copyleft-exception",
-	conditions: ["reciprocal"],
-	url: "https://spdx.org/licenses/MPL-2.0-no-copyleft-exception.html",
+    name: "SPDX-license-identifier-MPL-2.0-no-copyleft-exception",
+    conditions: ["reciprocal"],
+    url: "https://spdx.org/licenses/MPL-2.0-no-copyleft-exception.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-MS-PL",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/MS-PL.html",
+    name: "SPDX-license-identifier-MS-PL",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/MS-PL.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-NCSA",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/NCSA.html",
+    name: "SPDX-license-identifier-MS-RL",
+    conditions: ["by_exception_only"],
+    url: "https://spdx.org/licenses/MS-RL.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-OFL",
-	conditions: ["by_exception_only"],
+    name: "SPDX-license-identifier-NCSA",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/NCSA.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-OFL-1.0",
-	conditions: ["by_exception_only"],
-	url: "https://spdx.org/licenses/OFL-1.0.html",
+    name: "SPDX-license-identifier-OFL",
+    conditions: ["by_exception_only"],
 }
 
 license_kind {
-	name: "SPDX-license-identifier-OFL-1.0-RFN",
-	conditions: ["by_exception_only"],
-	url: "https://spdx.org/licenses/OFL-1.0-RFN.html",
+    name: "SPDX-license-identifier-OFL-1.0",
+    conditions: ["by_exception_only"],
+    url: "https://spdx.org/licenses/OFL-1.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-OFL-1.0-no-RFN",
-	conditions: ["by_exception_only"],
-	url: "https://spdx.org/licenses/OFL-1.0-no-RFN.html",
+    name: "SPDX-license-identifier-OFL-1.0-RFN",
+    conditions: ["by_exception_only"],
+    url: "https://spdx.org/licenses/OFL-1.0-RFN.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-OFL-1.1",
-	conditions: ["by_exception_only"],
-	url: "https://spdx.org/licenses/OFL-1.1.html",
+    name: "SPDX-license-identifier-OFL-1.0-no-RFN",
+    conditions: ["by_exception_only"],
+    url: "https://spdx.org/licenses/OFL-1.0-no-RFN.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-OFL-1.1-RFN",
-	conditions: ["by_exception_only"],
-	url: "https://spdx.org/licenses/OFL-1.1-RFN.html",
+    name: "SPDX-license-identifier-OFL-1.1",
+    conditions: ["by_exception_only"],
+    url: "https://spdx.org/licenses/OFL-1.1.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-OFL-1.1-no-RFN",
-	conditions: ["by_exception_only"],
-	url: "https://spdx.org/licenses/OFL-1.1-no-RFN.html",
+    name: "SPDX-license-identifier-OFL-1.1-RFN",
+    conditions: ["by_exception_only"],
+    url: "https://spdx.org/licenses/OFL-1.1-RFN.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-OpenSSL",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/OpenSSL.html",
+    name: "SPDX-license-identifier-OFL-1.1-no-RFN",
+    conditions: ["by_exception_only"],
+    url: "https://spdx.org/licenses/OFL-1.1-no-RFN.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-PSF-2.0",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/PSF-2.0.html",
+    name: "SPDX-license-identifier-OpenSSL",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/OpenSSL.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-SISSL",
-	conditions: ["by_exception_only", "not_allowed"],
-	url: "https://spdx.org/licenses/SISSL.html",
+    name: "SPDX-license-identifier-PSF-2.0",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/PSF-2.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-SISSL-1.2",
-	conditions: ["by_exception_only", "not_allowed"],
-	url: "https://spdx.org/licenses/SISSL-1.2.html",
+    name: "SPDX-license-identifier-SISSL",
+    conditions: [
+        "by_exception_only",
+        "not_allowed",
+    ],
+    url: "https://spdx.org/licenses/SISSL.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-SPL-1.0",
-	conditions: ["by_exception_only", "reciprocal"],
-	url: "https://spdx.org/licenses/SPL-1.0.html",
+    name: "SPDX-license-identifier-SISSL-1.2",
+    conditions: [
+        "by_exception_only",
+        "not_allowed",
+    ],
+    url: "https://spdx.org/licenses/SISSL-1.2.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-SSPL",
-	conditions: ["by_exception_only", "not_allowed"],
-	url: "https://spdx.org/licenses/SSPL.html",
+    name: "SPDX-license-identifier-SPL-1.0",
+    conditions: [
+        "by_exception_only",
+        "reciprocal",
+    ],
+    url: "https://spdx.org/licenses/SPL-1.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-UPL-1.0",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/UPL-1.-.html",
+    name: "SPDX-license-identifier-SSPL",
+    conditions: [
+        "by_exception_only",
+        "not_allowed",
+    ],
+    url: "https://spdx.org/licenses/SSPL.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-Unicode-DFS",
-	conditions: ["notice"],
+    name: "SPDX-license-identifier-UPL-1.0",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/UPL-1.-.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-Unicode-DFS-2015",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/Unicode-DFS-2015.html",
+    name: "SPDX-license-identifier-Unicode-DFS",
+    conditions: ["notice"],
 }
 
 license_kind {
-	name: "SPDX-license-identifier-Unicode-DFS-2016",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/Unicode-DFS-2016.html",
+    name: "SPDX-license-identifier-Unicode-DFS-2015",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/Unicode-DFS-2015.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-Unlicense",
-	conditions: ["unencumbered"],
-	url: "https://spdx.org/licenses/Unlicense.html",
+    name: "SPDX-license-identifier-Unicode-DFS-2016",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/Unicode-DFS-2016.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-W3C",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/W3C.html",
+    name: "SPDX-license-identifier-Unlicense",
+    conditions: ["unencumbered"],
+    url: "https://spdx.org/licenses/Unlicense.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-W3C-19980720",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/W3C-19980720.html",
+    name: "SPDX-license-identifier-W3C",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/W3C.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-W3C-20150513",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/W3C-20150513.html",
+    name: "SPDX-license-identifier-W3C-19980720",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/W3C-19980720.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-WTFPL",
-	conditions: ["by_exception_only", "not_allowed"],
-	url: "https://spdx.org/licenses/WTFPL.html",
+    name: "SPDX-license-identifier-W3C-20150513",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/W3C-20150513.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-Watcom-1.0",
-	conditions: ["by_exception_only", "not_allowed"],
-	url: "https://spdx.org/licenses/Watcom-1.0.html",
+    name: "SPDX-license-identifier-WTFPL",
+    conditions: [
+        "by_exception_only",
+        "not_allowed",
+    ],
+    url: "https://spdx.org/licenses/WTFPL.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-Xnet",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/Xnet.html",
+    name: "SPDX-license-identifier-Watcom-1.0",
+    conditions: [
+        "by_exception_only",
+        "not_allowed",
+    ],
+    url: "https://spdx.org/licenses/Watcom-1.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-ZPL",
-	conditions: ["notice"],
+    name: "SPDX-license-identifier-Xnet",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/Xnet.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-ZPL-1.1",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/ZPL-1.1.html",
+    name: "SPDX-license-identifier-ZPL",
+    conditions: ["notice"],
 }
 
 license_kind {
-	name: "SPDX-license-identifier-ZPL-2.0",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/ZPL-2.0.html",
+    name: "SPDX-license-identifier-ZPL-1.1",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/ZPL-1.1.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-ZPL-2.1",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/ZPL-2.1.html",
+    name: "SPDX-license-identifier-ZPL-2.0",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/ZPL-2.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-Zend-2.0",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/Zend-2.0.html",
+    name: "SPDX-license-identifier-ZPL-2.1",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/ZPL-2.1.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-Zlib",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/Zlib.html",
+    name: "SPDX-license-identifier-Zend-2.0",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/Zend-2.0.html",
 }
 
 license_kind {
-	name: "SPDX-license-identifier-libtiff",
-	conditions: ["notice"],
-	url: "https://spdx.org/licenses/libtiff.html",
+    name: "SPDX-license-identifier-Zlib",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/Zlib.html",
 }
 
+license_kind {
+    name: "SPDX-license-identifier-libtiff",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/libtiff.html",
+}
 
 // Legacy license kinds -- do not add new references -- use an spdx kind instead.
 license_kind {
-	name: "legacy_unknown",
-	conditions: ["by_exception_only"],
+    name: "legacy_unknown",
+    conditions: ["by_exception_only"],
 }
 
 license_kind {
-	name: "legacy_unencumbered",
-	conditions: ["unencumbered"],
+    name: "legacy_unencumbered",
+    conditions: ["unencumbered"],
 }
 
 license_kind {
-	name: "legacy_notice",
-	conditions: ["notice"],
+    name: "legacy_permissive",
+    conditions: ["permissive"],
 }
 
 license_kind {
-	name: "legacy_reciprocal",
-	conditions: ["reciprocal"],
+    name: "legacy_notice",
+    conditions: ["notice"],
 }
 
 license_kind {
-	name: "legacy_restricted",
-	conditions: ["restricted"],
+    name: "legacy_reciprocal",
+    conditions: ["reciprocal"],
 }
 
 license_kind {
-	name: "legacy_by_exception_only",
-	conditions: ["by_exception_only"],
+    name: "legacy_restricted",
+    conditions: ["restricted"],
 }
 
 license_kind {
-	name: "legacy_not_allowed",
-	conditions: ["by_exception_only", "not_allowed"],
+    name: "legacy_by_exception_only",
+    conditions: ["by_exception_only"],
 }
 
 license_kind {
-	name: "legacy_proprietary",
-	conditions: ["by_exception_only", "not_allowed", "proprietary"],
+    name: "legacy_not_a_contribution",
+    conditions: [
+        "by_exception_only",
+        "not_allowed",
+    ],
+}
+
+license_kind {
+    name: "legacy_not_allowed",
+    conditions: [
+        "by_exception_only",
+        "not_allowed",
+    ],
+}
+
+license_kind {
+    name: "legacy_proprietary",
+    conditions: [
+        "by_exception_only",
+        "not_allowed",
+        "proprietary",
+    ],
 }
diff --git a/licenses/OWNERS b/licenses/OWNERS
new file mode 100644
index 0000000..fddfc48
--- /dev/null
+++ b/licenses/OWNERS
@@ -0,0 +1,2 @@
+per-file * = bbadour@google.com
+
diff --git a/rust/Android.bp b/rust/Android.bp
index df731db..ad3040a 100644
--- a/rust/Android.bp
+++ b/rust/Android.bp
@@ -15,6 +15,7 @@
         "clippy.go",
         "compiler.go",
         "coverage.go",
+        "fuzz.go",
         "image.go",
         "library.go",
         "prebuilt.go",
@@ -22,6 +23,7 @@
         "project_json.go",
         "protobuf.go",
         "rust.go",
+        "sanitize.go",
         "strip.go",
         "source_provider.go",
         "test.go",
@@ -34,6 +36,7 @@
         "clippy_test.go",
         "compiler_test.go",
         "coverage_test.go",
+        "fuzz_test.go",
         "image_test.go",
         "library_test.go",
         "project_json_test.go",
diff --git a/rust/builder.go b/rust/builder.go
index becebfe..56fe031 100644
--- a/rust/builder.go
+++ b/rust/builder.go
@@ -83,7 +83,7 @@
 
 func TransformSrcToBinary(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
 	outputFile android.WritablePath, linkDirs []string) buildOutput {
-	flags.RustFlags = append(flags.RustFlags, "-C lto")
+	flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto")
 
 	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "bin", linkDirs)
 }
@@ -100,13 +100,13 @@
 
 func TransformSrctoStatic(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
 	outputFile android.WritablePath, linkDirs []string) buildOutput {
-	flags.RustFlags = append(flags.RustFlags, "-C lto")
+	flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto")
 	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "staticlib", linkDirs)
 }
 
 func TransformSrctoShared(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
 	outputFile android.WritablePath, linkDirs []string) buildOutput {
-	flags.RustFlags = append(flags.RustFlags, "-C lto")
+	flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto")
 	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "cdylib", linkDirs)
 }
 
@@ -185,7 +185,7 @@
 	implicits = append(implicits, rustLibsToPaths(deps.DyLibs)...)
 	implicits = append(implicits, rustLibsToPaths(deps.ProcMacros)...)
 	implicits = append(implicits, deps.StaticLibs...)
-	implicits = append(implicits, deps.SharedLibs...)
+	implicits = append(implicits, deps.SharedLibDeps...)
 	implicits = append(implicits, deps.srcProviderFiles...)
 
 	if deps.CrtBegin.Valid() {
diff --git a/rust/compiler.go b/rust/compiler.go
index d45a5aa..c921824 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -145,6 +145,7 @@
 	relative string
 	path     android.InstallPath
 	location installLocation
+	sanitize *sanitize
 
 	distFile android.OptionalPath
 	// Stripped output file. If Valid(), this file will be installed instead of outputFile.
diff --git a/rust/config/arm64_device.go b/rust/config/arm64_device.go
index 21b22a4..5066428 100644
--- a/rust/config/arm64_device.go
+++ b/rust/config/arm64_device.go
@@ -26,8 +26,8 @@
 	Arm64LinkFlags            = []string{}
 
 	Arm64ArchVariantRustFlags = map[string][]string{
-		"armv8-a":  []string{},
-		"armv8-2a": []string{},
+		"armv8-a":          []string{},
+		"armv8-2a":         []string{},
 		"armv8-2a-dotprod": []string{},
 	}
 )
@@ -71,6 +71,10 @@
 	return true
 }
 
+func (toolchainArm64) LibclangRuntimeLibraryArch() string {
+	return "aarch64"
+}
+
 func Arm64ToolchainFactory(arch android.Arch) Toolchain {
 	archVariant := arch.ArchVariant
 	if archVariant == "" {
diff --git a/rust/config/arm_device.go b/rust/config/arm_device.go
index adfe917..42c1c02 100644
--- a/rust/config/arm_device.go
+++ b/rust/config/arm_device.go
@@ -72,6 +72,10 @@
 	return true
 }
 
+func (toolchainArm) LibclangRuntimeLibraryArch() string {
+	return "arm"
+}
+
 func ArmToolchainFactory(arch android.Arch) Toolchain {
 	toolchainRustFlags := []string{
 		"${config.ArmToolchainRustFlags}",
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/config/toolchain.go b/rust/config/toolchain.go
index 616d88b..9525c38 100644
--- a/rust/config/toolchain.go
+++ b/rust/config/toolchain.go
@@ -34,6 +34,8 @@
 	Supported() bool
 
 	Bionic() bool
+
+	LibclangRuntimeLibraryArch() string
 }
 
 type toolchainBase struct {
@@ -106,6 +108,36 @@
 	return false
 }
 
+func (toolchainBase) LibclangRuntimeLibraryArch() string {
+	return ""
+}
+
+func LibFuzzerRuntimeLibrary(t Toolchain) string {
+	return LibclangRuntimeLibrary(t, "fuzzer")
+}
+
+func LibclangRuntimeLibrary(t Toolchain, library string) string {
+	arch := t.LibclangRuntimeLibraryArch()
+	if arch == "" {
+		return ""
+	}
+	if !t.Bionic() {
+		return "libclang_rt." + library + "-" + arch
+	}
+	return "libclang_rt." + library + "-" + arch + "-android"
+}
+
+func LibRustRuntimeLibrary(t Toolchain, library string) string {
+	arch := t.LibclangRuntimeLibraryArch()
+	if arch == "" {
+		return ""
+	}
+	if !t.Bionic() {
+		return "librustc_rt." + library + "-" + arch
+	}
+	return "librustc_rt." + library + "-" + arch + "-android"
+}
+
 func toolchainBaseFactory() Toolchain {
 	return &toolchainBase{}
 }
diff --git a/rust/config/x86_64_device.go b/rust/config/x86_64_device.go
index 5f6e85a..94b719f 100644
--- a/rust/config/x86_64_device.go
+++ b/rust/config/x86_64_device.go
@@ -77,6 +77,10 @@
 	return true
 }
 
+func (toolchainX86_64) LibclangRuntimeLibraryArch() string {
+	return "x86_64"
+}
+
 func x86_64ToolchainFactory(arch android.Arch) Toolchain {
 	toolchainRustFlags := []string{
 		"${config.X86_64ToolchainRustFlags}",
diff --git a/rust/config/x86_device.go b/rust/config/x86_device.go
index daeeb14..aae1125 100644
--- a/rust/config/x86_device.go
+++ b/rust/config/x86_device.go
@@ -80,6 +80,10 @@
 	return true
 }
 
+func (toolchainX86) LibclangRuntimeLibraryArch() string {
+	return "i686"
+}
+
 func x86ToolchainFactory(arch android.Arch) Toolchain {
 	toolchainRustFlags := []string{
 		"${config.X86ToolchainRustFlags}",
diff --git a/rust/config/x86_linux_host.go b/rust/config/x86_linux_host.go
index e7f26ce..b63e14d 100644
--- a/rust/config/x86_linux_host.go
+++ b/rust/config/x86_linux_host.go
@@ -103,6 +103,14 @@
 	return "x86"
 }
 
+func (toolchainLinuxX86) LibclangRuntimeLibraryArch() string {
+	return "i386"
+}
+
+func (toolchainLinuxX8664) LibclangRuntimeLibraryArch() string {
+	return "x86_64"
+}
+
 func (t *toolchainLinuxX86) RustTriple() string {
 	return "i686-unknown-linux-gnu"
 }
diff --git a/rust/fuzz.go b/rust/fuzz.go
new file mode 100644
index 0000000..da8f209
--- /dev/null
+++ b/rust/fuzz.go
@@ -0,0 +1,96 @@
+// Copyright 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rust
+
+import (
+	"android/soong/android"
+	"android/soong/cc"
+	"android/soong/rust/config"
+)
+
+func init() {
+	android.RegisterModuleType("rust_fuzz", RustFuzzFactory)
+}
+
+type fuzzDecorator struct {
+	*binaryDecorator
+
+	Properties            cc.FuzzProperties
+	dictionary            android.Path
+	corpus                android.Paths
+	corpusIntermediateDir android.Path
+	config                android.Path
+	data                  android.Paths
+	dataIntermediateDir   android.Path
+}
+
+var _ compiler = (*binaryDecorator)(nil)
+
+// rust_binary produces a binary that is runnable on a device.
+func RustFuzzFactory() android.Module {
+	module, _ := NewRustFuzz(android.HostAndDeviceSupported)
+	return module.Init()
+}
+
+func NewRustFuzz(hod android.HostOrDeviceSupported) (*Module, *fuzzDecorator) {
+	module, binary := NewRustBinary(hod)
+	fuzz := &fuzzDecorator{
+		binaryDecorator: binary,
+	}
+
+	// Change the defaults for the binaryDecorator's baseCompiler
+	fuzz.binaryDecorator.baseCompiler.dir = "fuzz"
+	fuzz.binaryDecorator.baseCompiler.dir64 = "fuzz"
+	fuzz.binaryDecorator.baseCompiler.location = InstallInData
+	module.sanitize.SetSanitizer(cc.Fuzzer, true)
+	module.compiler = fuzz
+	return module, fuzz
+}
+
+func (fuzzer *fuzzDecorator) compilerFlags(ctx ModuleContext, flags Flags) Flags {
+	flags = fuzzer.binaryDecorator.compilerFlags(ctx, flags)
+
+	// `../lib` for installed fuzz targets (both host and device), and `./lib` for fuzz target packages.
+	flags.LinkFlags = append(flags.LinkFlags, `-Wl,-rpath,\$$ORIGIN/../lib`)
+	flags.LinkFlags = append(flags.LinkFlags, `-Wl,-rpath,\$$ORIGIN/lib`)
+
+	return flags
+}
+
+func (fuzzer *fuzzDecorator) compilerDeps(ctx DepsContext, deps Deps) Deps {
+	deps.StaticLibs = append(deps.StaticLibs,
+		config.LibFuzzerRuntimeLibrary(ctx.toolchain()))
+	deps.SharedLibs = append(deps.SharedLibs,
+		config.LibclangRuntimeLibrary(ctx.toolchain(), "asan"))
+	deps.SharedLibs = append(deps.SharedLibs, "libc++")
+	deps.Rlibs = append(deps.Rlibs, "liblibfuzzer_sys")
+
+	deps = fuzzer.binaryDecorator.compilerDeps(ctx, deps)
+
+	return deps
+}
+
+func (fuzzer *fuzzDecorator) compilerProps() []interface{} {
+	return append(fuzzer.binaryDecorator.compilerProps(),
+		&fuzzer.Properties)
+}
+
+func (fuzzer *fuzzDecorator) stdLinkage(ctx *depsContext) RustLinkage {
+	return RlibLinkage
+}
+
+func (fuzzer *fuzzDecorator) autoDep(ctx BaseModuleContext) autoDep {
+	return rlibAutoDep
+}
diff --git a/rust/fuzz_test.go b/rust/fuzz_test.go
new file mode 100644
index 0000000..f93ccc7
--- /dev/null
+++ b/rust/fuzz_test.go
@@ -0,0 +1,66 @@
+// Copyright 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rust
+
+import (
+	"strings"
+	"testing"
+
+	"android/soong/android"
+)
+
+func TestRustFuzz(t *testing.T) {
+	ctx := testRust(t, `
+			rust_library {
+				name: "libtest_fuzzing",
+				crate_name: "test_fuzzing",
+				srcs: ["foo.rs"],
+			}
+			rust_fuzz {
+				name: "fuzz_libtest",
+				srcs: ["foo.rs"],
+				rustlibs: ["libtest_fuzzing"],
+			}
+	`)
+
+	// Check that appropriate dependencies are added and that the rustlib linkage is correct.
+	fuzz_libtest_mod := ctx.ModuleForTests("fuzz_libtest", "android_arm64_armv8-a_fuzzer").Module().(*Module)
+	if !android.InList("libclang_rt.asan-aarch64-android", fuzz_libtest_mod.Properties.AndroidMkSharedLibs) {
+		t.Errorf("libclang_rt.asan-aarch64-android shared library dependency missing for rust_fuzz module.")
+	}
+	if !android.InList("liblibfuzzer_sys.rlib-std", fuzz_libtest_mod.Properties.AndroidMkRlibs) {
+		t.Errorf("liblibfuzzer_sys rlib library dependency missing for rust_fuzz module. %#v", fuzz_libtest_mod.Properties.AndroidMkRlibs)
+	}
+	if !android.InList("libtest_fuzzing.rlib-std", fuzz_libtest_mod.Properties.AndroidMkRlibs) {
+		t.Errorf("rustlibs not linked as rlib for rust_fuzz module.")
+	}
+
+	// Check that compiler flags are set appropriately .
+	fuzz_libtest := ctx.ModuleForTests("fuzz_libtest", "android_arm64_armv8-a_fuzzer").Output("fuzz_libtest")
+	if !strings.Contains(fuzz_libtest.Args["rustcFlags"], "-Z sanitizer=address") ||
+		!strings.Contains(fuzz_libtest.Args["rustcFlags"], "-C passes='sancov'") ||
+		!strings.Contains(fuzz_libtest.Args["rustcFlags"], "--cfg fuzzing") {
+		t.Errorf("rust_fuzz module does not contain the expected flags (sancov, cfg fuzzing, address sanitizer).")
+
+	}
+
+	// Check that dependencies have 'fuzzer' variants produced for them as well.
+	libtest_fuzzer := ctx.ModuleForTests("libtest_fuzzing", "android_arm64_armv8-a_rlib_rlib-std_fuzzer").Output("libtest_fuzzing.rlib")
+	if !strings.Contains(libtest_fuzzer.Args["rustcFlags"], "-Z sanitizer=address") ||
+		!strings.Contains(libtest_fuzzer.Args["rustcFlags"], "-C passes='sancov'") ||
+		!strings.Contains(libtest_fuzzer.Args["rustcFlags"], "--cfg fuzzing") {
+		t.Errorf("rust_fuzz dependent library does not contain the expected flags (sancov, cfg fuzzing, address sanitizer).")
+	}
+}
diff --git a/rust/image.go b/rust/image.go
index 5e55e22..5ff10ae 100644
--- a/rust/image.go
+++ b/rust/image.go
@@ -68,11 +68,7 @@
 
 // Returns true when this module is configured to have core and vendor variants.
 func (mod *Module) HasVendorVariant() bool {
-	return mod.IsVndk() || Bool(mod.VendorProperties.Vendor_available)
-}
-
-func (c *Module) VendorAvailable() bool {
-	return Bool(c.VendorProperties.Vendor_available)
+	return Bool(mod.VendorProperties.Vendor_available) || Bool(mod.VendorProperties.Odm_available)
 }
 
 func (c *Module) InProduct() bool {
@@ -114,10 +110,15 @@
 	coreVariantNeeded := true
 	var vendorVariants []string
 
-	if Bool(mod.VendorProperties.Vendor_available) {
+	if mod.HasVendorVariant() {
+		prop := "vendor_available"
+		if Bool(mod.VendorProperties.Odm_available) {
+			prop = "odm_available"
+		}
+
 		if vendorSpecific {
-			mctx.PropertyErrorf("vendor_available",
-				"doesn't make sense at the same time as `vendor: true`, `proprietary: true`, or `device_specific:true`")
+			mctx.PropertyErrorf(prop,
+				"doesn't make sense at the same time as `vendor: true`, `proprietary: true`, or `device_specific: true`")
 		}
 
 		if lib, ok := mod.compiler.(libraryInterface); ok {
@@ -128,9 +129,8 @@
 			// We can't check shared() here because image mutator is called before the library mutator, so we need to
 			// check buildShared()
 			if lib.buildShared() {
-				mctx.PropertyErrorf("vendor_available",
-					"vendor_available can only be set for rust_ffi_static modules.")
-			} else if Bool(mod.VendorProperties.Vendor_available) == true {
+				mctx.PropertyErrorf(prop, "can only be set for rust_ffi_static modules.")
+			} else {
 				vendorVariants = append(vendorVariants, platformVndkVersion)
 			}
 		}
diff --git a/rust/image_test.go b/rust/image_test.go
index 025b0fd..fd71962 100644
--- a/rust/image_test.go
+++ b/rust/image_test.go
@@ -46,7 +46,7 @@
 
 // Test that shared libraries cannot be made vendor available until proper support is added.
 func TestForbiddenVendorLinkage(t *testing.T) {
-	testRustError(t, "vendor_available can only be set for rust_ffi_static modules", `
+	testRustError(t, "can only be set for rust_ffi_static modules", `
 		rust_ffi_shared {
 			name: "libfoo_vendor",
 			crate_name: "foo",
diff --git a/rust/proc_macro.go b/rust/proc_macro.go
index 0c6ec99..f753e7f 100644
--- a/rust/proc_macro.go
+++ b/rust/proc_macro.go
@@ -51,6 +51,8 @@
 		flagExporter: NewFlagExporter(),
 	}
 
+	// Don't sanitize procMacros
+	module.sanitize = nil
 	module.compiler = procMacro
 
 	return module, procMacro
diff --git a/rust/rust.go b/rust/rust.go
index a2d0292..2ef9daf 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -42,6 +42,10 @@
 		ctx.BottomUp("rust_libraries", LibraryMutator).Parallel()
 		ctx.BottomUp("rust_stdlinkage", LibstdMutator).Parallel()
 		ctx.BottomUp("rust_begin", BeginMutator).Parallel()
+
+	})
+	android.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
+		ctx.BottomUp("rust_sanitizers", rustSanitizerRuntimeMutator).Parallel()
 	})
 	pctx.Import("android/soong/rust/config")
 	pctx.ImportAs("cc_config", "android/soong/cc/config")
@@ -97,6 +101,7 @@
 	compiler         compiler
 	coverage         *coverage
 	clippy           *clippy
+	sanitize         *sanitize
 	cachedToolchain  config.Toolchain
 	sourceProvider   SourceProvider
 	subAndroidMkOnce map[SubAndroidMkProvider]bool
@@ -125,7 +130,9 @@
 }
 
 func (mod *Module) SanitizePropDefined() bool {
-	return false
+	// Because compiler is not set for some Rust modules where sanitize might be set, check that compiler is also not
+	// nil since we need compiler to actually sanitize.
+	return mod.sanitize != nil && mod.compiler != nil
 }
 
 func (mod *Module) IsDependencyRoot() bool {
@@ -267,14 +274,15 @@
 }
 
 type PathDeps struct {
-	DyLibs      RustLibraries
-	RLibs       RustLibraries
-	SharedLibs  android.Paths
-	StaticLibs  android.Paths
-	ProcMacros  RustLibraries
-	linkDirs    []string
-	depFlags    []string
-	linkObjects []string
+	DyLibs        RustLibraries
+	RLibs         RustLibraries
+	SharedLibs    android.Paths
+	SharedLibDeps android.Paths
+	StaticLibs    android.Paths
+	ProcMacros    RustLibraries
+	linkDirs      []string
+	depFlags      []string
+	linkObjects   []string
 	//ReexportedDeps android.Paths
 
 	// Used by bindgen modules which call clang
@@ -417,6 +425,7 @@
 		&cc.CoverageProperties{},
 		&cc.RustBindgenClangProperties{},
 		&ClippyProperties{},
+		&SanitizeProperties{},
 	)
 
 	android.InitDefaultsModule(module)
@@ -537,6 +546,9 @@
 	if mod.sourceProvider != nil {
 		mod.AddProperties(mod.sourceProvider.SourceProviderProps()...)
 	}
+	if mod.sanitize != nil {
+		mod.AddProperties(mod.sanitize.props()...)
+	}
 
 	android.InitAndroidArchModule(mod, mod.hod, mod.multilib)
 	android.InitApexModule(mod)
@@ -555,6 +567,7 @@
 	module := newBaseModule(hod, multilib)
 	module.coverage = &coverage{}
 	module.clippy = &clippy{}
+	module.sanitize = &sanitize{}
 	return module
 }
 
@@ -669,6 +682,9 @@
 	if mod.clippy != nil {
 		flags, deps = mod.clippy.flags(ctx, flags, deps)
 	}
+	if mod.sanitize != nil {
+		flags, deps = mod.sanitize.flags(ctx, flags, deps)
+	}
 
 	// SourceProvider needs to call GenerateSource() before compiler calls
 	// compile() so it can provide the source. A SourceProvider has
@@ -712,6 +728,10 @@
 		deps = mod.coverage.deps(ctx, deps)
 	}
 
+	if mod.sanitize != nil {
+		deps = mod.sanitize.deps(ctx, deps)
+	}
+
 	deps.Rlibs = android.LastUniqueStrings(deps.Rlibs)
 	deps.Dylibs = android.LastUniqueStrings(deps.Dylibs)
 	deps.Rustlibs = android.LastUniqueStrings(deps.Rustlibs)
@@ -772,6 +792,9 @@
 	if mod.coverage != nil {
 		mod.coverage.begin(ctx)
 	}
+	if mod.sanitize != nil {
+		mod.sanitize.begin(ctx)
+	}
 }
 
 func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
@@ -940,9 +963,15 @@
 		staticLibDepFiles = append(staticLibDepFiles, dep.OutputFile().Path())
 	}
 
+	var sharedLibFiles android.Paths
 	var sharedLibDepFiles android.Paths
 	for _, dep := range directSharedLibDeps {
-		sharedLibDepFiles = append(sharedLibDepFiles, dep.OutputFile().Path())
+		sharedLibFiles = append(sharedLibFiles, dep.OutputFile().Path())
+		if dep.Toc().Valid() {
+			sharedLibDepFiles = append(sharedLibDepFiles, dep.Toc().Path())
+		} else {
+			sharedLibDepFiles = append(sharedLibDepFiles, dep.OutputFile().Path())
+		}
 	}
 
 	var srcProviderDepFiles android.Paths
@@ -958,12 +987,14 @@
 	depPaths.RLibs = append(depPaths.RLibs, rlibDepFiles...)
 	depPaths.DyLibs = append(depPaths.DyLibs, dylibDepFiles...)
 	depPaths.SharedLibs = append(depPaths.SharedLibs, sharedLibDepFiles...)
+	depPaths.SharedLibDeps = append(depPaths.SharedLibDeps, sharedLibDepFiles...)
 	depPaths.StaticLibs = append(depPaths.StaticLibs, staticLibDepFiles...)
 	depPaths.ProcMacros = append(depPaths.ProcMacros, procMacroDepFiles...)
 	depPaths.SrcDeps = append(depPaths.SrcDeps, srcProviderDepFiles...)
 
 	// Dedup exported flags from dependencies
 	depPaths.linkDirs = android.FirstUniqueStrings(depPaths.linkDirs)
+	depPaths.linkObjects = android.FirstUniqueStrings(depPaths.linkObjects)
 	depPaths.depFlags = android.FirstUniqueStrings(depPaths.depFlags)
 	depPaths.depClangFlags = android.FirstUniqueStrings(depPaths.depClangFlags)
 	depPaths.depIncludePaths = android.FirstUniquePaths(depPaths.depIncludePaths)
diff --git a/rust/sanitize.go b/rust/sanitize.go
new file mode 100644
index 0000000..67460ba
--- /dev/null
+++ b/rust/sanitize.go
@@ -0,0 +1,258 @@
+// Copyright 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rust
+
+import (
+	"android/soong/android"
+	"android/soong/cc"
+	"android/soong/rust/config"
+	"fmt"
+	"github.com/google/blueprint"
+)
+
+type SanitizeProperties struct {
+	// enable AddressSanitizer, ThreadSanitizer, or UndefinedBehaviorSanitizer
+	Sanitize struct {
+		Address *bool `android:"arch_variant"`
+		Fuzzer  *bool `android:"arch_variant"`
+		Never   *bool `android:"arch_variant"`
+	}
+	SanitizerEnabled bool `blueprint:"mutated"`
+	SanitizeDep      bool `blueprint:"mutated"`
+
+	// Used when we need to place libraries in their own directory, such as ASAN.
+	InSanitizerDir bool `blueprint:"mutated"`
+}
+
+var fuzzerFlags = []string{
+	"-C passes='sancov'",
+
+	"--cfg fuzzing",
+	"-C llvm-args=-sanitizer-coverage-level=4",
+	"-C llvm-args=-sanitizer-coverage-trace-compares",
+	"-C llvm-args=-sanitizer-coverage-inline-8bit-counters",
+	"-C llvm-args=-sanitizer-coverage-trace-geps",
+	"-C llvm-args=-sanitizer-coverage-prune-blocks=0",
+	"-C llvm-args=-sanitizer-coverage-pc-table",
+	"-C link-dead-code=y",
+	"-Z sanitizer=address",
+
+	// Sancov breaks with lto
+	// TODO: Remove when https://bugs.llvm.org/show_bug.cgi?id=41734 is resolved and sancov works with LTO
+	"-C lto=no",
+}
+
+var asanFlags = []string{
+	"-Z sanitizer=address",
+}
+
+func boolPtr(v bool) *bool {
+	if v {
+		return &v
+	} else {
+		return nil
+	}
+}
+
+func init() {
+}
+func (sanitize *sanitize) props() []interface{} {
+	return []interface{}{&sanitize.Properties}
+}
+
+func (sanitize *sanitize) begin(ctx BaseModuleContext) {
+	s := sanitize.Properties.Sanitize
+
+	// TODO:(b/178369775)
+	// For now sanitizing is only supported on devices
+	if ctx.Os() == android.Android && Bool(s.Fuzzer) {
+		sanitize.Properties.SanitizerEnabled = true
+	}
+
+	if ctx.Os() == android.Android && Bool(s.Address) {
+		sanitize.Properties.SanitizerEnabled = true
+	}
+}
+
+type sanitize struct {
+	Properties SanitizeProperties
+}
+
+func (sanitize *sanitize) flags(ctx ModuleContext, flags Flags, deps PathDeps) (Flags, PathDeps) {
+	if !sanitize.Properties.SanitizerEnabled {
+		return flags, deps
+	}
+	if Bool(sanitize.Properties.Sanitize.Fuzzer) {
+		flags.RustFlags = append(flags.RustFlags, fuzzerFlags...)
+	}
+	if Bool(sanitize.Properties.Sanitize.Address) {
+		flags.RustFlags = append(flags.RustFlags, asanFlags...)
+	}
+	return flags, deps
+}
+
+func (sanitize *sanitize) deps(ctx BaseModuleContext, deps Deps) Deps {
+	return deps
+}
+
+func rustSanitizerRuntimeMutator(mctx android.BottomUpMutatorContext) {
+	if mod, ok := mctx.Module().(*Module); ok && mod.sanitize != nil {
+		if !mod.Enabled() {
+			return
+		}
+		if Bool(mod.sanitize.Properties.Sanitize.Fuzzer) || Bool(mod.sanitize.Properties.Sanitize.Address) {
+			mctx.AddFarVariationDependencies(append(mctx.Target().Variations(), []blueprint.Variation{
+				{Mutator: "link", Variation: "shared"},
+			}...), cc.SharedDepTag(), config.LibclangRuntimeLibrary(mod.toolchain(mctx), "asan"))
+		}
+	}
+}
+
+func (sanitize *sanitize) SetSanitizer(t cc.SanitizerType, b bool) {
+	sanitizerSet := false
+	switch t {
+	case cc.Fuzzer:
+		sanitize.Properties.Sanitize.Fuzzer = boolPtr(b)
+		sanitizerSet = true
+	case cc.Asan:
+		sanitize.Properties.Sanitize.Address = boolPtr(b)
+		sanitizerSet = true
+	default:
+		panic(fmt.Errorf("setting unsupported sanitizerType %d", t))
+	}
+	if b && sanitizerSet {
+		sanitize.Properties.SanitizerEnabled = true
+	}
+}
+
+// Check if the sanitizer is explicitly disabled (as opposed to nil by
+// virtue of not being set).
+func (sanitize *sanitize) isSanitizerExplicitlyDisabled(t cc.SanitizerType) bool {
+	if sanitize == nil {
+		return false
+	}
+	if Bool(sanitize.Properties.Sanitize.Never) {
+		return true
+	}
+	sanitizerVal := sanitize.getSanitizerBoolPtr(t)
+	return sanitizerVal != nil && *sanitizerVal == false
+}
+
+// There isn't an analog of the method above (ie:isSanitizerExplicitlyEnabled)
+// because enabling a sanitizer either directly (via the blueprint) or
+// 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 cc.SanitizerType) bool {
+	if sanitize == nil || !sanitize.Properties.SanitizerEnabled {
+		return false
+	}
+
+	sanitizerVal := sanitize.getSanitizerBoolPtr(t)
+	return sanitizerVal != nil && *sanitizerVal == true
+}
+
+func (sanitize *sanitize) getSanitizerBoolPtr(t cc.SanitizerType) *bool {
+	switch t {
+	case cc.Fuzzer:
+		return sanitize.Properties.Sanitize.Fuzzer
+	case cc.Asan:
+		return sanitize.Properties.Sanitize.Address
+	default:
+		return nil
+	}
+}
+
+func (mod *Module) SanitizerSupported(t cc.SanitizerType) bool {
+	if mod.Host() {
+		return false
+	}
+	switch t {
+	case cc.Fuzzer:
+		return true
+	case cc.Asan:
+		return true
+	default:
+		return false
+	}
+}
+
+func (mod *Module) IsSanitizerEnabled(t cc.SanitizerType) bool {
+	return mod.sanitize.isSanitizerEnabled(t)
+}
+
+func (mod *Module) IsSanitizerExplicitlyDisabled(t cc.SanitizerType) bool {
+	if mod.Host() {
+		return true
+	}
+
+	// TODO(b/178365482): Rust/CC interop doesn't work just yet; don't sanitize rust_ffi modules until
+	// linkage issues are resolved.
+	if lib, ok := mod.compiler.(libraryInterface); ok {
+		if lib.shared() || lib.static() {
+			return true
+		}
+	}
+
+	return mod.sanitize.isSanitizerExplicitlyDisabled(t)
+}
+
+func (mod *Module) SanitizeDep() bool {
+	return mod.sanitize.Properties.SanitizeDep
+}
+
+func (mod *Module) SetSanitizer(t cc.SanitizerType, b bool) {
+	if !Bool(mod.sanitize.Properties.Sanitize.Never) {
+		mod.sanitize.SetSanitizer(t, b)
+	}
+}
+
+func (mod *Module) SetSanitizeDep(b bool) {
+	mod.sanitize.Properties.SanitizeDep = b
+}
+
+func (mod *Module) StaticallyLinked() bool {
+	if lib, ok := mod.compiler.(libraryInterface); ok {
+		if lib.rlib() || lib.static() {
+			return true
+		}
+	} else if Bool(mod.compiler.(*binaryDecorator).Properties.Static_executable) {
+		return true
+	}
+	return false
+}
+
+func (mod *Module) SetInSanitizerDir() {
+	mod.sanitize.Properties.InSanitizerDir = true
+}
+
+func (mod *Module) SanitizeNever() bool {
+	return Bool(mod.sanitize.Properties.Sanitize.Never)
+}
+
+var _ cc.PlatformSanitizeable = (*Module)(nil)
+
+func IsSanitizableDependencyTag(tag blueprint.DependencyTag) bool {
+	switch t := tag.(type) {
+	case dependencyTag:
+		return t.library
+	default:
+		return cc.IsSanitizableDependencyTag(tag)
+	}
+}
+
+func (m *Module) SanitizableDepTagChecker() cc.SantizableDependencyTagChecker {
+	return IsSanitizableDependencyTag
+}
diff --git a/rust/testing.go b/rust/testing.go
index 07f557a..bb511b6 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -87,6 +87,13 @@
 			system_shared_libs: [],
 			export_include_dirs: ["libprotobuf-cpp-full-includes"],
 		}
+		cc_library {
+			name: "libclang_rt.asan-aarch64-android",
+			no_libcrt: true,
+			nocrt: true,
+			system_shared_libs: [],
+			export_include_dirs: ["libprotobuf-cpp-full-includes"],
+		}
 		rust_library {
 			name: "libstd",
 			crate_name: "std",
@@ -129,7 +136,12 @@
 			srcs: ["foo.rs"],
 			host_supported: true,
 		}
-
+		rust_library {
+			name: "liblibfuzzer_sys",
+			crate_name: "libfuzzer_sys",
+			srcs:["foo.rs"],
+			host_supported: true,
+		}
 `
 	return bp
 }
@@ -147,6 +159,7 @@
 	ctx.RegisterModuleType("rust_library_host", RustLibraryHostFactory)
 	ctx.RegisterModuleType("rust_library_host_dylib", RustLibraryDylibHostFactory)
 	ctx.RegisterModuleType("rust_library_host_rlib", RustLibraryRlibHostFactory)
+	ctx.RegisterModuleType("rust_fuzz", RustFuzzFactory)
 	ctx.RegisterModuleType("rust_ffi", RustFFIFactory)
 	ctx.RegisterModuleType("rust_ffi_shared", RustFFISharedFactory)
 	ctx.RegisterModuleType("rust_ffi_static", RustFFIStaticFactory)
diff --git a/scripts/gen_ndk_backedby_apex.sh b/scripts/gen_ndk_backedby_apex.sh
new file mode 100755
index 0000000..e0da602
--- /dev/null
+++ b/scripts/gen_ndk_backedby_apex.sh
@@ -0,0 +1,55 @@
+#!/bin/bash -e
+
+# 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.
+
+# Generates NDK API txt file used by Mainline modules. NDK APIs would have value
+# "UND" in Ndx column and have suffix "@LIB_NAME" in Name column.
+# For example, current line llvm-readelf output is:
+# 1: 00000000     0     FUNC      GLOBAL  DEFAULT   UND   dlopen@LIBC
+# After the parse function below "dlopen" would be write to the output file.
+printHelp() {
+    echo "**************************** Usage Instructions ****************************"
+    echo "This script is used to generate the Mainline modules backed-by NDK symbols."
+    echo ""
+    echo "To run this script use: ./ndk_backedby_module.sh \$BINARY_IMAGE_DIRECTORY \$OUTPUT_FILE_PATH \$NDK_LIB_NAME_LIST"
+    echo "For example: If all the module image files that you would like to run is under directory '/myModule' and output write to /backedby.txt then the command would be:"
+    echo "./ndk_usedby_module.sh /myModule /backedby.txt /ndkLibList.txt"
+    echo "If the module1 is backing lib1 then the backedby.txt would contains: "
+    echo "lib1"
+}
+
+genBackedByList() {
+  dir="$1"
+  [[ ! -e "$2" ]] && echo "" >> "$2"
+  while IFS= read -r line
+  do
+    soFileName=$(echo "$line" | sed 's/\(.*so\).*/\1/')
+    if [[ ! -z "$soFileName" && "$soFileName" != *"#"* ]]
+    then
+      find "$dir" -type f -name "$soFileName" -exec echo "$soFileName" >> "$2" \;
+    fi
+  done < "$3"
+}
+
+if [[ "$1" == "help" ]]
+then
+  printHelp
+elif [[ "$#" -ne 3 ]]
+then
+  echo "Wrong argument length. Expecting 3 argument representing image file directory, output path, path to ndk library list."
+else
+  [[ -e "$2" ]] && rm "$2"
+  genBackedByList "$1" "$2" "$3"
+fi
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/testing.go b/sdk/testing.go
index 38755a9..1ac873b 100644
--- a/sdk/testing.go
+++ b/sdk/testing.go
@@ -107,12 +107,7 @@
 	ctx.PostDepsMutators(android.RegisterVisibilityRuleEnforcer)
 
 	// from java package
-	java.RegisterJavaBuildComponents(ctx)
-	java.RegisterAppBuildComponents(ctx)
-	java.RegisterSdkLibraryBuildComponents(ctx)
-	java.RegisterPrebuiltApisBuildComponents(ctx)
-	java.RegisterStubsBuildComponents(ctx)
-	java.RegisterSystemModulesBuildComponents(ctx)
+	java.RegisterRequiredBuildComponentsForTest(ctx)
 
 	// from cc package
 	cc.RegisterRequiredBuildComponentsForTest(ctx)
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/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()
diff --git a/sysprop/sysprop_test.go b/sysprop/sysprop_test.go
index b8b93f6..5cb9e64 100644
--- a/sysprop/sysprop_test.go
+++ b/sysprop/sysprop_test.go
@@ -58,9 +58,7 @@
 func testContext(config android.Config) *android.TestContext {
 
 	ctx := android.NewTestArchContext(config)
-	java.RegisterJavaBuildComponents(ctx)
-	java.RegisterAppBuildComponents(ctx)
-	java.RegisterSystemModulesBuildComponents(ctx)
+	java.RegisterRequiredBuildComponentsForTest(ctx)
 
 	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
 	ctx.PreArchMutators(func(ctx android.RegisterMutatorsContext) {
diff --git a/ui/build/cleanbuild.go b/ui/build/cleanbuild.go
index 06f6c63..b1f8551 100644
--- a/ui/build/cleanbuild.go
+++ b/ui/build/cleanbuild.go
@@ -117,6 +117,7 @@
 		productOut("misc_info.txt"),
 		productOut("apex"),
 		productOut("kernel"),
+		productOut("kernel-*"),
 		productOut("data"),
 		productOut("skin"),
 		productOut("obj/NOTICE_FILES"),
@@ -124,7 +125,6 @@
 		productOut("ramdisk"),
 		productOut("debug_ramdisk"),
 		productOut("vendor-ramdisk"),
-		productOut("vendor-ramdisk-debug.cpio.gz"),
 		productOut("vendor_debug_ramdisk"),
 		productOut("test_harness_ramdisk"),
 		productOut("recovery"),